diff --git a/.cargo/config.toml b/.cargo/config.toml
index a05c05ab5..10746d766 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,3 +1,5 @@
+[target.'cfg(target_env = "gnu")']
+rustflags = ["-C", "link-args=-Wl,-z,nodelete"]
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
@@ -10,4 +12,16 @@ linker = "aarch64-linux-musl-gcc"
rustflags = ["-C", "target-feature=-crt-static"]
[target.wasm32-unknown-unknown]
-rustflags = ["-C", "link-arg=--export-table"]
+rustflags = [
+ "-C",
+ "link-arg=--export-table",
+ '--cfg',
+ 'getrandom_backend="custom"',
+]
+
+
+# Statically link Visual Studio redistributables on Windows builds
+[target.x86_64-pc-windows-msvc]
+rustflags = ["-C", "target-feature=+crt-static"]
+[target.aarch64-pc-windows-msvc]
+rustflags = ["-C", "target-feature=+crt-static"]
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5e0254d6a..95d304311 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,6 +12,9 @@ jobs:
- os: windows-latest
target: x86_64-pc-windows-msvc
binary: lightningcss.exe
+ - os: windows-latest
+ target: aarch64-pc-windows-msvc
+ binary: lightningcss.exe
# Mac OS
- os: macos-latest
target: x86_64-apple-darwin
@@ -45,7 +48,7 @@ jobs:
if: ${{ matrix.strip }}
run: ${{ matrix.strip }} *.node ${{ matrix.binary }}
- name: Upload artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.target }}
path: |
@@ -87,7 +90,7 @@ jobs:
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
run: strip -x *.node lightningcss
- name: Upload artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: bindings-aarch64-apple-darwin
path: |
@@ -106,14 +109,17 @@ jobs:
- target: aarch64-unknown-linux-gnu
strip: llvm-strip
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
+ - target: aarch64-linux-android
+ strip: llvm-strip
+ image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
- target: armv7-unknown-linux-gnueabihf
strip: llvm-strip
- image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig
+ image: ghcr.io/napi-rs/napi-rs/nodejs-rust@sha256:c22284b2d79092d3e885f64ede00f6afdeb2ccef7e2b6e78be52e7909091cd57
- target: aarch64-unknown-linux-musl
- image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
+ image: ghcr.io/napi-rs/napi-rs/nodejs-rust@sha256:78c9ab1f117f8c535b93c4b91a2f19063dda6e4dba48a6187df49810625992c1
strip: aarch64-linux-musl-strip
- target: x86_64-unknown-linux-musl
- image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
+ image: ghcr.io/napi-rs/napi-rs/nodejs-rust@sha256:78c9ab1f117f8c535b93c4b91a2f19063dda6e4dba48a6187df49810625992c1
strip: strip
name: build-${{ matrix.target }}
@@ -130,6 +136,14 @@ jobs:
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
+ - name: Setup Android NDK
+ if: ${{ matrix.target == 'aarch64-linux-android' }}
+ run: |
+ sudo apt update && sudo apt install unzip -y
+ cd /tmp
+ wget -q https://dl.google.com/android/repository/android-ndk-r28-linux.zip -O /tmp/ndk.zip
+ unzip ndk.zip
+
- name: Setup cross compile toolchain
if: ${{ matrix.setup }}
run: ${{ matrix.setup }}
@@ -141,8 +155,11 @@ jobs:
- name: Build release
run: yarn build-release
env:
+ ANDROID_NDK_LATEST_HOME: /tmp/android-ndk-r28
RUST_TARGET: ${{ matrix.target }}
- name: Build CLI
+ env:
+ ANDROID_NDK_LATEST_HOME: /tmp/android-ndk-r28
run: |
yarn napi build --bin lightningcss --release --features cli --target ${{ matrix.target }}
mv target/${{ matrix.target }}/release/lightningcss lightningcss
@@ -150,7 +167,7 @@ jobs:
if: ${{ matrix.strip }}
run: ${{ matrix.strip }} *.node lightningcss
- name: Upload artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.target }}
path: |
@@ -158,11 +175,11 @@ jobs:
lightningcss
build-freebsd:
- runs-on: macos-latest
+ runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build FreeBSD
- uses: cross-platform-actions/action@v0.19.0
+ uses: cross-platform-actions/action@v0.25.0
env:
DEBUG: napi:*
RUSTUP_HOME: /usr/local/rustup
@@ -170,7 +187,10 @@ jobs:
RUSTUP_IO_THREADS: 1
with:
operating_system: freebsd
- version: '13.2'
+ version: '14.0'
+ memory: 13G
+ cpu_count: 3
+ environment_variables: 'DEBUG RUSTUP_IO_THREADS'
shell: bash
run: |
sudo pkg install -y -f curl node libnghttp2 npm yarn
@@ -195,7 +215,7 @@ jobs:
rm -rf .yarn/cache
- name: Upload artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: bindings-x86_64-unknown-freebsd
path: |
@@ -226,7 +246,7 @@ jobs:
export PATH="$PATH:./binaryen-version_111/bin"
yarn wasm:build-release
- name: Upload artifacts
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: wasm
path: wasm/lightningcss_node.wasm
@@ -244,9 +264,11 @@ jobs:
- uses: actions/checkout@v3
- uses: bahmutov/npm-install@v1.8.32
- name: Download artifacts
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@v4
with:
path: artifacts
+ - name: Show artifacts
+ run: ls -R artifacts
- name: Build npm packages
run: |
node scripts/build-npm.js
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e81411540..10868f4a6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -47,6 +47,13 @@ yarn wasm:build
yarn wasm:build-release
```
+Note: If you plan to build the WASM target, ensure that you have the required toolchain and binaries installed.
+
+```sh
+rustup target add wasm32-unknown-unknown
+cargo install wasm-opt
+```
+
## Website
The website is built using [Parcel](https://parceljs.org). You can start the development server by running:
diff --git a/Cargo.lock b/Cargo.lock
index 39b9a54ed..bb4bb2840 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,26 +1,26 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 3
+version = 4
[[package]]
name = "ahash"
-version = "0.7.7"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
+checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
dependencies = [
- "getrandom",
+ "getrandom 0.2.15",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
-version = "0.8.6"
+version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
- "getrandom",
+ "getrandom 0.3.3",
"once_cell",
"serde",
"version_check",
@@ -29,9 +29,9 @@ dependencies = [
[[package]]
name = "aho-corasick"
-version = "1.1.2"
+version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
@@ -53,26 +53,21 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.4"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
-
-[[package]]
-name = "anyhow"
-version = "1.0.75"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "assert_cmd"
-version = "2.0.12"
+version = "2.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6"
+checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d"
dependencies = [
"anstyle",
"bstr",
"doc-comment",
- "predicates 3.0.4",
+ "libc",
+ "predicates 3.1.3",
"predicates-core",
"predicates-tree",
"wait-timeout",
@@ -80,14 +75,14 @@ dependencies = [
[[package]]
name = "assert_fs"
-version = "1.0.13"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f070617a68e5c2ed5d06ee8dd620ee18fb72b99f6c094bed34cf8ab07c875b48"
+checksum = "7efdb1fdb47602827a342857666feb372712cbc64b414172bd6b167a02927674"
dependencies = [
"anstyle",
"doc-comment",
"globwalk",
- "predicates 3.0.4",
+ "predicates 3.1.3",
"predicates-core",
"predicates-tree",
"tempfile",
@@ -106,9 +101,9 @@ dependencies = [
[[package]]
name = "autocfg"
-version = "1.1.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "base64-simd"
@@ -127,9 +122,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.4.1"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
+dependencies = [
+ "serde",
+]
[[package]]
name = "bitvec"
@@ -143,36 +141,38 @@ dependencies = [
"wyz 0.5.1",
]
+[[package]]
+name = "browserslist-data"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c49471c5ae53cefe3ac4acc4d3c75cb4b68995b70b3bbb864f8e08fae282098c"
+dependencies = [
+ "ahash 0.8.12",
+ "chrono",
+]
+
[[package]]
name = "browserslist-rs"
-version = "0.12.4"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9bda9b4595376bf255f68dafb5dcc5b0e2842b38dc2a7b52c4e0bfe9fd1c651"
+checksum = "8dd48a6ca358df4f7000e3fb5f08738b1b91a0e5d5f862e2f77b2b14647547f5"
dependencies = [
- "ahash 0.8.6",
- "anyhow",
+ "ahash 0.8.12",
+ "browserslist-data",
"chrono",
"either",
- "getrandom",
- "itertools 0.10.5",
- "js-sys",
+ "itertools 0.13.0",
"nom",
- "once_cell",
- "quote",
"serde",
- "serde-wasm-bindgen",
"serde_json",
- "string_cache",
- "string_cache_codegen",
"thiserror",
- "wasm-bindgen",
]
[[package]]
name = "bstr"
-version = "1.7.0"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019"
+checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8"
dependencies = [
"memchr",
"regex-automata",
@@ -181,15 +181,15 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.14.0"
+version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytecheck"
-version = "0.6.11"
+version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627"
+checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
dependencies = [
"bytecheck_derive",
"ptr_meta",
@@ -198,9 +198,9 @@ dependencies = [
[[package]]
name = "bytecheck_derive"
-version = "0.6.11"
+version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61"
+checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
dependencies = [
"proc-macro2",
"quote",
@@ -208,10 +208,10 @@ dependencies = [
]
[[package]]
-name = "byteorder"
-version = "1.5.0"
+name = "bytes"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]]
name = "cbindgen"
@@ -221,7 +221,7 @@ checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d"
dependencies = [
"clap",
"heck",
- "indexmap",
+ "indexmap 1.9.3",
"log",
"proc-macro2",
"quote",
@@ -234,11 +234,11 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.0.83"
+version = "1.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
+checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
dependencies = [
- "libc",
+ "shlex",
]
[[package]]
@@ -249,15 +249,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
-version = "0.4.31"
+version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
dependencies = [
"android-tzdata",
"iana-time-zone",
- "js-sys",
"num-traits",
- "wasm-bindgen",
"windows-targets",
]
@@ -271,7 +269,7 @@ dependencies = [
"bitflags 1.3.2",
"clap_derive",
"clap_lex",
- "indexmap",
+ "indexmap 1.9.3",
"once_cell",
"strsim",
"termcolor",
@@ -331,52 +329,43 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "crossbeam-channel"
-version = "0.5.8"
+version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
dependencies = [
- "cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
-version = "0.8.3"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
- "cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.15"
+version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
- "autocfg",
- "cfg-if",
"crossbeam-utils",
- "memoffset",
- "scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.16"
+version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
-dependencies = [
- "cfg-if",
-]
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "cssparser"
@@ -387,7 +376,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
- "phf 0.11.2",
+ "phf",
"serde",
"smallvec",
]
@@ -408,17 +397,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
- "syn 2.0.39",
+ "syn 2.0.90",
]
[[package]]
name = "ctor"
-version = "0.1.26"
+version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
dependencies = [
"quote",
- "syn 1.0.109",
+ "syn 2.0.90",
]
[[package]]
@@ -428,7 +417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
- "hashbrown 0.14.2",
+ "hashbrown 0.14.5",
"lock_api",
"once_cell",
"parking_lot_core",
@@ -436,9 +425,9 @@ dependencies = [
[[package]]
name = "data-encoding"
-version = "2.4.0"
+version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2"
[[package]]
name = "data-url"
@@ -449,6 +438,12 @@ dependencies = [
"matches",
]
+[[package]]
+name = "diff"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+
[[package]]
name = "difflib"
version = "0.4.0"
@@ -469,30 +464,36 @@ checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
[[package]]
name = "dtoa-short"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbaceec3c6e4211c79e7b1800fb9680527106beb2f9c51904a3210c03a448c74"
+checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
dependencies = [
"dtoa",
]
[[package]]
name = "dyn-clone"
-version = "1.0.16"
+version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
+checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "either"
-version = "1.9.0"
+version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
-version = "0.3.6"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys",
@@ -500,9 +501,9 @@ dependencies = [
[[package]]
name = "fastrand"
-version = "2.0.1"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "float-cmp"
@@ -513,12 +514,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
[[package]]
name = "fs_extra"
version = "1.3.0"
@@ -532,47 +527,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
-name = "fxhash"
-version = "0.2.1"
+name = "getrandom"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
- "byteorder",
+ "cfg-if",
+ "libc",
+ "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
-version = "0.2.10"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
- "js-sys",
"libc",
- "wasi",
- "wasm-bindgen",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
]
[[package]]
name = "globset"
-version = "0.4.13"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d"
+checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
dependencies = [
"aho-corasick",
"bstr",
- "fnv",
"log",
- "regex",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
name = "globwalk"
-version = "0.8.1"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
+checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
"ignore",
"walkdir",
]
@@ -583,14 +579,20 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
- "ahash 0.7.7",
+ "ahash 0.7.8",
]
[[package]]
name = "hashbrown"
-version = "0.14.2"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
@@ -609,9 +611,9 @@ dependencies = [
[[package]]
name = "iana-time-zone"
-version = "0.1.58"
+version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -632,17 +634,16 @@ dependencies = [
[[package]]
name = "ignore"
-version = "0.4.20"
+version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
+checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [
+ "crossbeam-deque",
"globset",
- "lazy_static",
"log",
"memchr",
- "regex",
+ "regex-automata",
"same-file",
- "thread_local",
"walkdir",
"winapi-util",
]
@@ -657,6 +658,17 @@ dependencies = [
"hashbrown 0.12.3",
]
+[[package]]
+name = "indexmap"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.15.2",
+ "serde",
+]
+
[[package]]
name = "indoc"
version = "1.0.9"
@@ -674,18 +686,18 @@ dependencies = [
[[package]]
name = "itertools"
-version = "0.11.0"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "itoa"
-version = "1.0.9"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
[[package]]
name = "jemalloc-sys"
@@ -710,44 +722,45 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.65"
+version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
dependencies = [
+ "once_cell",
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
-version = "0.2.150"
+version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libloading"
-version = "0.8.1"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
+checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
- "windows-sys",
+ "windows-targets",
]
[[package]]
name = "lightningcss"
-version = "1.0.0-alpha.51"
+version = "1.0.0-alpha.70"
dependencies = [
- "ahash 0.7.7",
+ "ahash 0.8.12",
"assert_cmd",
"assert_fs",
"atty",
- "bitflags 2.4.1",
+ "bitflags 2.6.0",
"browserslist-rs",
"clap",
"const-str",
@@ -755,6 +768,8 @@ dependencies = [
"cssparser-color",
"dashmap",
"data-encoding",
+ "getrandom 0.3.3",
+ "indexmap 2.7.0",
"indoc",
"itertools 0.10.5",
"jemallocator",
@@ -762,12 +777,14 @@ dependencies = [
"lightningcss-derive",
"parcel_selectors",
"parcel_sourcemap",
- "paste",
+ "pastey",
"pathdiff",
"predicates 2.1.5",
+ "pretty_assertions",
"rayon",
"schemars",
"serde",
+ "serde-content",
"serde_json",
"smallvec",
"static-self",
@@ -775,13 +792,31 @@ dependencies = [
[[package]]
name = "lightningcss-derive"
-version = "1.0.0-alpha.42"
+version = "1.0.0-alpha.43"
dependencies = [
+ "convert_case",
"proc-macro2",
"quote",
"syn 1.0.109",
]
+[[package]]
+name = "lightningcss-napi"
+version = "0.4.7"
+dependencies = [
+ "crossbeam-channel",
+ "cssparser",
+ "lightningcss",
+ "napi",
+ "parcel_sourcemap",
+ "rayon",
+ "serde",
+ "serde-content",
+ "serde-detach",
+ "serde_bytes",
+ "smallvec",
+]
+
[[package]]
name = "lightningcss_c_bindings"
version = "0.1.0"
@@ -796,32 +831,24 @@ dependencies = [
name = "lightningcss_node"
version = "0.1.0"
dependencies = [
- "crossbeam-channel",
- "cssparser",
"jemallocator",
- "lightningcss",
+ "lightningcss-napi",
"napi",
"napi-build",
"napi-derive",
- "parcel_sourcemap",
- "rayon",
- "serde",
- "serde-detach",
- "serde_bytes",
- "smallvec",
]
[[package]]
name = "linux-raw-sys"
-version = "0.4.10"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_api"
-version = "0.4.11"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
@@ -829,9 +856,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.20"
+version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "matches"
@@ -841,18 +868,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "memchr"
-version = "2.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
-
-[[package]]
-name = "memoffset"
-version = "0.9.0"
+version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
-dependencies = [
- "autocfg",
-]
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "minimal-lexical"
@@ -862,17 +880,17 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "napi"
-version = "2.10.3"
+version = "2.16.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a967e17e9ba4e015a7bf9b92f90aa8dc321c6d913f6a6d2afd5b66a8ab36fc81"
+checksum = "214f07a80874bb96a8433b3cdfc84980d56c7b02e1a0d7ba4ba0db5cef785e2b"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
"ctor",
+ "napi-derive",
"napi-sys",
"once_cell",
"serde",
"serde_json",
- "thread_local",
]
[[package]]
@@ -883,23 +901,23 @@ checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b"
[[package]]
name = "napi-derive"
-version = "2.14.0"
+version = "2.16.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a01328a2da5c52a77fe839ec8576ddf063529cf23db8958b1030005675f32c45"
+checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c"
dependencies = [
"cfg-if",
"convert_case",
"napi-derive-backend",
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.90",
]
[[package]]
name = "napi-derive-backend"
-version = "1.0.53"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2470ee27b89da8973defd10dec6def6b412f2873ecc173c9cdc7f44e324d83dc"
+checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf"
dependencies = [
"convert_case",
"once_cell",
@@ -907,24 +925,18 @@ dependencies = [
"quote",
"regex",
"semver",
- "syn 1.0.109",
+ "syn 2.0.90",
]
[[package]]
name = "napi-sys"
-version = "2.3.0"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2503fa6af34dc83fb74888df8b22afe933b58d37daf7d80424b1c60c68196b8b"
+checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3"
dependencies = [
"libloading",
]
-[[package]]
-name = "new_debug_unreachable"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
-
[[package]]
name = "nom"
version = "7.1.3"
@@ -943,18 +955,18 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-traits"
-version = "0.2.17"
+version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
-version = "1.18.0"
+version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "os_str_bytes"
@@ -970,15 +982,15 @@ checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4"
[[package]]
name = "parcel_selectors"
-version = "0.26.4"
+version = "0.28.2"
dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.6.0",
"cssparser",
- "fxhash",
"log",
- "phf 0.10.1",
+ "phf",
"phf_codegen",
"precomputed-hash",
+ "rustc-hash",
"schemars",
"serde",
"smallvec",
@@ -999,21 +1011,11 @@ dependencies = [
"vlq",
]
-[[package]]
-name = "parking_lot"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
[[package]]
name = "parking_lot_core"
-version = "0.9.9"
+version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
@@ -1023,25 +1025,16 @@ dependencies = [
]
[[package]]
-name = "paste"
-version = "1.0.14"
+name = "pastey"
+version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
+checksum = "b3a8cb46bdc156b1c90460339ae6bfd45ba0394e5effbaa640badb4987fdc261"
[[package]]
name = "pathdiff"
-version = "0.2.1"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
-
-[[package]]
-name = "phf"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
-dependencies = [
- "phf_shared 0.10.0",
-]
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]]
name = "phf"
@@ -1050,27 +1043,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros",
- "phf_shared 0.11.2",
+ "phf_shared",
]
[[package]]
name = "phf_codegen"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.10.0"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
+checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
dependencies = [
- "phf_shared 0.10.0",
- "rand",
+ "phf_generator",
+ "phf_shared",
]
[[package]]
@@ -1079,7 +1062,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
- "phf_shared 0.11.2",
+ "phf_shared",
"rand",
]
@@ -1089,20 +1072,11 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
- "phf_generator 0.11.2",
- "phf_shared 0.11.2",
+ "phf_generator",
+ "phf_shared",
"proc-macro2",
"quote",
- "syn 2.0.39",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
-dependencies = [
- "siphasher",
+ "syn 2.0.90",
]
[[package]]
@@ -1114,12 +1088,6 @@ dependencies = [
"siphasher",
]
-[[package]]
-name = "ppv-lite86"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
-
[[package]]
name = "precomputed-hash"
version = "0.1.1"
@@ -1142,32 +1110,41 @@ dependencies = [
[[package]]
name = "predicates"
-version = "3.0.4"
+version = "3.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0"
+checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
dependencies = [
"anstyle",
"difflib",
- "itertools 0.11.0",
"predicates-core",
]
[[package]]
name = "predicates-core"
-version = "1.0.6"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
+checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
[[package]]
name = "predicates-tree"
-version = "1.0.9"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
+checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
dependencies = [
"predicates-core",
"termtree",
]
+[[package]]
+name = "pretty_assertions"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
+dependencies = [
+ "diff",
+ "yansi",
+]
+
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@@ -1194,9 +1171,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
dependencies = [
"unicode-ident",
]
@@ -1223,13 +1200,19 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.33"
+version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
[[package]]
name = "radium"
version = "0.7.0"
@@ -1242,18 +1225,6 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
- "libc",
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
-dependencies = [
- "ppv-lite86",
"rand_core",
]
@@ -1262,15 +1233,12 @@ name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom",
-]
[[package]]
name = "rayon"
-version = "1.8.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
@@ -1278,9 +1246,9 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.12.0"
+version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
@@ -1288,18 +1256,18 @@ dependencies = [
[[package]]
name = "redox_syscall"
-version = "0.4.1"
+version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.6.0",
]
[[package]]
name = "regex"
-version = "1.10.2"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@@ -1309,9 +1277,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.3"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@@ -1320,27 +1288,28 @@ dependencies = [
[[package]]
name = "regex-syntax"
-version = "0.8.2"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rend"
-version = "0.4.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd"
+checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
dependencies = [
"bytecheck",
]
[[package]]
name = "rkyv"
-version = "0.7.42"
+version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58"
+checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
dependencies = [
"bitvec",
"bytecheck",
+ "bytes",
"hashbrown 0.12.3",
"ptr_meta",
"rend",
@@ -1352,22 +1321,28 @@ dependencies = [
[[package]]
name = "rkyv_derive"
-version = "0.7.42"
+version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d"
+checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
+[[package]]
+name = "rustc-hash"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
+
[[package]]
name = "rustix"
-version = "0.38.21"
+version = "0.38.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
dependencies = [
- "bitflags 2.4.1",
+ "bitflags 2.6.0",
"errno",
"libc",
"linux-raw-sys",
@@ -1376,9 +1351,9 @@ dependencies = [
[[package]]
name = "ryu"
-version = "1.0.15"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "same-file"
@@ -1391,11 +1366,12 @@ dependencies = [
[[package]]
name = "schemars"
-version = "0.8.15"
+version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c"
+checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
dependencies = [
"dyn-clone",
+ "indexmap 2.7.0",
"schemars_derive",
"serde",
"serde_json",
@@ -1404,14 +1380,14 @@ dependencies = [
[[package]]
name = "schemars_derive"
-version = "0.8.15"
+version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c"
+checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
- "syn 1.0.109",
+ "syn 2.0.90",
]
[[package]]
@@ -1428,19 +1404,29 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "semver"
-version = "1.0.20"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
+checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
[[package]]
name = "serde"
-version = "1.0.192"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
+ "serde_core",
"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-detach"
version = "0.0.1"
@@ -1452,58 +1438,63 @@ dependencies = [
]
[[package]]
-name = "serde-wasm-bindgen"
-version = "0.4.5"
+name = "serde_bytes"
+version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf"
+checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
dependencies = [
- "js-sys",
"serde",
- "wasm-bindgen",
]
[[package]]
-name = "serde_bytes"
-version = "0.11.12"
+name = "serde_core"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
- "serde",
+ "serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.192"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.90",
]
[[package]]
name = "serde_derive_internals"
-version = "0.26.0"
+version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c"
+checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.90",
]
[[package]]
name = "serde_json"
-version = "1.0.108"
+version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
"itoa",
+ "memchr",
"ryu",
"serde",
]
+[[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"
@@ -1515,9 +1506,9 @@ dependencies = [
[[package]]
name = "simdutf8"
-version = "0.1.4"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "siphasher"
@@ -1527,17 +1518,18 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "smallvec"
-version = "1.11.1"
+version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
dependencies = [
"serde",
]
[[package]]
name = "static-self"
-version = "0.1.1"
+version = "0.1.2"
dependencies = [
+ "indexmap 2.7.0",
"smallvec",
"static-self-derive",
]
@@ -1551,32 +1543,6 @@ dependencies = [
"syn 1.0.109",
]
-[[package]]
-name = "string_cache"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
-dependencies = [
- "new_debug_unreachable",
- "once_cell",
- "parking_lot",
- "phf_shared 0.10.0",
- "precomputed-hash",
- "serde",
-]
-
-[[package]]
-name = "string_cache_codegen"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
- "proc-macro2",
- "quote",
-]
-
[[package]]
name = "strsim"
version = "0.10.0"
@@ -1596,9 +1562,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.39"
+version = "2.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
dependencies = [
"proc-macro2",
"quote",
@@ -1613,73 +1579,63 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tempfile"
-version = "3.8.1"
+version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
dependencies = [
"cfg-if",
"fastrand",
- "redox_syscall",
+ "once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "termcolor"
-version = "1.3.0"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "termtree"
-version = "0.4.1"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
+checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
[[package]]
name = "textwrap"
-version = "0.16.0"
+version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
+checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
[[package]]
name = "thiserror"
-version = "1.0.50"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.50"
+version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
-]
-
-[[package]]
-name = "thread_local"
-version = "1.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
-dependencies = [
- "cfg-if",
- "once_cell",
+ "syn 2.0.90",
]
[[package]]
name = "tinyvec"
-version = "1.6.0"
+version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
+checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
dependencies = [
"tinyvec_macros",
]
@@ -1701,27 +1657,27 @@ dependencies = [
[[package]]
name = "unicode-ident"
-version = "1.0.12"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unicode-segmentation"
-version = "1.10.1"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "uuid"
-version = "1.5.0"
+version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
+checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
[[package]]
name = "version_check"
-version = "0.9.4"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "vlq"
@@ -1740,9 +1696,9 @@ dependencies = [
[[package]]
name = "walkdir"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
@@ -1754,36 +1710,45 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
[[package]]
name = "wasm-bindgen"
-version = "0.2.88"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
dependencies = [
"cfg-if",
+ "once_cell",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.88"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
dependencies = [
"bumpalo",
"log",
- "once_cell",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.90",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.88"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1791,22 +1756,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.88"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.90",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.88"
+version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
[[package]]
name = "winapi"
@@ -1826,11 +1791,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.6"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "winapi",
+ "windows-sys",
]
[[package]]
@@ -1841,31 +1806,32 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
-version = "0.51.1"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
-version = "0.48.0"
+version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
-version = "0.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
+ "windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
@@ -1874,45 +1840,60 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+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.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.5"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags 2.6.0",
+]
[[package]]
name = "wyz"
@@ -1929,22 +1910,28 @@ dependencies = [
"tap",
]
+[[package]]
+name = "yansi"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+
[[package]]
name = "zerocopy"
-version = "0.7.25"
+version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557"
+checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.25"
+version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b"
+checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.90",
]
diff --git a/Cargo.toml b/Cargo.toml
index 4d00dcd70..c113a3921 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,21 +1,22 @@
[workspace]
members = [
"node",
+ "napi",
"selectors",
"c",
"derive",
"static-self",
- "static-self-derive"
+ "static-self-derive",
]
[package]
authors = ["Devon Govett "]
name = "lightningcss"
-version = "1.0.0-alpha.51"
+version = "1.0.0-alpha.70"
description = "A CSS parser, transformer, and minifier"
license = "MPL-2.0"
edition = "2021"
-keywords = [ "CSS", "minifier", "Parcel" ]
+keywords = ["CSS", "minifier", "Parcel"]
repository = "https://github.com/parcel-bundler/lightningcss"
[package.metadata.docs.rs]
@@ -33,24 +34,37 @@ path = "src/lib.rs"
crate-type = ["rlib"]
[features]
-default = ["bundler", "grid", "nodejs", "sourcemap"]
+default = ["bundler", "nodejs", "sourcemap"]
browserslist = ["browserslist-rs"]
bundler = ["dashmap", "sourcemap", "rayon"]
cli = ["atty", "clap", "serde_json", "browserslist", "jemallocator"]
-grid = []
jsonschema = ["schemars", "serde", "parcel_selectors/jsonschema"]
-nodejs = ["dep:serde"]
-serde = ["dep:serde", "smallvec/serde", "cssparser/serde", "parcel_selectors/serde", "into_owned"]
+nodejs = ["dep:serde", "dep:serde-content"]
+serde = [
+ "dep:serde",
+ "dep:serde-content",
+ "bitflags/serde",
+ "smallvec/serde",
+ "cssparser/serde",
+ "parcel_selectors/serde",
+ "into_owned",
+]
sourcemap = ["parcel_sourcemap"]
-visitor = ["lightningcss-derive"]
-into_owned = ["static-self", "static-self/smallvec", "parcel_selectors/into_owned"]
+visitor = []
+into_owned = [
+ "static-self",
+ "static-self/smallvec",
+ "static-self/indexmap",
+ "parcel_selectors/into_owned",
+]
substitute_variables = ["visitor", "into_owned"]
[dependencies]
-serde = { version = "1.0.123", features = ["derive"], optional = true }
+serde = { version = "1.0.228", features = ["derive"], optional = true }
+serde-content = { version = "0.1.2", features = ["serde"], optional = true }
cssparser = "0.33.0"
cssparser-color = "0.1.0"
-parcel_selectors = { version = "0.26.4", path = "./selectors" }
+parcel_selectors = { version = "0.28.2", path = "./selectors" }
itertools = "0.10.1"
smallvec = { version = "1.7.0", features = ["union"] }
bitflags = "2.2.1"
@@ -59,21 +73,27 @@ data-encoding = "2.3.2"
lazy_static = "1.4.0"
const-str = "0.3.1"
pathdiff = "0.2.1"
-ahash = "0.7.6"
-paste = "1.0.12"
+ahash = "0.8.7"
+pastey = "0.1.0"
+indexmap = { version = "2.2.6", features = ["serde"] }
# CLI deps
atty = { version = "0.2", optional = true }
clap = { version = "3.0.6", features = ["derive"], optional = true }
-browserslist-rs = { version = "0.12.3", optional = true }
+browserslist-rs = { version = "0.19.0", optional = true }
rayon = { version = "1.5.1", optional = true }
dashmap = { version = "5.0.0", optional = true }
serde_json = { version = "1.0.78", optional = true }
-lightningcss-derive = { version = "=1.0.0-alpha.42", path = "./derive", optional = true }
-schemars = { version = "0.8.11", features = ["smallvec"], optional = true }
-static-self = { version = "0.1.0", path = "static-self", optional = true }
+lightningcss-derive = { version = "=1.0.0-alpha.43", path = "./derive" }
+schemars = { version = "0.8.19", features = ["smallvec", "indexmap2"], optional = true }
+static-self = { version = "0.1.2", path = "static-self", optional = true }
[target.'cfg(target_os = "macos")'.dependencies]
-jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"], optional = true }
+jemallocator = { version = "0.3.2", features = [
+ "disable_initial_exec_tls",
+], optional = true }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+getrandom = { version = "0.3", default-features = false }
[dev-dependencies]
indoc = "1.0.3"
@@ -81,6 +101,7 @@ assert_cmd = "2.0"
assert_fs = "1.0"
predicates = "2.1"
serde_json = "1"
+pretty_assertions = "1.4.0"
[[test]]
name = "cli_integration_tests"
diff --git a/README.md b/README.md
index 035544626..f44d7b3cd 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ An extremely fast CSS parser, transformer, and minifier written in Rust. Use it
- **Extremely fast** – Parsing and minifying large files is completed in milliseconds, often with significantly smaller output than other tools. See [benchmarks](#benchmarks) below.
- **Typed property values** – many other CSS parsers treat property values as an untyped series of tokens. This means that each transformer that wants to do something with these values must interpret them itself, leading to duplicate work and inconsistencies. Lightning CSS parses all values using the grammar from the CSS specification, and exposes a specific value type for each property.
-- **Browser-grade parser** – Lightning CSS is built on the [cssparser](https://github.com/servo/rust-cssparser) and [selectors](https://github.com/servo/servo/tree/master/components/selectors) crates created by Mozilla and used by Firefox and Servo. These provide a solid general purpose CSS-parsing foundation on top of which Lightning CSS implements support for all specific CSS rules and properties.
+- **Browser-grade parser** – Lightning CSS is built on the [cssparser](https://github.com/servo/rust-cssparser) and [selectors](https://github.com/servo/stylo/tree/main/selectors) crates created by Mozilla and used by Firefox and Servo. These provide a solid general purpose CSS-parsing foundation on top of which Lightning CSS implements support for all specific CSS rules and properties.
- **Minification** – One of the main purposes of Lightning CSS is to minify CSS to make it smaller. This includes many optimizations including:
- Combining longhand properties into shorthands where possible.
- Merging adjacent rules with the same selectors or declarations when it is safe to do so.
diff --git a/c/Cargo.toml b/c/Cargo.toml
index 2b56703a8..f9a9d167f 100644
--- a/c/Cargo.toml
+++ b/c/Cargo.toml
@@ -11,7 +11,7 @@ crate-type = ["cdylib"]
[dependencies]
lightningcss = { path = "../", features = ["browserslist"] }
parcel_sourcemap = { version = "2.1.1", features = ["json"] }
-browserslist-rs = { version = "0.12.3" }
+browserslist-rs = { version = "0.19.0" }
[build-dependencies]
cbindgen = "0.24.3"
diff --git a/c/build.rs b/c/build.rs
index 6dac4f91c..02fcb75b1 100644
--- a/c/build.rs
+++ b/c/build.rs
@@ -1,5 +1,3 @@
-extern crate cbindgen;
-
use std::env;
fn main() {
diff --git a/c/src/lib.rs b/c/src/lib.rs
index 68bfc7e48..759a18dbe 100644
--- a/c/src/lib.rs
+++ b/c/src/lib.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::not_unsafe_ptr_arg_deref)]
+
use std::collections::HashSet;
use std::ffi::{CStr, CString};
use std::mem::ManuallyDrop;
@@ -279,6 +281,7 @@ pub extern "C" fn lightningcss_stylesheet_parse(
Some(lightningcss::css_modules::Config {
pattern,
dashed_idents: options.css_modules_dashed_idents,
+ ..Default::default()
})
} else {
None
diff --git a/cli/postinstall.js b/cli/postinstall.js
index abf9dc191..19dadc796 100644
--- a/cli/postinstall.js
+++ b/cli/postinstall.js
@@ -3,7 +3,8 @@ let path = require('path');
let parts = [process.platform, process.arch];
if (process.platform === 'linux') {
- const {MUSL, family} = require('detect-libc');
+ const {MUSL, familySync} = require('detect-libc');
+ const family = familySync();
if (family === MUSL) {
parts.push('musl');
} else if (process.arch === 'arm') {
diff --git a/derive/Cargo.toml b/derive/Cargo.toml
index 1864748e9..ce55e5cca 100644
--- a/derive/Cargo.toml
+++ b/derive/Cargo.toml
@@ -2,7 +2,7 @@
authors = ["Devon Govett "]
name = "lightningcss-derive"
description = "Derive macros for lightningcss"
-version = "1.0.0-alpha.42"
+version = "1.0.0-alpha.43"
license = "MPL-2.0"
edition = "2021"
repository = "https://github.com/parcel-bundler/lightningcss"
@@ -14,3 +14,4 @@ proc-macro = true
syn = { version = "1.0", features = ["extra-traits"] }
quote = "1.0"
proc-macro2 = "1.0"
+convert_case = "0.6.0"
diff --git a/derive/src/lib.rs b/derive/src/lib.rs
index 00b77a266..122414911 100644
--- a/derive/src/lib.rs
+++ b/derive/src/lib.rs
@@ -1,293 +1,20 @@
-use std::collections::HashSet;
+use proc_macro::TokenStream;
-use proc_macro::{self, TokenStream};
-use proc_macro2::{Span, TokenStream as TokenStream2};
-use quote::quote;
-use syn::{
- parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields,
- GenericParam, Generics, Ident, Member, Token, Type, Visibility,
-};
+mod parse;
+mod to_css;
+mod visit;
#[proc_macro_derive(Visit, attributes(visit, skip_visit, skip_type, visit_types))]
pub fn derive_visit_children(input: TokenStream) -> TokenStream {
- let DeriveInput {
- ident,
- data,
- generics,
- attrs,
- ..
- } = parse_macro_input!(input);
-
- let options: Vec = attrs
- .iter()
- .filter_map(|attr| {
- if attr.path.is_ident("visit") {
- let opts: VisitOptions = attr.parse_args().unwrap();
- Some(opts)
- } else {
- None
- }
- })
- .collect();
-
- let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) {
- let types: VisitTypes = attr.parse_args().unwrap();
- let types = types.types;
- Some(quote! { crate::visit_types!(#(#types)|*) })
- } else {
- None
- };
-
- if options.is_empty() {
- derive(&ident, &data, &generics, None, visit_types)
- } else {
- options
- .into_iter()
- .map(|options| derive(&ident, &data, &generics, Some(options), visit_types.clone()))
- .collect()
- }
-}
-
-fn derive(
- ident: &Ident,
- data: &Data,
- generics: &Generics,
- options: Option,
- visit_types: Option,
-) -> TokenStream {
- let mut impl_generics = generics.clone();
- let mut type_defs = quote! {};
- let generics = if let Some(VisitOptions {
- generic: Some(generic), ..
- }) = &options
- {
- let mappings = generics
- .type_params()
- .zip(generic.type_params())
- .map(|(a, b)| quote! { type #a = #b; });
- type_defs = quote! { #(#mappings)* };
- impl_generics.params.clear();
- generic
- } else {
- &generics
- };
-
- if impl_generics.lifetimes().next().is_none() {
- impl_generics.params.insert(0, parse_quote! { 'i })
- }
-
- let lifetime = impl_generics.lifetimes().next().unwrap().clone();
- let t = impl_generics.type_params().find(|g| &g.ident.to_string() == "R");
- let v = quote! { __V };
- let t = if let Some(t) = t {
- GenericParam::Type(t.ident.clone().into())
- } else {
- let t: GenericParam = parse_quote! { __T };
- impl_generics
- .params
- .push(parse_quote! { #t: crate::visitor::Visit<#lifetime, __T, #v> });
- t
- };
-
- impl_generics
- .params
- .push(parse_quote! { #v: ?Sized + crate::visitor::Visitor<#lifetime, #t> });
-
- for ty in generics.type_params() {
- let name = &ty.ident;
- impl_generics.make_where_clause().predicates.push(parse_quote! {
- #name: Visit<#lifetime, #t, #v>
- })
- }
-
- let mut seen_types = HashSet::new();
- let mut child_types = Vec::new();
- let mut visit = Vec::new();
- match data {
- Data::Struct(s) => {
- for (
- index,
- Field {
- vis, ty, ident, attrs, ..
- },
- ) in s.fields.iter().enumerate()
- {
- if attrs.iter().any(|attr| attr.path.is_ident("skip_visit")) {
- continue;
- }
-
- if matches!(ty, Type::Reference(_)) || !matches!(vis, Visibility::Public(..)) {
- continue;
- }
-
- if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) {
- seen_types.insert(ty.clone());
- child_types.push(quote! {
- <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
- });
- }
-
- let name = ident
- .as_ref()
- .map_or_else(|| Member::Unnamed(index.into()), |ident| Member::Named(ident.clone()));
- visit.push(quote! { self.#name.visit(visitor)?; })
- }
- }
- Data::Enum(DataEnum { variants, .. }) => {
- let variants = variants
- .iter()
- .map(|variant| {
- let name = &variant.ident;
- let mut field_names = Vec::new();
- let mut visit_fields = Vec::new();
- for (index, Field { ty, ident, attrs, .. }) in variant.fields.iter().enumerate() {
- let name = ident.as_ref().map_or_else(
- || Ident::new(&format!("_{}", index), Span::call_site()),
- |ident| ident.clone(),
- );
- field_names.push(name.clone());
-
- if matches!(ty, Type::Reference(_)) {
- continue;
- }
-
- if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) && !skip_type(&variant.attrs)
- {
- seen_types.insert(ty.clone());
- child_types.push(quote! {
- <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
- });
- }
-
- visit_fields.push(quote! { #name.visit(visitor)?; })
- }
-
- match variant.fields {
- Fields::Unnamed(_) => {
- quote! {
- Self::#name(#(#field_names),*) => {
- #(#visit_fields)*
- }
- }
- }
- Fields::Named(_) => {
- quote! {
- Self::#name { #(#field_names),* } => {
- #(#visit_fields)*
- }
- }
- }
- Fields::Unit => quote! {},
- }
- })
- .collect::();
-
- visit.push(quote! {
- match self {
- #variants
- _ => {}
- }
- })
- }
- _ => {}
- }
-
- if visit_types.is_none() && child_types.is_empty() {
- child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() });
- }
-
- let (_, ty_generics, _) = generics.split_for_impl();
- let (impl_generics, _, where_clause) = impl_generics.split_for_impl();
-
- let self_visit = if let Some(VisitOptions {
- visit: Some(visit),
- kind: Some(kind),
- ..
- }) = &options
- {
- child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() });
-
- quote! {
- fn visit(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
- if visitor.visit_types().contains(crate::visitor::VisitTypes::#kind) {
- visitor.#visit(self)
- } else {
- self.visit_children(visitor)
- }
- }
- }
- } else {
- quote! {}
- };
-
- let child_types = visit_types.unwrap_or_else(|| {
- quote! {
- {
- #type_defs
- crate::visitor::VisitTypes::from_bits_retain(#(#child_types)|*)
- }
- }
- });
-
- let output = quote! {
- impl #impl_generics Visit<#lifetime, #t, #v> for #ident #ty_generics #where_clause {
- const CHILD_TYPES: crate::visitor::VisitTypes = #child_types;
-
- #self_visit
-
- fn visit_children(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
- if !<#lifetime, #t, #v>>::CHILD_TYPES.intersects(visitor.visit_types()) {
- return Ok(())
- }
-
- #(#visit)*
-
- Ok(())
- }
- }
- };
-
- output.into()
-}
-
-fn skip_type(attrs: &Vec) -> bool {
- attrs.iter().any(|attr| attr.path.is_ident("skip_type"))
-}
-
-struct VisitOptions {
- visit: Option,
- kind: Option,
- generic: Option,
-}
-
-impl Parse for VisitOptions {
- fn parse(input: syn::parse::ParseStream) -> syn::Result {
- let (visit, kind, comma) = if input.peek(Ident) {
- let visit: Ident = input.parse()?;
- let _: Token![,] = input.parse()?;
- let kind: Ident = input.parse()?;
- let comma: Result = input.parse();
- (Some(visit), Some(kind), comma.is_ok())
- } else {
- (None, None, true)
- };
- let generic: Option = if comma { Some(input.parse()?) } else { None };
- Ok(Self { visit, kind, generic })
- }
+ visit::derive_visit_children(input)
}
-struct VisitTypes {
- types: Vec,
+#[proc_macro_derive(Parse, attributes(css))]
+pub fn derive_parse(input: TokenStream) -> TokenStream {
+ parse::derive_parse(input)
}
-impl Parse for VisitTypes {
- fn parse(input: syn::parse::ParseStream) -> syn::Result {
- let first: Ident = input.parse()?;
- let mut types = vec![first];
- while input.parse::().is_ok() {
- let id: Ident = input.parse()?;
- types.push(id);
- }
- Ok(Self { types })
- }
+#[proc_macro_derive(ToCss, attributes(css))]
+pub fn derive_to_css(input: TokenStream) -> TokenStream {
+ to_css::derive_to_css(input)
}
diff --git a/derive/src/parse.rs b/derive/src/parse.rs
new file mode 100644
index 000000000..995b344e3
--- /dev/null
+++ b/derive/src/parse.rs
@@ -0,0 +1,213 @@
+use convert_case::Casing;
+use proc_macro::{self, TokenStream};
+use proc_macro2::{Literal, Span, TokenStream as TokenStream2};
+use quote::quote;
+use syn::{
+ parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Fields, Ident, Token,
+};
+
+pub fn derive_parse(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ data,
+ mut generics,
+ attrs,
+ ..
+ } = parse_macro_input!(input);
+ let opts = CssOptions::parse_attributes(&attrs).unwrap();
+ let cloned_generics = generics.clone();
+ let (_, ty_generics, _) = cloned_generics.split_for_impl();
+
+ if generics.lifetimes().next().is_none() {
+ generics.params.insert(0, parse_quote! { 'i })
+ }
+
+ let lifetime = generics.lifetimes().next().unwrap().clone();
+ let (impl_generics, _, where_clause) = generics.split_for_impl();
+
+ let imp = match &data {
+ Data::Enum(data) => derive_enum(&data, &ident, &opts),
+ _ => todo!(),
+ };
+
+ let output = quote! {
+ impl #impl_generics Parse<#lifetime> for #ident #ty_generics #where_clause {
+ fn parse<'t>(input: &mut Parser<#lifetime, 't>) -> Result<#lifetime, ParserError<#lifetime>>> {
+ #imp
+ }
+ }
+ };
+
+ output.into()
+}
+
+fn derive_enum(data: &DataEnum, ident: &Ident, opts: &CssOptions) -> TokenStream2 {
+ let mut idents = Vec::new();
+ let mut non_idents = Vec::new();
+ for (index, variant) in data.variants.iter().enumerate() {
+ let name = &variant.ident;
+ let fields = variant
+ .fields
+ .iter()
+ .enumerate()
+ .map(|(index, field)| {
+ field.ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ )
+ })
+ .collect::<_>>();
+
+ let mut expr = match &variant.fields {
+ Fields::Unit => {
+ idents.push((
+ Literal::string(&variant.ident.to_string().to_case(opts.case)),
+ name.clone(),
+ ));
+ continue;
+ }
+ Fields::Named(_) => {
+ quote! {
+ return Ok(#ident::#name { #(#fields),* })
+ }
+ }
+ Fields::Unnamed(_) => {
+ quote! {
+ return Ok(#ident::#name(#(#fields),*))
+ }
+ }
+ };
+
+ // Group multiple ident branches together.
+ if !idents.is_empty() {
+ if idents.len() == 1 {
+ let (s, name) = idents.remove(0);
+ non_idents.push(quote! {
+ if input.try_parse(|input| input.expect_ident_matching(#s)).is_ok() {
+ return Ok(#ident::#name)
+ }
+ });
+ } else {
+ let matches = idents
+ .iter()
+ .map(|(s, name)| {
+ quote! {
+ #s => return Ok(#ident::#name),
+ }
+ })
+ .collect::<_>>();
+ non_idents.push(quote! {
+ {
+ let state = input.state();
+ if let Ok(ident) = input.try_parse(|input| input.expect_ident_cloned()) {
+ cssparser::match_ignore_ascii_case! { &*ident,
+ #(#matches)*
+ _ => {}
+ }
+ input.reset(&state);
+ }
+ }
+ });
+ idents.clear();
+ }
+ }
+
+ let is_last = index == data.variants.len() - 1;
+
+ for (index, field) in variant.fields.iter().enumerate().rev() {
+ let ty = &field.ty;
+ let field_name = field.ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ );
+ if is_last {
+ expr = quote! {
+ let #field_name = <#ty>::parse(input)?;
+ #expr
+ };
+ } else {
+ expr = quote! {
+ if let Ok(#field_name) = input.try_parse(<#ty>::parse) {
+ #expr
+ }
+ };
+ }
+ }
+
+ non_idents.push(expr);
+ }
+
+ let idents = if idents.is_empty() {
+ quote! {}
+ } else if idents.len() == 1 {
+ let (s, name) = idents.remove(0);
+ quote! {
+ input.expect_ident_matching(#s)?;
+ Ok(#ident::#name)
+ }
+ } else {
+ let idents = idents
+ .into_iter()
+ .map(|(s, name)| {
+ quote! {
+ #s => Ok(#ident::#name),
+ }
+ })
+ .collect::<_>>();
+ quote! {
+ let location = input.current_source_location();
+ let ident = input.expect_ident()?;
+ cssparser::match_ignore_ascii_case! { &*ident,
+ #(#idents)*
+ _ => Err(location.new_unexpected_token_error(
+ cssparser::Token::Ident(ident.clone())
+ ))
+ }
+ }
+ };
+
+ let output = quote! {
+ #(#non_idents)*
+ #idents
+ };
+
+ output.into()
+}
+
+pub struct CssOptions {
+ pub case: convert_case::Case,
+}
+
+impl CssOptions {
+ pub fn parse_attributes(attrs: &Vec) -> syn::Result {
+ for attr in attrs {
+ if attr.path.is_ident("css") {
+ let opts: CssOptions = attr.parse_args()?;
+ return Ok(opts);
+ }
+ }
+
+ Ok(CssOptions {
+ case: convert_case::Case::Kebab,
+ })
+ }
+}
+
+impl Parse for CssOptions {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ let mut case = convert_case::Case::Kebab;
+ while !input.is_empty() {
+ let k: Ident = input.parse()?;
+ let _: Token![=] = input.parse()?;
+ let v: Ident = input.parse()?;
+
+ if k == "case" {
+ if v == "lower" {
+ case = convert_case::Case::Flat;
+ }
+ }
+ }
+
+ Ok(Self { case })
+ }
+}
diff --git a/derive/src/to_css.rs b/derive/src/to_css.rs
new file mode 100644
index 000000000..739a16d40
--- /dev/null
+++ b/derive/src/to_css.rs
@@ -0,0 +1,156 @@
+use convert_case::Casing;
+use proc_macro::{self, TokenStream};
+use proc_macro2::{Literal, Span, TokenStream as TokenStream2};
+use quote::quote;
+use syn::{parse_macro_input, Data, DataEnum, DeriveInput, Fields, Ident, Type};
+
+use crate::parse::CssOptions;
+
+pub fn derive_to_css(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ data,
+ generics,
+ attrs,
+ ..
+ } = parse_macro_input!(input);
+
+ let opts = CssOptions::parse_attributes(&attrs).unwrap();
+ let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
+
+ let imp = match &data {
+ Data::Enum(data) => derive_enum(&data, &opts),
+ _ => todo!(),
+ };
+
+ let output = quote! {
+ impl #impl_generics ToCss for #ident #ty_generics #where_clause {
+ fn to_css(&self, dest: &mut Printer) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
+ #imp
+ }
+ }
+ };
+
+ output.into()
+}
+
+fn derive_enum(data: &DataEnum, opts: &CssOptions) -> TokenStream2 {
+ let variants = data
+ .variants
+ .iter()
+ .map(|variant| {
+ let name = &variant.ident;
+ let fields = variant
+ .fields
+ .iter()
+ .enumerate()
+ .map(|(index, field)| {
+ field.ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ )
+ })
+ .collect::<_>>();
+
+ #[derive(PartialEq)]
+ enum NeedsSpace {
+ Yes,
+ No,
+ Maybe,
+ }
+
+ let mut needs_space = NeedsSpace::No;
+ let mut fields_iter = variant.fields.iter().zip(fields.iter()).peekable();
+ let mut writes = Vec::new();
+ let mut has_needs_space = false;
+ while let Some((field, name)) = fields_iter.next() {
+ writes.push(if fields.len() > 1 {
+ let space = match needs_space {
+ NeedsSpace::Yes => quote! { dest.write_char(' ')?; },
+ NeedsSpace::No => quote! {},
+ NeedsSpace::Maybe => {
+ has_needs_space = true;
+ quote! {
+ if needs_space {
+ dest.write_char(' ')?;
+ }
+ }
+ }
+ };
+
+ if is_option(&field.ty) {
+ needs_space = NeedsSpace::Maybe;
+ let after_space = if matches!(fields_iter.peek(), Some((field, _)) if !is_option(&field.ty)) {
+ // If the next field is non-optional, just insert the space here.
+ needs_space = NeedsSpace::No;
+ quote! { dest.write_char(' ')?; }
+ } else {
+ quote! {}
+ };
+ quote! {
+ if let Some(v) = #name {
+ #space
+ v.to_css(dest)?;
+ #after_space
+ }
+ }
+ } else {
+ needs_space = NeedsSpace::Yes;
+ quote! {
+ #space
+ #name.to_css(dest)?;
+ }
+ }
+ } else {
+ quote! { #name.to_css(dest) }
+ });
+ }
+
+ if writes.len() > 1 {
+ writes.push(quote! { Ok(()) });
+ }
+
+ if has_needs_space {
+ writes.insert(0, quote! { let mut needs_space = false });
+ }
+
+ match variant.fields {
+ Fields::Unit => {
+ let s = Literal::string(&variant.ident.to_string().to_case(opts.case));
+ quote! {
+ Self::#name => dest.write_str(#s)
+ }
+ }
+ Fields::Named(_) => {
+ quote! {
+ Self::#name { #(#fields),* } => {
+ #(#writes)*
+ }
+ }
+ }
+ Fields::Unnamed(_) => {
+ quote! {
+ Self::#name(#(#fields),*) => {
+ #(#writes)*
+ }
+ }
+ }
+ }
+ })
+ .collect::<_>>();
+
+ let output = quote! {
+ match self {
+ #(#variants),*
+ }
+ };
+
+ output.into()
+}
+
+fn is_option(ty: &Type) -> bool {
+ matches!(&ty, Type::Path(p) if p.qself.is_none() && p.path.segments.iter().next().unwrap().ident == "Option")
+}
diff --git a/derive/src/visit.rs b/derive/src/visit.rs
new file mode 100644
index 000000000..020933644
--- /dev/null
+++ b/derive/src/visit.rs
@@ -0,0 +1,292 @@
+use std::collections::HashSet;
+
+use proc_macro::{self, TokenStream};
+use proc_macro2::{Span, TokenStream as TokenStream2};
+use quote::quote;
+use syn::{
+ parse::Parse, parse_macro_input, parse_quote, Attribute, Data, DataEnum, DeriveInput, Field, Fields,
+ GenericParam, Generics, Ident, Member, Token, Type, Visibility,
+};
+
+pub fn derive_visit_children(input: TokenStream) -> TokenStream {
+ let DeriveInput {
+ ident,
+ data,
+ generics,
+ attrs,
+ ..
+ } = parse_macro_input!(input);
+
+ let options: Vec = attrs
+ .iter()
+ .filter_map(|attr| {
+ if attr.path.is_ident("visit") {
+ let opts: VisitOptions = attr.parse_args().unwrap();
+ Some(opts)
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ let visit_types = if let Some(attr) = attrs.iter().find(|attr| attr.path.is_ident("visit_types")) {
+ let types: VisitTypes = attr.parse_args().unwrap();
+ let types = types.types;
+ Some(quote! { crate::visit_types!(#(#types)|*) })
+ } else {
+ None
+ };
+
+ if options.is_empty() {
+ derive(&ident, &data, &generics, None, visit_types)
+ } else {
+ options
+ .into_iter()
+ .map(|options| derive(&ident, &data, &generics, Some(options), visit_types.clone()))
+ .collect()
+ }
+}
+
+fn derive(
+ ident: &Ident,
+ data: &Data,
+ generics: &Generics,
+ options: Option,
+ visit_types: Option,
+) -> TokenStream {
+ let mut impl_generics = generics.clone();
+ let mut type_defs = quote! {};
+ let generics = if let Some(VisitOptions {
+ generic: Some(generic), ..
+ }) = &options
+ {
+ let mappings = generics
+ .type_params()
+ .zip(generic.type_params())
+ .map(|(a, b)| quote! { type #a = #b; });
+ type_defs = quote! { #(#mappings)* };
+ impl_generics.params.clear();
+ generic
+ } else {
+ &generics
+ };
+
+ if impl_generics.lifetimes().next().is_none() {
+ impl_generics.params.insert(0, parse_quote! { 'i })
+ }
+
+ let lifetime = impl_generics.lifetimes().next().unwrap().clone();
+ let t = impl_generics.type_params().find(|g| &g.ident.to_string() == "R");
+ let v = quote! { __V };
+ let t = if let Some(t) = t {
+ GenericParam::Type(t.ident.clone().into())
+ } else {
+ let t: GenericParam = parse_quote! { __T };
+ impl_generics
+ .params
+ .push(parse_quote! { #t: crate::visitor::Visit<#lifetime, __T, #v> });
+ t
+ };
+
+ impl_generics
+ .params
+ .push(parse_quote! { #v: ?Sized + crate::visitor::Visitor<#lifetime, #t> });
+
+ for ty in generics.type_params() {
+ let name = &ty.ident;
+ impl_generics.make_where_clause().predicates.push(parse_quote! {
+ #name: Visit<#lifetime, #t, #v>
+ })
+ }
+
+ let mut seen_types = HashSet::new();
+ let mut child_types = Vec::new();
+ let mut visit = Vec::new();
+ match data {
+ Data::Struct(s) => {
+ for (
+ index,
+ Field {
+ vis, ty, ident, attrs, ..
+ },
+ ) in s.fields.iter().enumerate()
+ {
+ if attrs.iter().any(|attr| attr.path.is_ident("skip_visit")) {
+ continue;
+ }
+
+ if matches!(ty, Type::Reference(_)) || !matches!(vis, Visibility::Public(..)) {
+ continue;
+ }
+
+ if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) {
+ seen_types.insert(ty.clone());
+ child_types.push(quote! {
+ <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
+ });
+ }
+
+ let name = ident
+ .as_ref()
+ .map_or_else(|| Member::Unnamed(index.into()), |ident| Member::Named(ident.clone()));
+ visit.push(quote! { self.#name.visit(visitor)?; })
+ }
+ }
+ Data::Enum(DataEnum { variants, .. }) => {
+ let variants = variants
+ .iter()
+ .map(|variant| {
+ let name = &variant.ident;
+ let mut field_names = Vec::new();
+ let mut visit_fields = Vec::new();
+ for (index, Field { ty, ident, attrs, .. }) in variant.fields.iter().enumerate() {
+ let name = ident.as_ref().map_or_else(
+ || Ident::new(&format!("_{}", index), Span::call_site()),
+ |ident| ident.clone(),
+ );
+ field_names.push(name.clone());
+
+ if matches!(ty, Type::Reference(_)) {
+ continue;
+ }
+
+ if visit_types.is_none() && !seen_types.contains(ty) && !skip_type(attrs) && !skip_type(&variant.attrs)
+ {
+ seen_types.insert(ty.clone());
+ child_types.push(quote! {
+ <#ty as Visit<#lifetime, #t, #v>>::CHILD_TYPES.bits()
+ });
+ }
+
+ visit_fields.push(quote! { #name.visit(visitor)?; })
+ }
+
+ match variant.fields {
+ Fields::Unnamed(_) => {
+ quote! {
+ Self::#name(#(#field_names),*) => {
+ #(#visit_fields)*
+ }
+ }
+ }
+ Fields::Named(_) => {
+ quote! {
+ Self::#name { #(#field_names),* } => {
+ #(#visit_fields)*
+ }
+ }
+ }
+ Fields::Unit => quote! {},
+ }
+ })
+ .collect::();
+
+ visit.push(quote! {
+ match self {
+ #variants
+ _ => {}
+ }
+ })
+ }
+ _ => {}
+ }
+
+ if visit_types.is_none() && child_types.is_empty() {
+ child_types.push(quote! { crate::visitor::VisitTypes::empty().bits() });
+ }
+
+ let (_, ty_generics, _) = generics.split_for_impl();
+ let (impl_generics, _, where_clause) = impl_generics.split_for_impl();
+
+ let self_visit = if let Some(VisitOptions {
+ visit: Some(visit),
+ kind: Some(kind),
+ ..
+ }) = &options
+ {
+ child_types.push(quote! { crate::visitor::VisitTypes::#kind.bits() });
+
+ quote! {
+ fn visit(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
+ if visitor.visit_types().contains(crate::visitor::VisitTypes::#kind) {
+ visitor.#visit(self)
+ } else {
+ self.visit_children(visitor)
+ }
+ }
+ }
+ } else {
+ quote! {}
+ };
+
+ let child_types = visit_types.unwrap_or_else(|| {
+ quote! {
+ {
+ #type_defs
+ crate::visitor::VisitTypes::from_bits_retain(#(#child_types)|*)
+ }
+ }
+ });
+
+ let output = quote! {
+ impl #impl_generics Visit<#lifetime, #t, #v> for #ident #ty_generics #where_clause {
+ const CHILD_TYPES: crate::visitor::VisitTypes = #child_types;
+
+ #self_visit
+
+ fn visit_children(&mut self, visitor: &mut #v) -> Result<(), #v::Error> {
+ if !<#lifetime, #t, #v>>::CHILD_TYPES.intersects(visitor.visit_types()) {
+ return Ok(())
+ }
+
+ #(#visit)*
+
+ Ok(())
+ }
+ }
+ };
+
+ output.into()
+}
+
+fn skip_type(attrs: &Vec) -> bool {
+ attrs.iter().any(|attr| attr.path.is_ident("skip_type"))
+}
+
+struct VisitOptions {
+ visit: Option,
+ kind: Option,
+ generic: Option,
+}
+
+impl Parse for VisitOptions {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ let (visit, kind, comma) = if input.peek(Ident) {
+ let visit: Ident = input.parse()?;
+ let _: Token![,] = input.parse()?;
+ let kind: Ident = input.parse()?;
+ let comma: Result = input.parse();
+ (Some(visit), Some(kind), comma.is_ok())
+ } else {
+ (None, None, true)
+ };
+ let generic: Option = if comma { Some(input.parse()?) } else { None };
+ Ok(Self { visit, kind, generic })
+ }
+}
+
+struct VisitTypes {
+ types: Vec,
+}
+
+impl Parse for VisitTypes {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result {
+ let first: Ident = input.parse()?;
+ let mut types = vec![first];
+ while input.parse::().is_ok() {
+ let id: Ident = input.parse()?;
+ types.push(id);
+ }
+ Ok(Self { types })
+ }
+}
diff --git a/napi/Cargo.toml b/napi/Cargo.toml
new file mode 100644
index 000000000..789062ea6
--- /dev/null
+++ b/napi/Cargo.toml
@@ -0,0 +1,33 @@
+[package]
+authors = ["Devon Govett "]
+name = "lightningcss-napi"
+version = "0.4.7"
+description = "Node-API bindings for Lightning CSS"
+license = "MPL-2.0"
+repository = "https://github.com/parcel-bundler/lightningcss"
+edition = "2021"
+
+[features]
+default = []
+visitor = ["lightningcss/visitor"]
+bundler = ["dep:crossbeam-channel", "dep:rayon"]
+
+[dependencies]
+serde = { version = "1.0.201", features = ["derive"] }
+serde-content = { version = "0.1.2", features = ["serde"] }
+serde_bytes = "0.11.5"
+cssparser = "0.33.0"
+lightningcss = { version = "1.0.0-alpha.70", path = "../", features = [
+ "nodejs",
+ "serde",
+] }
+parcel_sourcemap = { version = "2.1.1", features = ["json"] }
+serde-detach = "0.0.1"
+smallvec = { version = "1.7.0", features = ["union"] }
+napi = { version = "2", default-features = false, features = [
+ "napi4",
+ "napi5",
+ "serde-json",
+] }
+crossbeam-channel = { version = "0.5.6", optional = true }
+rayon = { version = "1.5.1", optional = true }
diff --git a/node/src/at_rule_parser.rs b/napi/src/at_rule_parser.rs
similarity index 97%
rename from node/src/at_rule_parser.rs
rename to napi/src/at_rule_parser.rs
index 111bd106c..919eda2dc 100644
--- a/node/src/at_rule_parser.rs
+++ b/napi/src/at_rule_parser.rs
@@ -11,7 +11,6 @@ use lightningcss::{
string::CowArcStr,
syntax::{ParsedComponent, SyntaxString},
},
- visitor::{Visit, VisitTypes, Visitor},
};
use serde::{Deserialize, Deserializer, Serialize};
@@ -198,6 +197,10 @@ impl<'i> ToCss for AtRule<'i> {
}
}
+#[cfg(feature = "visitor")]
+use lightningcss::visitor::{Visit, VisitTypes, Visitor};
+
+#[cfg(feature = "visitor")]
impl<'i, V: Visitor<'i, AtRule<'i>>> Visit<'i, AtRule<'i>, V> for AtRule<'i> {
const CHILD_TYPES: VisitTypes = VisitTypes::empty();
diff --git a/napi/src/lib.rs b/napi/src/lib.rs
new file mode 100644
index 000000000..f43a8d46e
--- /dev/null
+++ b/napi/src/lib.rs
@@ -0,0 +1,1211 @@
+#[cfg(feature = "bundler")]
+use at_rule_parser::AtRule;
+use at_rule_parser::{CustomAtRuleConfig, CustomAtRuleParser};
+use lightningcss::bundler::BundleErrorKind;
+#[cfg(feature = "bundler")]
+use lightningcss::bundler::{Bundler, SourceProvider};
+use lightningcss::css_modules::{CssModuleExports, CssModuleReferences, PatternParseError};
+use lightningcss::dependencies::{Dependency, DependencyOptions};
+use lightningcss::error::{Error, ErrorLocation, MinifyErrorKind, ParserError, PrinterErrorKind};
+use lightningcss::stylesheet::{
+ MinifyOptions, ParserFlags, ParserOptions, PrinterOptions, PseudoClasses, StyleAttribute, StyleSheet,
+};
+use lightningcss::targets::{Browsers, Features, Targets};
+use napi::bindgen_prelude::{FromNapiValue, ToNapiValue};
+use napi::{CallContext, Env, JsObject, JsUnknown};
+use parcel_sourcemap::SourceMap;
+use serde::{Deserialize, Serialize};
+use std::collections::{HashMap, HashSet};
+use std::sync::{Arc, RwLock};
+
+mod at_rule_parser;
+#[cfg(feature = "bundler")]
+#[cfg(not(target_arch = "wasm32"))]
+mod threadsafe_function;
+#[cfg(feature = "visitor")]
+mod transformer;
+mod utils;
+
+#[cfg(feature = "visitor")]
+use transformer::JsVisitor;
+
+#[cfg(not(feature = "visitor"))]
+struct JsVisitor;
+
+#[cfg(feature = "visitor")]
+use lightningcss::visitor::Visit;
+
+use utils::get_named_property;
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+struct TransformResult<'i> {
+ #[serde(with = "serde_bytes")]
+ code: Vec,
+ #[serde(with = "serde_bytes")]
+ map: Option>,
+ exports: Option,
+ references: Option,
+ dependencies: Option>,
+ warnings: Vec<'i>>,
+}
+
+impl<'i> TransformResult<'i> {
+ fn into_js(self, env: Env) -> napi::Result {
+ // Manually construct buffers so we avoid a copy and work around
+ // https://github.com/napi-rs/napi-rs/issues/1124.
+ let mut obj = env.create_object()?;
+ let buf = env.create_buffer_with_data(self.code)?;
+ obj.set_named_property("code", buf.into_raw())?;
+ obj.set_named_property(
+ "map",
+ if let Some(map) = self.map {
+ let buf = env.create_buffer_with_data(map)?;
+ buf.into_raw().into_unknown()
+ } else {
+ env.get_null()?.into_unknown()
+ },
+ )?;
+ obj.set_named_property("exports", env.to_js_value(&self.exports)?)?;
+ obj.set_named_property("references", env.to_js_value(&self.references)?)?;
+ obj.set_named_property("dependencies", env.to_js_value(&self.dependencies)?)?;
+ obj.set_named_property("warnings", env.to_js_value(&self.warnings)?)?;
+ Ok(obj.into_unknown())
+ }
+}
+
+#[cfg(feature = "visitor")]
+fn get_visitor(env: Env, opts: &JsObject) -> Option {
+ if let Ok(visitor) = get_named_property::(opts, "visitor") {
+ Some(JsVisitor::new(env, visitor))
+ } else {
+ None
+ }
+}
+
+#[cfg(not(feature = "visitor"))]
+fn get_visitor(_env: Env, _opts: &JsObject) -> Option {
+ None
+}
+
+pub fn transform(ctx: CallContext) -> napi::Result {
+ let opts = ctx.get::(0)?;
+ let mut visitor = get_visitor(*ctx.env, &opts);
+
+ let config: Config = ctx.env.from_js_value(opts)?;
+ let code = unsafe { std::str::from_utf8_unchecked(&config.code) };
+ let res = compile(code, &config, &mut visitor);
+
+ match res {
+ Ok(res) => res.into_js(*ctx.env),
+ Err(err) => Err(err.into_js_error(*ctx.env, Some(code))?),
+ }
+}
+
+pub fn transform_style_attribute(ctx: CallContext) -> napi::Result {
+ let opts = ctx.get::(0)?;
+ let mut visitor = get_visitor(*ctx.env, &opts);
+
+ let config: AttrConfig = ctx.env.from_js_value(opts)?;
+ let code = unsafe { std::str::from_utf8_unchecked(&config.code) };
+ let res = compile_attr(code, &config, &mut visitor);
+
+ match res {
+ Ok(res) => res.into_js(ctx),
+ Err(err) => Err(err.into_js_error(*ctx.env, Some(code))?),
+ }
+}
+
+#[cfg(feature = "bundler")]
+#[cfg(not(target_arch = "wasm32"))]
+mod bundle {
+ use super::*;
+ use crossbeam_channel::{self, Receiver, Sender};
+ use lightningcss::bundler::{FileProvider, ResolveResult};
+ use napi::{Env, JsBoolean, JsFunction, JsString, NapiRaw};
+ use std::path::{Path, PathBuf};
+ use std::str::FromStr;
+ use std::sync::Mutex;
+ use threadsafe_function::{ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode};
+
+ pub fn bundle(ctx: CallContext) -> napi::Result {
+ let opts = ctx.get::(0)?;
+ let mut visitor = get_visitor(*ctx.env, &opts);
+
+ let config: BundleConfig = ctx.env.from_js_value(opts)?;
+ let fs = FileProvider::new();
+
+ // This is pretty silly, but works around a rust limitation that you cannot
+ // explicitly annotate lifetime bounds on closures.
+ fn annotate<'i, 'o, F>(f: F) -> F
+ where
+ F: FnOnce(&mut StyleSheet<'i, 'o, AtRule<'i>>) -> napi::Result<()>,
+ {
+ f
+ }
+
+ let res = compile_bundle(
+ &fs,
+ &config,
+ visitor.as_mut().map(|visitor| annotate(|stylesheet| stylesheet.visit(visitor))),
+ );
+
+ match res {
+ Ok(res) => res.into_js(*ctx.env),
+ Err(err) => Err(err.into_js_error(*ctx.env, None)?),
+ }
+ }
+
+ // A SourceProvider which calls JavaScript functions to resolve and read files.
+ struct JsSourceProvider {
+ resolve: Option>,
+ read: Option>,
+ inputs: Mutex<*mut String>>,
+ }
+
+ unsafe impl Sync for JsSourceProvider {}
+ unsafe impl Send for JsSourceProvider {}
+
+ // Allocate a single channel per thread to communicate with the JS thread.
+ thread_local! {
+ static CHANNEL: (Sender>, Receiver>) = crossbeam_channel::unbounded();
+ static RESOLVER_CHANNEL: (Sender>, Receiver>) = crossbeam_channel::unbounded();
+ }
+
+ impl SourceProvider for JsSourceProvider {
+ type Error = napi::Error;
+
+ fn read<'a>(&'a self, file: &Path) -> Result<&'a str, Self::Error> {
+ let source = if let Some(read) = &self.read {
+ CHANNEL.with(|channel| {
+ let message = ReadMessage {
+ file: file.to_str().unwrap().to_owned(),
+ tx: channel.0.clone(),
+ };
+
+ read.call(message, ThreadsafeFunctionCallMode::Blocking);
+ channel.1.recv().unwrap()
+ })
+ } else {
+ Ok(std::fs::read_to_string(file)?)
+ };
+
+ match source {
+ Ok(source) => {
+ // cache the result
+ let ptr = Box::into_raw(Box::new(source));
+ self.inputs.lock().unwrap().push(ptr);
+ // SAFETY: this is safe because the pointer is not dropped
+ // until the JsSourceProvider is, and we never remove from the
+ // list of pointers stored in the vector.
+ Ok(unsafe { &*ptr })
+ }
+ Err(e) => Err(e),
+ }
+ }
+
+ fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
+ if let Some(resolve) = &self.resolve {
+ return RESOLVER_CHANNEL.with(|channel| {
+ let message = ResolveMessage {
+ specifier: specifier.to_owned(),
+ originating_file: originating_file.to_str().unwrap().to_owned(),
+ tx: channel.0.clone(),
+ };
+
+ resolve.call(message, ThreadsafeFunctionCallMode::Blocking);
+ channel.1.recv().unwrap()
+ });
+ }
+
+ Ok(originating_file.with_file_name(specifier).into())
+ }
+ }
+
+ struct ResolveMessage {
+ specifier: String,
+ originating_file: String,
+ tx: Sender>,
+ }
+
+ struct ReadMessage {
+ file: String,
+ tx: Sender>,
+ }
+
+ struct VisitMessage {
+ stylesheet: &'static mut StyleSheet<'static, 'static, AtRule<'static>>,
+ tx: Sender>,
+ }
+
+ fn await_promise(env: Env, result: JsUnknown, tx: Sender>, parse: Cb) -> napi::Result<()>
+ where
+ T: 'static,
+ Cb: 'static + Fn(JsUnknown) -> Result,
+ {
+ // If the result is a promise, wait for it to resolve, and send the result to the channel.
+ // Otherwise, send the result immediately.
+ if result.is_promise()? {
+ let result: JsObject = result.try_into()?;
+ let then: JsFunction = get_named_property(&result, "then")?;
+ let tx2 = tx.clone();
+ let cb = env.create_function_from_closure("callback", move |ctx| {
+ let res = parse(ctx.get::(0)?)?;
+ tx.send(Ok(res)).unwrap();
+ ctx.env.get_undefined()
+ })?;
+ let eb = env.create_function_from_closure("error_callback", move |ctx| {
+ let res = ctx.get::(0)?;
+ tx2.send(Err(napi::Error::from(res))).unwrap();
+ ctx.env.get_undefined()
+ })?;
+ then.call(Some(&result), &[cb, eb])?;
+ } else {
+ let result = parse(result)?;
+ tx.send(Ok(result)).unwrap();
+ }
+
+ Ok(())
+ }
+
+ fn resolve_on_js_thread(ctx: ThreadSafeCallContext) -> napi::Result<()> {
+ let specifier = ctx.env.create_string(&ctx.value.specifier)?;
+ let originating_file = ctx.env.create_string(&ctx.value.originating_file)?;
+ let result = ctx.callback.unwrap().call(None, &[specifier, originating_file])?;
+ await_promise(ctx.env, result, ctx.value.tx, move |unknown| {
+ ctx.env.from_js_value(unknown)
+ })
+ }
+
+ fn handle_error(tx: Sender>, res: napi::Result<()>) -> napi::Result<()> {
+ match res {
+ Ok(_) => Ok(()),
+ Err(e) => {
+ tx.send(Err(e)).expect("send error");
+ Ok(())
+ }
+ }
+ }
+
+ fn resolve_on_js_thread_wrapper(ctx: ThreadSafeCallContext) -> napi::Result<()> {
+ let tx = ctx.value.tx.clone();
+ handle_error(tx, resolve_on_js_thread(ctx))
+ }
+
+ fn read_on_js_thread(ctx: ThreadSafeCallContext) -> napi::Result<()> {
+ let file = ctx.env.create_string(&ctx.value.file)?;
+ let result = ctx.callback.unwrap().call(None, &[file])?;
+ await_promise(ctx.env, result, ctx.value.tx, |unknown| {
+ JsString::try_from(unknown)?.into_utf8()?.into_owned()
+ })
+ }
+
+ fn read_on_js_thread_wrapper(ctx: ThreadSafeCallContext) -> napi::Result<()> {
+ let tx = ctx.value.tx.clone();
+ handle_error(tx, read_on_js_thread(ctx))
+ }
+
+ pub fn bundle_async(ctx: CallContext) -> napi::Result {
+ let opts = ctx.get::(0)?;
+ let visitor = get_visitor(*ctx.env, &opts);
+
+ let config: BundleConfig = ctx.env.from_js_value(&opts)?;
+
+ if let Ok(resolver) = get_named_property::(&opts, "resolver") {
+ let read = if resolver.has_named_property("read")? {
+ let read = get_named_property::(&resolver, "read")?;
+ Some(ThreadsafeFunction::create(
+ ctx.env.raw(),
+ unsafe { read.raw() },
+ 0,
+ read_on_js_thread_wrapper,
+ )?)
+ } else {
+ None
+ };
+
+ let resolve = if resolver.has_named_property("resolve")? {
+ let resolve = get_named_property::(&resolver, "resolve")?;
+ Some(ThreadsafeFunction::create(
+ ctx.env.raw(),
+ unsafe { resolve.raw() },
+ 0,
+ resolve_on_js_thread_wrapper,
+ )?)
+ } else {
+ None
+ };
+
+ let provider = JsSourceProvider {
+ resolve,
+ read,
+ inputs: Mutex::new(Vec::new()),
+ };
+
+ run_bundle_task(provider, config, visitor, *ctx.env)
+ } else {
+ let provider = FileProvider::new();
+ run_bundle_task(provider, config, visitor, *ctx.env)
+ }
+ }
+
+ // Runs bundling on a background thread managed by rayon. This is similar to AsyncTask from napi-rs, however,
+ // because we call back into the JS thread, which might call other tasks in the node threadpool (e.g. fs.readFile),
+ // we may end up deadlocking if the number of rayon threads exceeds node's threadpool size. Therefore, we must
+ // run bundling from a thread not managed by Node.
+ fn run_bundle_task(
+ provider: P,
+ config: BundleConfig,
+ visitor: Option,
+ env: Env,
+ ) -> napi::Result
+ where
+ P::Error: IntoJsError,
+ {
+ let (deferred, promise) = env.create_deferred()?;
+
+ let tsfn = if let Some(mut visitor) = visitor {
+ Some(ThreadsafeFunction::create(
+ env.raw(),
+ std::ptr::null_mut(),
+ 0,
+ move |ctx: ThreadSafeCallContext| {
+ if let Err(err) = ctx.value.stylesheet.visit(&mut visitor) {
+ ctx.value.tx.send(Err(err)).expect("send error");
+ return Ok(());
+ }
+ ctx.value.tx.send(Ok(Default::default())).expect("send error");
+ Ok(())
+ },
+ )?)
+ } else {
+ None
+ };
+
+ // Run bundling task in rayon threadpool.
+ rayon::spawn(move || {
+ let res = compile_bundle(
+ unsafe { std::mem::transmute::<&'_ P, &'static P>(&provider) },
+ &config,
+ tsfn.map(move |tsfn| {
+ move |stylesheet: &mut StyleSheet| {
+ CHANNEL.with(|channel| {
+ let message = VisitMessage {
+ // SAFETY: we immediately lock the thread until we get a response,
+ // so stylesheet cannot be dropped in that time.
+ stylesheet: unsafe {
+ std::mem::transmute::<
+ &'_ mut StyleSheet<'_, '_, AtRule>,
+ &'static mut StyleSheet<'static, 'static, AtRule>,
+ >(stylesheet)
+ },
+ tx: channel.0.clone(),
+ };
+
+ tsfn.call(message, ThreadsafeFunctionCallMode::Blocking);
+ channel.1.recv().expect("recv error").map(|_| ())
+ })
+ }
+ }),
+ );
+
+ deferred.resolve(move |env| match res {
+ Ok(v) => v.into_js(env),
+ Err(err) => Err(err.into_js_error(env, None)?),
+ });
+ });
+
+ Ok(promise)
+ }
+}
+
+#[cfg(feature = "bundler")]
+#[cfg(target_arch = "wasm32")]
+mod bundle {
+ use super::*;
+ use lightningcss::bundler::ResolveResult;
+ use napi::{Env, JsFunction, JsString, NapiRaw, NapiValue, Ref};
+ use std::cell::UnsafeCell;
+ use std::path::Path;
+
+ pub fn bundle(ctx: CallContext) -> napi::Result {
+ let opts = ctx.get::(0)?;
+ let mut visitor = get_visitor(*ctx.env, &opts);
+
+ let resolver = get_named_property::(&opts, "resolver")?;
+ let read = get_named_property::(&resolver, "read")?;
+ let resolve = if resolver.has_named_property("resolve")? {
+ let resolve = get_named_property::(&resolver, "resolve")?;
+ Some(ctx.env.create_reference(resolve)?)
+ } else {
+ None
+ };
+ let config: BundleConfig = ctx.env.from_js_value(opts)?;
+
+ let provider = JsSourceProvider {
+ env: ctx.env.clone(),
+ resolve,
+ read: ctx.env.create_reference(read)?,
+ inputs: UnsafeCell::new(Vec::new()),
+ };
+
+ // This is pretty silly, but works around a rust limitation that you cannot
+ // explicitly annotate lifetime bounds on closures.
+ fn annotate<'i, 'o, F>(f: F) -> F
+ where
+ F: FnOnce(&mut StyleSheet<'i, 'o, AtRule<'i>>) -> napi::Result<()>,
+ {
+ f
+ }
+
+ let res = compile_bundle(
+ &provider,
+ &config,
+ visitor.as_mut().map(|visitor| annotate(|stylesheet| stylesheet.visit(visitor))),
+ );
+
+ match res {
+ Ok(res) => res.into_js(*ctx.env),
+ Err(err) => Err(err.into_js_error(*ctx.env, None)?),
+ }
+ }
+
+ struct JsSourceProvider {
+ env: Env,
+ resolve: Option[<()>>,
+ read: Ref<()>,
+ inputs: UnsafeCell<*mut String>>,
+ }
+
+ impl Drop for JsSourceProvider {
+ fn drop(&mut self) {
+ if let Some(resolve) = &mut self.resolve {
+ drop(resolve.unref(self.env));
+ }
+ drop(self.read.unref(self.env));
+ }
+ }
+
+ unsafe impl Sync for JsSourceProvider {}
+ unsafe impl Send for JsSourceProvider {}
+
+ // This relies on Binaryen's Asyncify transform to allow Rust to call async JS functions from sync code.
+ // See the comments in async.mjs for more details about how this works.
+ extern "C" {
+ fn await_promise_sync(
+ promise: napi::sys::napi_value,
+ result: *mut napi::sys::napi_value,
+ error: *mut napi::sys::napi_value,
+ );
+ }
+
+ fn get_result(env: Env, mut value: JsUnknown) -> napi::Result {
+ if value.is_promise()? {
+ let mut result = std::ptr::null_mut();
+ let mut error = std::ptr::null_mut();
+ unsafe { await_promise_sync(value.raw(), &mut result, &mut error) };
+ if !error.is_null() {
+ let error = unsafe { JsUnknown::from_raw(env.raw(), error)? };
+ return Err(napi::Error::from(error));
+ }
+ if result.is_null() {
+ return Err(napi::Error::new(napi::Status::GenericFailure, "No result".to_string()));
+ }
+
+ value = unsafe { JsUnknown::from_raw(env.raw(), result)? };
+ }
+
+ Ok(value)
+ }
+
+ impl SourceProvider for JsSourceProvider {
+ type Error = napi::Error;
+
+ fn read<'a>(&'a self, file: &Path) -> Result<&'a str, Self::Error> {
+ let read: JsFunction = self.env.get_reference_value_unchecked(&self.read)?;
+ let file = self.env.create_string(file.to_str().unwrap())?;
+ let source: JsUnknown = read.call(None, &[file])?;
+ let source = get_result(self.env, source)?;
+ let source: JsString = source.try_into()?;
+ let source = source.into_utf8()?.into_owned()?;
+
+ // cache the result
+ let ptr = Box::into_raw(Box::new(source));
+ let inputs = unsafe { &mut *self.inputs.get() };
+ inputs.push(ptr);
+ // SAFETY: this is safe because the pointer is not dropped
+ // until the JsSourceProvider is, and we never remove from the
+ // list of pointers stored in the vector.
+ Ok(unsafe { &*ptr })
+ }
+
+ fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
+ if let Some(resolve) = &self.resolve {
+ let resolve: JsFunction = self.env.get_reference_value_unchecked(resolve)?;
+ let specifier = self.env.create_string(specifier)?;
+ let originating_file = self.env.create_string(originating_file.to_str().unwrap())?;
+ let result: JsUnknown = resolve.call(None, &[specifier, originating_file])?;
+ let result = get_result(self.env, result)?;
+ let result = self.env.from_js_value(result)?;
+ Ok(result)
+ } else {
+ Ok(ResolveResult::File(originating_file.with_file_name(specifier)))
+ }
+ }
+ }
+}
+
+#[cfg(feature = "bundler")]
+pub use bundle::*;
+
+// ---------------------------------------------
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct Config {
+ pub filename: Option,
+ pub project_root: Option,
+ #[serde(with = "serde_bytes")]
+ pub code: Vec,
+ pub targets: Option,
+ #[serde(default)]
+ pub include: u32,
+ #[serde(default)]
+ pub exclude: u32,
+ pub minify: Option,
+ pub source_map: Option,
+ pub input_source_map: Option,
+ pub drafts: Option,
+ pub non_standard: Option,
+ pub css_modules: Option,
+ pub analyze_dependencies: Option,
+ pub pseudo_classes: Option,
+ pub unused_symbols: Option>,
+ pub error_recovery: Option,
+ pub custom_at_rules: Option>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(untagged)]
+enum AnalyzeDependenciesOption {
+ Bool(bool),
+ Config(AnalyzeDependenciesConfig),
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct AnalyzeDependenciesConfig {
+ preserve_imports: bool,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(untagged)]
+enum CssModulesOption {
+ Bool(bool),
+ Config(CssModulesConfig),
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct CssModulesConfig {
+ pattern: Option,
+ dashed_idents: Option,
+ animation: Option,
+ container: Option,
+ grid: Option,
+ custom_idents: Option,
+ pure: Option,
+}
+
+#[cfg(feature = "bundler")]
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct BundleConfig {
+ pub filename: String,
+ pub project_root: Option,
+ pub targets: Option,
+ #[serde(default)]
+ pub include: u32,
+ #[serde(default)]
+ pub exclude: u32,
+ pub minify: Option,
+ pub source_map: Option,
+ pub drafts: Option,
+ pub non_standard: Option,
+ pub css_modules: Option,
+ pub analyze_dependencies: Option,
+ pub pseudo_classes: Option,
+ pub unused_symbols: Option>,
+ pub error_recovery: Option,
+ pub custom_at_rules: Option>,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct OwnedPseudoClasses {
+ pub hover: Option,
+ pub active: Option,
+ pub focus: Option,
+ pub focus_visible: Option,
+ pub focus_within: Option,
+}
+
+impl<'a> Into<'a>> for &'a OwnedPseudoClasses {
+ fn into(self) -> PseudoClasses<'a> {
+ PseudoClasses {
+ hover: self.hover.as_deref(),
+ active: self.active.as_deref(),
+ focus: self.focus.as_deref(),
+ focus_visible: self.focus_visible.as_deref(),
+ focus_within: self.focus_within.as_deref(),
+ }
+ }
+}
+
+#[derive(Serialize, Debug, Deserialize, Default)]
+#[serde(rename_all = "camelCase")]
+struct Drafts {
+ #[serde(default)]
+ custom_media: bool,
+}
+
+#[derive(Serialize, Debug, Deserialize, Default)]
+#[serde(rename_all = "camelCase")]
+struct NonStandard {
+ #[serde(default)]
+ deep_selector_combinator: bool,
+}
+
+fn compile<'i>(
+ code: &'i str,
+ config: &Config,
+ #[allow(unused_variables)] visitor: &mut Option,
+) -> Result<'i>, CompileError<'i, napi::Error>> {
+ let drafts = config.drafts.as_ref();
+ let non_standard = config.non_standard.as_ref();
+ let warnings = Some(Arc::new(RwLock::new(Vec::new())));
+
+ let filename = config.filename.clone().unwrap_or_default();
+ let project_root = config.project_root.as_ref().map(|p| p.as_ref());
+ let mut source_map = if config.source_map.unwrap_or_default() {
+ let mut sm = SourceMap::new(project_root.unwrap_or("/"));
+ sm.add_source(&filename);
+ sm.set_source_content(0, code)?;
+ Some(sm)
+ } else {
+ None
+ };
+
+ let res = {
+ let mut flags = ParserFlags::empty();
+ flags.set(ParserFlags::CUSTOM_MEDIA, matches!(drafts, Some(d) if d.custom_media));
+ flags.set(
+ ParserFlags::DEEP_SELECTOR_COMBINATOR,
+ matches!(non_standard, Some(v) if v.deep_selector_combinator),
+ );
+
+ let mut stylesheet = StyleSheet::parse_with(
+ &code,
+ ParserOptions {
+ filename: filename.clone(),
+ flags,
+ css_modules: if let Some(css_modules) = &config.css_modules {
+ match css_modules {
+ CssModulesOption::Bool(true) => Some(lightningcss::css_modules::Config::default()),
+ CssModulesOption::Bool(false) => None,
+ CssModulesOption::Config(c) => Some(lightningcss::css_modules::Config {
+ pattern: if let Some(pattern) = c.pattern.as_ref() {
+ match lightningcss::css_modules::Pattern::parse(pattern) {
+ Ok(p) => p,
+ Err(e) => return Err(CompileError::PatternError(e)),
+ }
+ } else {
+ Default::default()
+ },
+ dashed_idents: c.dashed_idents.unwrap_or_default(),
+ animation: c.animation.unwrap_or(true),
+ container: c.container.unwrap_or(true),
+ grid: c.grid.unwrap_or(true),
+ custom_idents: c.custom_idents.unwrap_or(true),
+ pure: c.pure.unwrap_or_default(),
+ }),
+ }
+ } else {
+ None
+ },
+ source_index: 0,
+ error_recovery: config.error_recovery.unwrap_or_default(),
+ warnings: warnings.clone(),
+ },
+ &mut CustomAtRuleParser {
+ configs: config.custom_at_rules.clone().unwrap_or_default(),
+ },
+ )?;
+
+ #[cfg(feature = "visitor")]
+ if let Some(visitor) = visitor.as_mut() {
+ stylesheet.visit(visitor).map_err(CompileError::JsError)?;
+ }
+
+ let targets = Targets {
+ browsers: config.targets,
+ include: Features::from_bits_truncate(config.include),
+ exclude: Features::from_bits_truncate(config.exclude),
+ };
+
+ stylesheet.minify(MinifyOptions {
+ targets,
+ unused_symbols: config.unused_symbols.clone().unwrap_or_default(),
+ })?;
+
+ stylesheet.to_css(PrinterOptions {
+ minify: config.minify.unwrap_or_default(),
+ source_map: source_map.as_mut(),
+ project_root,
+ targets,
+ analyze_dependencies: if let Some(d) = &config.analyze_dependencies {
+ match d {
+ AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }),
+ AnalyzeDependenciesOption::Config(c) => Some(DependencyOptions {
+ remove_imports: !c.preserve_imports,
+ }),
+ _ => None,
+ }
+ } else {
+ None
+ },
+ pseudo_classes: config.pseudo_classes.as_ref().map(|p| p.into()),
+ })?
+ };
+
+ let map = if let Some(mut source_map) = source_map {
+ if let Some(input_source_map) = &config.input_source_map {
+ if let Ok(mut sm) = SourceMap::from_json("/", input_source_map) {
+ let _ = source_map.extends(&mut sm);
+ }
+ }
+
+ source_map.to_json(None).ok()
+ } else {
+ None
+ };
+
+ Ok(TransformResult {
+ code: res.code.into_bytes(),
+ map: map.map(|m| m.into_bytes()),
+ exports: res.exports,
+ references: res.references,
+ dependencies: res.dependencies,
+ warnings: warnings.map_or(Vec::new(), |w| {
+ Arc::try_unwrap(w)
+ .unwrap()
+ .into_inner()
+ .unwrap()
+ .into_iter()
+ .map(|w| w.into())
+ .collect()
+ }),
+ })
+}
+
+#[cfg(feature = "bundler")]
+fn compile_bundle<
+ 'i,
+ 'o,
+ P: SourceProvider,
+ F: FnOnce(&mut StyleSheet<'i, 'o, AtRule<'i>>) -> napi::Result<()>,
+>(
+ fs: &'i P,
+ config: &'o BundleConfig,
+ visit: Option,
+) -> Result<'i>, CompileError<'i, P::Error>> {
+ use std::path::Path;
+
+ let project_root = config.project_root.as_ref().map(|p| p.as_ref());
+ let mut source_map = if config.source_map.unwrap_or_default() {
+ Some(SourceMap::new(project_root.unwrap_or("/")))
+ } else {
+ None
+ };
+ let warnings = Some(Arc::new(RwLock::new(Vec::new())));
+
+ let res = {
+ let drafts = config.drafts.as_ref();
+ let non_standard = config.non_standard.as_ref();
+ let mut flags = ParserFlags::empty();
+ flags.set(ParserFlags::CUSTOM_MEDIA, matches!(drafts, Some(d) if d.custom_media));
+ flags.set(
+ ParserFlags::DEEP_SELECTOR_COMBINATOR,
+ matches!(non_standard, Some(v) if v.deep_selector_combinator),
+ );
+
+ let parser_options = ParserOptions {
+ flags,
+ css_modules: if let Some(css_modules) = &config.css_modules {
+ match css_modules {
+ CssModulesOption::Bool(true) => Some(lightningcss::css_modules::Config::default()),
+ CssModulesOption::Bool(false) => None,
+ CssModulesOption::Config(c) => Some(lightningcss::css_modules::Config {
+ pattern: if let Some(pattern) = c.pattern.as_ref() {
+ match lightningcss::css_modules::Pattern::parse(pattern) {
+ Ok(p) => p,
+ Err(e) => return Err(CompileError::PatternError(e)),
+ }
+ } else {
+ Default::default()
+ },
+ dashed_idents: c.dashed_idents.unwrap_or_default(),
+ animation: c.animation.unwrap_or(true),
+ container: c.container.unwrap_or(true),
+ grid: c.grid.unwrap_or(true),
+ custom_idents: c.custom_idents.unwrap_or(true),
+ pure: c.pure.unwrap_or_default(),
+ }),
+ }
+ } else {
+ None
+ },
+ error_recovery: config.error_recovery.unwrap_or_default(),
+ warnings: warnings.clone(),
+ filename: String::new(),
+ source_index: 0,
+ };
+
+ let mut at_rule_parser = CustomAtRuleParser {
+ configs: config.custom_at_rules.clone().unwrap_or_default(),
+ };
+
+ let mut bundler =
+ Bundler::new_with_at_rule_parser(fs, source_map.as_mut(), parser_options, &mut at_rule_parser);
+ let mut stylesheet = bundler.bundle(Path::new(&config.filename))?;
+
+ if let Some(visit) = visit {
+ visit(&mut stylesheet).map_err(CompileError::JsError)?;
+ }
+
+ let targets = Targets {
+ browsers: config.targets,
+ include: Features::from_bits_truncate(config.include),
+ exclude: Features::from_bits_truncate(config.exclude),
+ };
+
+ stylesheet.minify(MinifyOptions {
+ targets,
+ unused_symbols: config.unused_symbols.clone().unwrap_or_default(),
+ })?;
+
+ stylesheet.to_css(PrinterOptions {
+ minify: config.minify.unwrap_or_default(),
+ source_map: source_map.as_mut(),
+ project_root,
+ targets,
+ analyze_dependencies: if let Some(d) = &config.analyze_dependencies {
+ match d {
+ AnalyzeDependenciesOption::Bool(b) if *b => Some(DependencyOptions { remove_imports: true }),
+ AnalyzeDependenciesOption::Config(c) => Some(DependencyOptions {
+ remove_imports: !c.preserve_imports,
+ }),
+ _ => None,
+ }
+ } else {
+ None
+ },
+ pseudo_classes: config.pseudo_classes.as_ref().map(|p| p.into()),
+ })?
+ };
+
+ let map = if let Some(source_map) = &mut source_map {
+ source_map.to_json(None).ok()
+ } else {
+ None
+ };
+
+ Ok(TransformResult {
+ code: res.code.into_bytes(),
+ map: map.map(|m| m.into_bytes()),
+ exports: res.exports,
+ references: res.references,
+ dependencies: res.dependencies,
+ warnings: warnings.map_or(Vec::new(), |w| {
+ Arc::try_unwrap(w)
+ .unwrap()
+ .into_inner()
+ .unwrap()
+ .into_iter()
+ .map(|w| w.into())
+ .collect()
+ }),
+ })
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct AttrConfig {
+ pub filename: Option,
+ #[serde(with = "serde_bytes")]
+ pub code: Vec,
+ pub targets: Option,
+ #[serde(default)]
+ pub include: u32,
+ #[serde(default)]
+ pub exclude: u32,
+ #[serde(default)]
+ pub minify: bool,
+ #[serde(default)]
+ pub analyze_dependencies: bool,
+ #[serde(default)]
+ pub error_recovery: bool,
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+struct AttrResult<'i> {
+ #[serde(with = "serde_bytes")]
+ code: Vec,
+ dependencies: Option>,
+ warnings: Vec<'i>>,
+}
+
+impl<'i> AttrResult<'i> {
+ fn into_js(self, ctx: CallContext) -> napi::Result {
+ // Manually construct buffers so we avoid a copy and work around
+ // https://github.com/napi-rs/napi-rs/issues/1124.
+ let mut obj = ctx.env.create_object()?;
+ let buf = ctx.env.create_buffer_with_data(self.code)?;
+ obj.set_named_property("code", buf.into_raw())?;
+ obj.set_named_property("dependencies", ctx.env.to_js_value(&self.dependencies)?)?;
+ obj.set_named_property("warnings", ctx.env.to_js_value(&self.warnings)?)?;
+ Ok(obj.into_unknown())
+ }
+}
+
+fn compile_attr<'i>(
+ code: &'i str,
+ config: &AttrConfig,
+ #[allow(unused_variables)] visitor: &mut Option,
+) -> Result<'i>, CompileError<'i, napi::Error>> {
+ let warnings = if config.error_recovery {
+ Some(Arc::new(RwLock::new(Vec::new())))
+ } else {
+ None
+ };
+ let res = {
+ let filename = config.filename.clone().unwrap_or_default();
+ let mut attr = StyleAttribute::parse(
+ &code,
+ ParserOptions {
+ filename,
+ error_recovery: config.error_recovery,
+ warnings: warnings.clone(),
+ ..ParserOptions::default()
+ },
+ )?;
+
+ #[cfg(feature = "visitor")]
+ if let Some(visitor) = visitor.as_mut() {
+ attr.visit(visitor).unwrap();
+ }
+
+ let targets = Targets {
+ browsers: config.targets,
+ include: Features::from_bits_truncate(config.include),
+ exclude: Features::from_bits_truncate(config.exclude),
+ };
+
+ attr.minify(MinifyOptions {
+ targets,
+ ..MinifyOptions::default()
+ });
+ attr.to_css(PrinterOptions {
+ minify: config.minify,
+ source_map: None,
+ project_root: None,
+ targets,
+ analyze_dependencies: if config.analyze_dependencies {
+ Some(DependencyOptions::default())
+ } else {
+ None
+ },
+ pseudo_classes: None,
+ })?
+ };
+ Ok(AttrResult {
+ code: res.code.into_bytes(),
+ dependencies: res.dependencies,
+ warnings: warnings.map_or(Vec::new(), |w| {
+ Arc::try_unwrap(w)
+ .unwrap()
+ .into_inner()
+ .unwrap()
+ .into_iter()
+ .map(|w| w.into())
+ .collect()
+ }),
+ })
+}
+
+enum CompileError<'i, E: std::error::Error> {
+ ParseError(Error<'i>>),
+ MinifyError(Error),
+ PrinterError(Error),
+ SourceMapError(parcel_sourcemap::SourceMapError),
+ BundleError(Error<'i, E>>),
+ PatternError(PatternParseError),
+ #[cfg(feature = "visitor")]
+ JsError(napi::Error),
+}
+
+impl<'i, E: std::error::Error> std::fmt::Display for CompileError<'i, E> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ CompileError::ParseError(err) => err.kind.fmt(f),
+ CompileError::MinifyError(err) => err.kind.fmt(f),
+ CompileError::PrinterError(err) => err.kind.fmt(f),
+ CompileError::BundleError(err) => err.kind.fmt(f),
+ CompileError::PatternError(err) => err.fmt(f),
+ CompileError::SourceMapError(err) => write!(f, "{}", err.to_string()), // TODO: switch to `fmt::Display` once parcel_sourcemap supports this
+ #[cfg(feature = "visitor")]
+ CompileError::JsError(err) => std::fmt::Debug::fmt(&err, f),
+ }
+ }
+}
+
+impl<'i, E: IntoJsError + std::error::Error> CompileError<'i, E> {
+ fn into_js_error(self, env: Env, code: Option<&str>) -> napi::Result {
+ let reason = self.to_string();
+ let data = match &self {
+ CompileError::ParseError(Error { kind, .. }) => env.to_js_value(kind)?,
+ CompileError::PrinterError(Error { kind, .. }) => env.to_js_value(kind)?,
+ CompileError::MinifyError(Error { kind, .. }) => env.to_js_value(kind)?,
+ CompileError::BundleError(Error { kind, .. }) => env.to_js_value(kind)?,
+ _ => env.get_null()?.into_unknown(),
+ };
+
+ let (js_error, loc) = match self {
+ CompileError::BundleError(Error {
+ loc,
+ kind: BundleErrorKind::ResolverError(e),
+ }) => {
+ // Add location info to existing JS error if available.
+ (e.into_js_error(env)?, loc)
+ }
+ CompileError::ParseError(Error { loc, .. })
+ | CompileError::PrinterError(Error { loc, .. })
+ | CompileError::MinifyError(Error { loc, .. })
+ | CompileError::BundleError(Error { loc, .. }) => {
+ // Generate an error with location information.
+ let syntax_error = env.get_global()?.get_named_property::("SyntaxError")?;
+ let reason = env.create_string_from_std(reason)?;
+ let obj = syntax_error.new_instance(&[reason])?;
+ (obj.into_unknown(), loc)
+ }
+ _ => return Ok(self.into()),
+ };
+
+ if js_error.get_type()? == napi::ValueType::Object {
+ let mut obj: JsObject = unsafe { js_error.cast() };
+ if let Some(loc) = loc {
+ let line = env.create_int32((loc.line + 1) as i32)?;
+ let col = env.create_int32(loc.column as i32)?;
+ let filename = env.create_string_from_std(loc.filename)?;
+ obj.set_named_property("fileName", filename)?;
+ if let Some(code) = code {
+ let source = env.create_string(code)?;
+ obj.set_named_property("source", source)?;
+ }
+ let mut loc = env.create_object()?;
+ loc.set_named_property("line", line)?;
+ loc.set_named_property("column", col)?;
+ obj.set_named_property("loc", loc)?;
+ }
+ obj.set_named_property("data", data)?;
+ Ok(obj.into_unknown().into())
+ } else {
+ Ok(js_error.into())
+ }
+ }
+}
+
+trait IntoJsError {
+ fn into_js_error(self, env: Env) -> napi::Result;
+}
+
+impl IntoJsError for std::io::Error {
+ fn into_js_error(self, env: Env) -> napi::Result {
+ let reason = self.to_string();
+ let syntax_error = env.get_global()?.get_named_property::("SyntaxError")?;
+ let reason = env.create_string_from_std(reason)?;
+ let obj = syntax_error.new_instance(&[reason])?;
+ Ok(obj.into_unknown())
+ }
+}
+
+impl IntoJsError for napi::Error {
+ fn into_js_error(self, env: Env) -> napi::Result {
+ unsafe { JsUnknown::from_napi_value(env.raw(), ToNapiValue::to_napi_value(env.raw(), self)?) }
+ }
+}
+
+impl<'i, E: std::error::Error> From<'i>>> for CompileError<'i, E> {
+ fn from(e: Error<'i>>) -> CompileError<'i, E> {
+ CompileError::ParseError(e)
+ }
+}
+
+impl<'i, E: std::error::Error> From> for CompileError<'i, E> {
+ fn from(err: Error) -> CompileError<'i, E> {
+ CompileError::MinifyError(err)
+ }
+}
+
+impl<'i, E: std::error::Error> From> for CompileError<'i, E> {
+ fn from(err: Error) -> CompileError<'i, E> {
+ CompileError::PrinterError(err)
+ }
+}
+
+impl<'i, E: std::error::Error> From for CompileError<'i, E> {
+ fn from(e: parcel_sourcemap::SourceMapError) -> CompileError<'i, E> {
+ CompileError::SourceMapError(e)
+ }
+}
+
+impl<'i, E: std::error::Error> From<'i, E>>> for CompileError<'i, E> {
+ fn from(e: Error<'i, E>>) -> CompileError<'i, E> {
+ CompileError::BundleError(e)
+ }
+}
+
+impl<'i, E: std::error::Error> From<'i, E>> for napi::Error {
+ fn from(e: CompileError<'i, E>) -> napi::Error {
+ match e {
+ CompileError::SourceMapError(e) => napi::Error::from_reason(e.to_string()),
+ CompileError::PatternError(e) => napi::Error::from_reason(e.to_string()),
+ #[cfg(feature = "visitor")]
+ CompileError::JsError(e) => e,
+ _ => napi::Error::new(napi::Status::GenericFailure, e.to_string()),
+ }
+ }
+}
+
+#[derive(Serialize)]
+struct Warning<'i> {
+ message: String,
+ #[serde(flatten)]
+ data: ParserError<'i>,
+ loc: Option,
+}
+
+impl<'i> From<'i>>> for Warning<'i> {
+ fn from(mut e: Error<'i>>) -> Self {
+ // Convert to 1-based line numbers.
+ if let Some(loc) = &mut e.loc {
+ loc.line += 1;
+ }
+ Warning {
+ message: e.kind.to_string(),
+ data: e.kind,
+ loc: e.loc,
+ }
+ }
+}
diff --git a/node/src/threadsafe_function.rs b/napi/src/threadsafe_function.rs
similarity index 100%
rename from node/src/threadsafe_function.rs
rename to napi/src/threadsafe_function.rs
diff --git a/node/src/transformer.rs b/napi/src/transformer.rs
similarity index 90%
rename from node/src/transformer.rs
rename to napi/src/transformer.rs
index 6873b5029..bab931f2d 100644
--- a/node/src/transformer.rs
+++ b/napi/src/transformer.rs
@@ -3,7 +3,6 @@ use std::{
ops::{Index, IndexMut},
};
-use lightningcss::traits::IntoOwned;
use lightningcss::{
media_query::MediaFeatureValue,
properties::{
@@ -20,14 +19,16 @@ use lightningcss::{
},
visitor::{Visit, VisitTypes, Visitor},
};
+use lightningcss::{stylesheet::StyleSheet, traits::IntoOwned};
use napi::{Env, JsFunction, JsObject, JsUnknown, Ref, ValueType};
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
-use crate::at_rule_parser::AtRule;
+use crate::{at_rule_parser::AtRule, utils::get_named_property};
pub struct JsVisitor {
env: Env,
+ visit_stylesheet: VisitorsRef,
visit_rule: VisitorsRef,
rule_map: VisitorsRef,
property_map: VisitorsRef,
@@ -98,7 +99,7 @@ impl Visitors {
fn named(&self, stage: VisitStage, name: &str) -> Option {
self
.for_stage(stage)
- .and_then(|m| m.get_named_property::(name).ok())
+ .and_then(|m| get_named_property::(m, name).ok())
}
fn custom(&self, stage: VisitStage, obj: &str, name: &str) -> Option {
@@ -111,7 +112,7 @@ impl Visitors {
Ok(ValueType::Object) => {
let o: napi::Result = v.try_into();
if let Ok(o) = o {
- return o.get_named_property::(name).ok();
+ return get_named_property::(&o, name).ok();
}
}
_ => {}
@@ -143,6 +144,7 @@ impl Drop for JsVisitor {
};
}
+ drop_tuple!(visit_stylesheet);
drop_tuple!(visit_rule);
drop_tuple!(rule_map);
drop_tuple!(visit_declaration);
@@ -175,7 +177,8 @@ impl JsVisitor {
let mut types = VisitTypes::empty();
macro_rules! get {
($name: literal, $( $t: ident )|+) => {{
- let res: Option = visitor.get_named_property($name).ok();
+ let res: Option = get_named_property(&visitor, $name).ok();
+
if res.is_some() {
types |= $( VisitTypes::$t )|+;
}
@@ -188,17 +191,19 @@ impl JsVisitor {
macro_rules! map {
($name: literal, $( $t: ident )|+) => {{
- if let Ok(obj) = visitor.get_named_property::($name) {
+ let obj: Option = get_named_property(&visitor, $name).ok();
+
+ if obj.is_some() {
types |= $( VisitTypes::$t )|+;
- env.create_reference(obj).ok()
- } else {
- None
}
+
+ obj.and_then(|obj| env.create_reference(obj).ok())
}};
}
Self {
env,
+ visit_stylesheet: VisitorsRef::new(get!("StyleSheet", RULES), get!("StyleSheetExit", RULES)),
visit_rule: VisitorsRef::new(get!("Rule", RULES), get!("RuleExit", RULES)),
rule_map: VisitorsRef::new(map!("Rule", RULES), get!("RuleExit", RULES)),
visit_declaration: VisitorsRef::new(get!("Declaration", PROPERTIES), get!("DeclarationExit", PROPERTIES)),
@@ -253,6 +258,26 @@ impl<'i> Visitor<'i, AtRule<'i>> for JsVisitor {
self.types
}
+ fn visit_stylesheet<'o>(&mut self, stylesheet: &mut StyleSheet<'i, 'o, AtRule<'i>>) -> Result<(), Self::Error> {
+ if self.types.contains(VisitTypes::RULES) {
+ let env = self.env;
+ let visit_stylesheet = self.visit_stylesheet.get::(&env);
+ if let Some(visit) = visit_stylesheet.for_stage(VisitStage::Enter) {
+ call_visitor(&env, stylesheet, visit)?
+ }
+
+ stylesheet.visit_children(self)?;
+
+ if let Some(visit) = visit_stylesheet.for_stage(VisitStage::Exit) {
+ call_visitor(&env, stylesheet, visit)?
+ }
+
+ Ok(())
+ } else {
+ stylesheet.visit_children(self)
+ }
+ }
+
fn visit_rule_list(
&mut self,
rules: &mut lightningcss::rules::CssRuleList<'i, AtRule<'i>>,
@@ -273,6 +298,7 @@ impl<'i> Visitor<'i, AtRule<'i>> for JsVisitor {
CssRule::Keyframes(..) => "keyframes",
CssRule::FontFace(..) => "font-face",
CssRule::FontPaletteValues(..) => "font-palette-values",
+ CssRule::FontFeatureValues(..) => "font-feature-values",
CssRule::Page(..) => "page",
CssRule::Supports(..) => "supports",
CssRule::CounterStyle(..) => "counter-style",
@@ -285,8 +311,10 @@ impl<'i> Visitor<'i, AtRule<'i>> for JsVisitor {
CssRule::Scope(..) => "scope",
CssRule::MozDocument(..) => "moz-document",
CssRule::Nesting(..) => "nesting",
+ CssRule::NestedDeclarations(..) => "nested-declarations",
CssRule::Viewport(..) => "viewport",
CssRule::StartingStyle(..) => "starting-style",
+ CssRule::ViewTransition(..) => "view-transition",
CssRule::Unknown(v) => {
let name = v.name.as_ref();
if let Some(visit) = rule_map.custom(stage, "unknown", name) {
@@ -585,13 +613,23 @@ fn visit<'static>>(
.as_ref()
.and_then(|v| env.get_reference_value_unchecked::(v).ok())
{
- let js_value = env.to_js_value(value)?;
- let res = visit.call(None, &[js_value])?;
- let new_value: Option = env.from_js_value(res).map(serde_detach::detach)?;
- match new_value {
- Some(new_value) => *value = new_value,
- None => {}
- }
+ call_visitor(env, value, &visit)?;
+ }
+
+ Ok(())
+}
+
+fn call_visitor<'static>>(
+ env: &Env,
+ value: &mut V,
+ visit: &JsFunction,
+) -> napi::Result<()> {
+ let js_value = env.to_js_value(value)?;
+ let res = visit.call(None, &[js_value])?;
+ let new_value: Option = env.from_js_value(res).map(serde_detach::detach)?;
+ match new_value {
+ Some(new_value) => *value = new_value,
+ None => {}
}
Ok(())
@@ -717,9 +755,8 @@ impl<'de, V: serde::Deserialize<'de>, const IS_VEC: bool> serde::Deserialize<'de
D: serde::Deserializer<'de>,
{
use serde::Deserializer;
- let content = serde::__private::de::Content::deserialize(deserializer)?;
- let de: serde::__private::de::ContentRefDeserializer =
- serde::__private::de::ContentRefDeserializer::new(&content);
+ let content = serde_content::Value::deserialize(deserializer)?;
+ let de = serde_content::Deserializer::new(content.clone()).coerce_numbers();
// Try to deserialize as a sequence first.
let mut was_seq = false;
@@ -731,13 +768,15 @@ impl<'de, V: serde::Deserialize<'de>, const IS_VEC: bool> serde::Deserialize<'de
if was_seq {
// Allow fallback if we know the value is also a list (e.g. selector).
if res.is_ok() || !IS_VEC {
- return res.map(ValueOrVec::Vec);
+ return res.map_err(|e| serde::de::Error::custom(e.to_string())).map(ValueOrVec::Vec);
}
}
// If it wasn't a sequence, try a value.
- let de = serde::__private::de::ContentRefDeserializer::new(&content);
- return V::deserialize(de).map(ValueOrVec::Value);
+ let de = serde_content::Deserializer::new(content).coerce_numbers();
+ return V::deserialize(de)
+ .map_err(|e| serde::de::Error::custom(e.to_string()))
+ .map(ValueOrVec::Value);
struct SeqVisitor<'a, V> {
was_seq: &'a mut bool,
@@ -773,16 +812,14 @@ impl<'i, 'de: 'i> serde::Deserialize<'de> for TokensOrRaw<'i> {
where
D: serde::Deserializer<'de>,
{
- use serde::__private::de::ContentRefDeserializer;
-
#[derive(serde::Deserialize)]
struct Raw<'i> {
#[serde(borrow)]
raw: CowArcStr<'i>,
}
- let content = serde::__private::de::Content::deserialize(deserializer)?;
- let de: ContentRefDeserializer = ContentRefDeserializer::new(&content);
+ let content = serde_content::Value::deserialize(deserializer)?;
+ let de = serde_content::Deserializer::new(content.clone()).coerce_numbers();
if let Ok(res) = Raw::deserialize(de) {
let res = TokenList::parse_string_with_options(res.raw.as_ref(), ParserOptions::default())
@@ -790,8 +827,10 @@ impl<'i, 'de: 'i> serde::Deserialize<'de> for TokensOrRaw<'i> {
return Ok(TokensOrRaw(ValueOrVec::Vec(res.into_owned().0)));
}
- let de = ContentRefDeserializer::new(&content);
- Ok(TokensOrRaw(ValueOrVec::deserialize(de)?))
+ let de = serde_content::Deserializer::new(content).coerce_numbers();
+ Ok(TokensOrRaw(
+ ValueOrVec::deserialize(de).map_err(|e| serde::de::Error::custom(e.to_string()))?,
+ ))
}
}
diff --git a/napi/src/utils.rs b/napi/src/utils.rs
new file mode 100644
index 000000000..90ac14957
--- /dev/null
+++ b/napi/src/utils.rs
@@ -0,0 +1,7 @@
+use napi::{Error, JsObject, JsUnknown, Result};
+
+// Workaround for https://github.com/napi-rs/napi-rs/issues/1641
+pub fn get_named_property>(obj: &JsObject, property: &str) -> Result {
+ let unknown = obj.get_named_property::(property)?;
+ T::try_from(unknown)
+}
diff --git a/node/Cargo.toml b/node/Cargo.toml
index 5b26eea75..b5c7505c3 100644
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -9,17 +9,14 @@ publish = false
crate-type = ["cdylib"]
[dependencies]
-serde = { version = "1.0.123", features = ["derive"] }
-serde_bytes = "0.11.5"
-cssparser = "0.33.0"
-lightningcss = { path = "../", features = ["nodejs", "serde", "visitor"] }
-parcel_sourcemap = { version = "2.1.1", features = ["json"] }
-serde-detach = "0.0.1"
-smallvec = { version = "1.7.0", features = ["union"] }
-napi = {version = "=2.10.3", default-features = false, features = ["napi4", "napi5", "compat-mode", "serde-json"]}
+lightningcss-napi = { version = "0.4.7", path = "../napi", features = [
+ "bundler",
+ "visitor",
+] }
+napi = { version = "2.15.4", default-features = false, features = [
+ "compat-mode",
+] }
napi-derive = "2"
-crossbeam-channel = "0.5.6"
-rayon = "1.5.1"
[target.'cfg(target_os = "macos")'.dependencies]
jemallocator = { version = "0.3.2", features = ["disable_initial_exec_tls"] }
diff --git a/node/ast.d.ts b/node/ast.d.ts
index 567a45f5a..28e9d0930 100644
--- a/node/ast.d.ts
+++ b/node/ast.d.ts
@@ -1,4 +1,4 @@
-/* tslint:disable */
+/* eslint-disable */
/**
* This file was automatically generated by json-schema-to-typescript.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
@@ -33,6 +33,10 @@ export type Rule = | {
type: "font-palette-values";
value: FontPaletteValuesRule;
}
+| {
+ type: "font-feature-values";
+ value: FontFeatureValuesRule;
+ }
| {
type: "page";
value: PageRule;
@@ -57,6 +61,10 @@ export type Rule = | {
type: "nesting";
value: NestingRule;
}
+| {
+ type: "nested-declarations";
+ value: NestedDeclarationsRule;
+ }
| {
type: "viewport";
value: ViewportRule;
@@ -89,6 +97,10 @@ export type Rule = | {
type: "starting-style";
value: StartingStyleRule;
}
+| {
+ type: "view-transition";
+ value: ViewTransitionRule;
+ }
| {
type: "ignored";
}
@@ -122,6 +134,10 @@ export type MediaCondition =
*/
operator: Operator;
type: "operation";
+ }
+ | {
+ type: "unknown";
+ value: TokenOrValue[];
};
/**
* A generic media feature or container feature.
@@ -504,6 +520,10 @@ export type TokenOrValue =
| {
type: "dashed-ident";
value: String;
+ }
+ | {
+ type: "animation-name";
+ value: AnimationName;
};
/**
* A raw CSS token.
@@ -637,7 +657,7 @@ export type Token =
*
* Each color space is represented as a struct that implements the `From` and `Into` traits for all other color spaces, so it is possible to convert between color spaces easily. In addition, colors support [interpolation](#method.interpolate) as in the `color-mix()` function.
*/
-export type CssColor = CurrentColor | RGBColor | LABColor | PredefinedColor | FloatColor;
+export type CssColor = CurrentColor | RGBColor | LABColor | PredefinedColor | FloatColor | LightDark | SystemColor;
export type CurrentColor = {
type: "currentcolor";
};
@@ -957,6 +977,57 @@ export type FloatColor =
*/
w: number;
};
+export type LightDark = {
+ dark: CssColor;
+ light: CssColor;
+ type: "light-dark";
+};
+/**
+ * A CSS [system color](https://drafts.csswg.org/css-color/#css-system-colors) keyword.
+ */
+export type SystemColor =
+ | "accentcolor"
+ | "accentcolortext"
+ | "activetext"
+ | "buttonborder"
+ | "buttonface"
+ | "buttontext"
+ | "canvas"
+ | "canvastext"
+ | "field"
+ | "fieldtext"
+ | "graytext"
+ | "highlight"
+ | "highlighttext"
+ | "linktext"
+ | "mark"
+ | "marktext"
+ | "selecteditem"
+ | "selecteditemtext"
+ | "visitedtext"
+ | "activeborder"
+ | "activecaption"
+ | "appworkspace"
+ | "background"
+ | "buttonhighlight"
+ | "buttonshadow"
+ | "captiontext"
+ | "inactiveborder"
+ | "inactivecaption"
+ | "inactivecaptiontext"
+ | "infobackground"
+ | "infotext"
+ | "menu"
+ | "menutext"
+ | "scrollbar"
+ | "threeddarkshadow"
+ | "threedface"
+ | "threedhighlight"
+ | "threedlightshadow"
+ | "threedshadow"
+ | "window"
+ | "windowframe"
+ | "windowtext";
/**
* A color value with an unresolved alpha value (e.g. a variable). These can be converted from the modern slash syntax to older comma syntax. This can only be done when the only unresolved component is the alpha since variables can resolve to multiple tokens.
*/
@@ -998,6 +1069,17 @@ export type UnresolvedColor =
*/
s: number;
type: "hsl";
+ }
+ | {
+ /**
+ * The dark value.
+ */
+ dark: TokenOrValue[];
+ /**
+ * The light value.
+ */
+ light: TokenOrValue[];
+ type: "light-dark";
};
/**
* Defines where the class names referenced in the `composes` property are located.
@@ -1052,6 +1134,21 @@ export type Time =
type: "milliseconds";
value: number;
};
+/**
+ * A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property.
+ */
+export type AnimationName =
+ | {
+ type: "none";
+ }
+ | {
+ type: "ident";
+ value: String;
+ }
+ | {
+ type: "string";
+ value: String;
+ };
/**
* A CSS environment variable name.
*/
@@ -1873,6 +1970,21 @@ export type PropertyId =
property: "animation-fill-mode";
vendorPrefix: VendorPrefix;
}
+ | {
+ property: "animation-composition";
+ }
+ | {
+ property: "animation-timeline";
+ }
+ | {
+ property: "animation-range-start";
+ }
+ | {
+ property: "animation-range-end";
+ }
+ | {
+ property: "animation-range";
+ }
| {
property: "animation";
vendorPrefix: VendorPrefix;
@@ -2004,6 +2116,12 @@ export type PropertyId =
property: "text-size-adjust";
vendorPrefix: VendorPrefix;
}
+ | {
+ property: "direction";
+ }
+ | {
+ property: "unicode-bidi";
+ }
| {
property: "box-decoration-break";
vendorPrefix: VendorPrefix;
@@ -2240,6 +2358,19 @@ export type PropertyId =
| {
property: "view-transition-name";
}
+ | {
+ property: "view-transition-class";
+ }
+ | {
+ property: "view-transition-group";
+ }
+ | {
+ property: "color-scheme";
+ }
+ | {
+ property: "print-color-adjust";
+ vendorPrefix: VendorPrefix;
+ }
| {
property: "all";
}
@@ -2391,7 +2522,7 @@ export type Declaration =
}
| {
property: "position";
- value: Position;
+ value: Position2;
}
| {
property: "top";
@@ -2685,7 +2816,7 @@ export type Declaration =
}
| {
property: "outline";
- value: GenericBorderFor_OutlineStyle;
+ value: GenericBorderFor_OutlineStyleAnd_11;
}
| {
property: "outline-color";
@@ -3212,6 +3343,26 @@ export type Declaration =
value: AnimationFillMode[];
vendorPrefix: VendorPrefix;
}
+ | {
+ property: "animation-composition";
+ value: AnimationComposition[];
+ }
+ | {
+ property: "animation-timeline";
+ value: AnimationTimeline[];
+ }
+ | {
+ property: "animation-range-start";
+ value: AnimationRangeStart[];
+ }
+ | {
+ property: "animation-range-end";
+ value: AnimationRangeEnd[];
+ }
+ | {
+ property: "animation-range";
+ value: AnimationRange[];
+ }
| {
property: "animation";
value: Animation[];
@@ -3380,6 +3531,14 @@ export type Declaration =
value: TextSizeAdjust;
vendorPrefix: VendorPrefix;
}
+ | {
+ property: "direction";
+ value: Direction2;
+ }
+ | {
+ property: "unicode-bidi";
+ value: UnicodeBidi;
+ }
| {
property: "box-decoration-break";
value: BoxDecorationBreak;
@@ -3686,7 +3845,28 @@ export type Declaration =
}
| {
property: "view-transition-name";
- value: String;
+ value: ViewTransitionName;
+ }
+ | {
+ property: "view-transition-class";
+ value: NoneOrCustomIdentList;
+ }
+ | {
+ property: "view-transition-group";
+ value: ViewTransitionGroup;
+ }
+ | {
+ property: "color-scheme";
+ value: ColorScheme;
+ }
+ | {
+ property: "print-color-adjust";
+ value: PrintColorAdjust;
+ vendorPrefix: VendorPrefix;
+ }
+ | {
+ property: "all";
+ value: CSSWideKeyword;
}
| {
property: "unparsed";
@@ -4089,23 +4269,30 @@ export type PositionComponentFor_VerticalPositionKeyword =
* See [RadialGradient](RadialGradient).
*/
export type EndingShape =
- | {
- type: "circle";
- value: Circle;
- }
| {
type: "ellipse";
value: Ellipse;
+ }
+ | {
+ type: "circle";
+ value: Circle;
};
/**
- * A circle ending shape for a `radial-gradient()`.
+ * An ellipse ending shape for a `radial-gradient()`.
*
* See [RadialGradient](RadialGradient).
*/
-export type Circle =
+export type Ellipse =
| {
- type: "radius";
- value: Length;
+ type: "size";
+ /**
+ * The x-radius of the ellipse.
+ */
+ x: DimensionPercentageFor_LengthValue;
+ /**
+ * The y-radius of the ellipse.
+ */
+ y: DimensionPercentageFor_LengthValue;
}
| {
type: "extent";
@@ -4118,21 +4305,14 @@ export type Circle =
*/
export type ShapeExtent = "closest-side" | "farthest-side" | "closest-corner" | "farthest-corner";
/**
- * An ellipse ending shape for a `radial-gradient()`.
+ * A circle ending shape for a `radial-gradient()`.
*
* See [RadialGradient](RadialGradient).
*/
-export type Ellipse =
+export type Circle =
| {
- type: "size";
- /**
- * The x-radius of the ellipse.
- */
- x: DimensionPercentageFor_LengthValue;
- /**
- * The y-radius of the ellipse.
- */
- y: DimensionPercentageFor_LengthValue;
+ type: "radius";
+ value: Length;
}
| {
type: "extent";
@@ -4297,11 +4477,11 @@ export type WebKitGradientPointComponentFor_HorizontalPositionKeyword =
*/
export type NumberOrPercentage =
| {
- type: "percentage";
+ type: "number";
value: number;
}
| {
- type: "number";
+ type: "percentage";
value: number;
};
/**
@@ -4527,6 +4707,26 @@ export type OverflowKeyword = "visible" | "hidden" | "clip" | "scroll" | "auto";
* A value for the [text-overflow](https://www.w3.org/TR/css-overflow-3/#text-overflow) property.
*/
export type TextOverflow = "clip" | "ellipsis";
+/**
+ * A value for the [position](https://www.w3.org/TR/css-position-3/#position-property) property.
+ */
+export type Position2 =
+ | {
+ type: "static";
+ }
+ | {
+ type: "relative";
+ }
+ | {
+ type: "absolute";
+ }
+ | {
+ type: "sticky";
+ value: VendorPrefix;
+ }
+ | {
+ type: "fixed";
+ };
/**
* A generic value that represents a value with two components, e.g. a border radius.
*
@@ -4592,13 +4792,13 @@ export type RectFor_LengthOrNumber = [LengthOrNumber, LengthOrNumber, LengthOrNu
* Either a [``](https://www.w3.org/TR/css-values-4/#lengths) or a [``](https://www.w3.org/TR/css-values-4/#numbers).
*/
export type LengthOrNumber =
- | {
- type: "length";
- value: Length;
- }
| {
type: "number";
value: number;
+ }
+ | {
+ type: "length";
+ value: Length;
};
/**
* A single [border-image-repeat](https://www.w3.org/TR/css-backgrounds-3/#border-image-repeat) keyword.
@@ -5031,7 +5231,7 @@ export type RepeatCount =
};
export type AutoFlowDirection = "row" | "column";
/**
- * A value for the [grid-template-areas](https://drafts.csswg.org/css-grid-2/#grid-template-areas-property) property.
+ * A value for the [grid-template-areas](https://drafts.csswg.org/css-grid-2/#grid-template-areas-property) property. none | +
*/
export type GridTemplateAreas =
| {
@@ -5335,21 +5535,6 @@ export type StepPosition =
| {
type: "jump-both";
};
-/**
- * A value for the [animation-name](https://drafts.csswg.org/css-animations/#animation-name) property.
- */
-export type AnimationName =
- | {
- type: "none";
- }
- | {
- type: "ident";
- value: String;
- }
- | {
- type: "string";
- value: String;
- };
/**
* A value for the [animation-iteration-count](https://drafts.csswg.org/css-animations/#animation-iteration-count) property.
*/
@@ -5373,6 +5558,75 @@ export type AnimationPlayState = "running" | "paused";
* A value for the [animation-fill-mode](https://drafts.csswg.org/css-animations/#animation-fill-mode) property.
*/
export type AnimationFillMode = "none" | "forwards" | "backwards" | "both";
+/**
+ * A value for the [animation-composition](https://drafts.csswg.org/css-animations-2/#animation-composition) property.
+ */
+export type AnimationComposition = "replace" | "add" | "accumulate";
+/**
+ * A value for the [animation-timeline](https://drafts.csswg.org/css-animations-2/#animation-timeline) property.
+ */
+export type AnimationTimeline =
+ | {
+ type: "auto";
+ }
+ | {
+ type: "none";
+ }
+ | {
+ type: "dashed-ident";
+ value: String;
+ }
+ | {
+ type: "scroll";
+ value: ScrollTimeline;
+ }
+ | {
+ type: "view";
+ value: ViewTimeline;
+ };
+/**
+ * A scroll axis, used in the `scroll()` function.
+ */
+export type ScrollAxis = "block" | "inline" | "x" | "y";
+/**
+ * A scroller, used in the `scroll()` function.
+ */
+export type Scroller = "root" | "nearest" | "self";
+/**
+ * A generic value that represents a value with two components, e.g. a border radius.
+ *
+ * When serialized, only a single component will be written if both are equal.
+ *
+ * @minItems 2
+ * @maxItems 2
+ */
+export type Size2DFor_LengthPercentageOrAuto = [LengthPercentageOrAuto, LengthPercentageOrAuto];
+/**
+ * A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) property.
+ */
+export type AnimationRangeStart = AnimationAttachmentRange;
+/**
+ * A value for the [animation-range-start](https://drafts.csswg.org/scroll-animations/#animation-range-start) or [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property.
+ */
+export type AnimationAttachmentRange =
+ "normal" | DimensionPercentageFor_LengthValue | {
+ /**
+ * The name of the timeline range.
+ */
+ name: TimelineRangeName;
+ /**
+ * The offset from the start of the named timeline range.
+ */
+ offset: DimensionPercentageFor_LengthValue;
+ };
+/**
+ * A [view progress timeline range](https://drafts.csswg.org/scroll-animations/#view-timelines-ranges)
+ */
+export type TimelineRangeName = "cover" | "contain" | "entry" | "exit" | "entry-crossing" | "exit-crossing";
+/**
+ * A value for the [animation-range-end](https://drafts.csswg.org/scroll-animations/#animation-range-end) property.
+ */
+export type AnimationRangeEnd = AnimationAttachmentRange;
/**
* An individual [transform function](https://www.w3.org/TR/2019/CR-css-transforms-1-20190214/#two-d-transform-functions).
*/
@@ -5488,7 +5742,7 @@ export type Transform =
/**
* A value for the [transform-style](https://drafts.csswg.org/css-transforms-2/#transform-style-property) property.
*/
-export type TransformStyle = "flat" | "preserve-3d";
+export type TransformStyle = "flat" | "preserve3d";
/**
* A value for the [transform-box](https://drafts.csswg.org/css-transforms-1/#transform-box) property.
*/
@@ -5508,6 +5762,44 @@ export type Perspective =
type: "length";
value: Length;
};
+/**
+ * A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property.
+ */
+export type Translate =
+ | "none"
+ | {
+ /**
+ * The x translation.
+ */
+ x: DimensionPercentageFor_LengthValue;
+ /**
+ * The y translation.
+ */
+ y: DimensionPercentageFor_LengthValue;
+ /**
+ * The z translation.
+ */
+ z: Length;
+ };
+/**
+ * A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property.
+ */
+export type Scale =
+ | "none"
+ | {
+ /**
+ * Scale on the x axis.
+ */
+ x: NumberOrPercentage;
+ /**
+ * Scale on the y axis.
+ */
+ y: NumberOrPercentage;
+ /**
+ * Scale on the z axis.
+ */
+ z: NumberOrPercentage;
+ };
/**
* Defines how text case should be transformed in the [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property.
*/
@@ -5640,6 +5932,14 @@ export type TextSizeAdjust =
type: "percentage";
value: number;
};
+/**
+ * A value for the [direction](https://drafts.csswg.org/css-writing-modes-3/#direction) property.
+ */
+export type Direction2 = "ltr" | "rtl";
+/**
+ * A value for the [unicode-bidi](https://drafts.csswg.org/css-writing-modes-3/#unicode-bidi) property.
+ */
+export type UnicodeBidi = "normal" | "embed" | "isolate" | "bidi-override" | "isolate-override" | "plaintext";
/**
* A value for the [box-decoration-break](https://www.w3.org/TR/css-break-3/#break-decoration) property.
*/
@@ -5838,9 +6138,6 @@ export type MarkerSide = "match-self" | "match-parent";
* An SVG [``](https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint) value used in the `fill` and `stroke` properties.
*/
export type SVGPaint =
- | {
- type: "none";
- }
| {
/**
* A fallback to be used used in case the paint server cannot be resolved.
@@ -5861,6 +6158,9 @@ export type SVGPaint =
}
| {
type: "context-stroke";
+ }
+ | {
+ type: "none";
};
/**
* A fallback for an SVG paint in case a paint server `url()` cannot be resolved.
@@ -5978,11 +6278,11 @@ export type BasicShape =
}
| {
type: "circle";
- value: Circle;
+ value: Circle2;
}
| {
type: "ellipse";
- value: Ellipse;
+ value: Ellipse2;
}
| {
type: "polygon";
@@ -6002,6 +6302,20 @@ export type RectFor_DimensionPercentageFor_LengthValue = [
DimensionPercentageFor_LengthValue,
DimensionPercentageFor_LengthValue
];
+/**
+ * A [``](https://www.w3.org/TR/css-shapes-1/#typedef-shape-radius) value that defines the radius of a `circle()` or `ellipse()` shape.
+ */
+export type ShapeRadius =
+ | {
+ type: "length-percentage";
+ value: DimensionPercentageFor_LengthValue;
+ }
+ | {
+ type: "closest-side";
+ }
+ | {
+ type: "farthest-side";
+ };
/**
* A value for the [mask-mode](https://www.w3.org/TR/css-masking-1/#the-mask-mode) property.
*/
@@ -6119,7 +6433,7 @@ export type ZIndex =
/**
* A value for the [container-type](https://drafts.csswg.org/css-contain-3/#container-type) property. Establishes the element as a query container for the purpose of container queries.
*/
-export type ContainerType = "normal" | "inline-size" | "size";
+export type ContainerType = "normal" | "inline-size" | "size" | "scroll-state";
/**
* A value for the [container-name](https://drafts.csswg.org/css-contain-3/#container-name) property.
*/
@@ -6132,9 +6446,32 @@ export type ContainerNameList =
value: String[];
};
/**
- * A CSS custom property name.
+ * A value for the [view-transition-name](https://drafts.csswg.org/css-view-transitions-1/#view-transition-name-prop) property.
*/
-export type CustomPropertyName = String | String;
+export type ViewTransitionName =
+ "none" | "auto" | String;
+/**
+ * The `none` keyword, or a space-separated list of custom idents.
+ */
+export type NoneOrCustomIdentList =
+ "none" | String[];
+/**
+ * A value for the [view-transition-group](https://drafts.csswg.org/css-view-transitions-2/#view-transition-group-prop) property.
+ */
+export type ViewTransitionGroup =
+ "normal" | "contain" | "nearest" | String;
+/**
+ * A value for the [print-color-adjust](https://drafts.csswg.org/css-color-adjust/#propdef-print-color-adjust) property.
+ */
+export type PrintColorAdjust = "economy" | "exact";
+/**
+ * A [CSS-wide keyword](https://drafts.csswg.org/css-cascade-5/#defaulting-keywords).
+ */
+export type CSSWideKeyword = "initial" | "inherit" | "unset" | "revert" | "revert-layer";
+/**
+ * A CSS custom property name.
+ */
+export type CustomPropertyName = String | String;
export type SelectorComponent =
| {
type: "combinator";
@@ -6454,6 +6791,23 @@ export type PseudoClass =
kind: "autofill";
vendorPrefix: VendorPrefix;
}
+ | {
+ kind: "active-view-transition";
+ }
+ | {
+ kind: "active-view-transition-type";
+ /**
+ * A view transition type.
+ */
+ type: String[];
+ }
+ | {
+ kind: "state";
+ /**
+ * The custom state identifier.
+ */
+ state: String;
+ }
| {
kind: "local";
/**
@@ -6534,6 +6888,12 @@ export type PseudoElement =
| {
kind: "first-letter";
}
+ | {
+ kind: "details-content";
+ }
+ | {
+ kind: "target-text";
+ }
| {
kind: "selection";
vendorPrefix: VendorPrefix;
@@ -6585,28 +6945,47 @@ export type PseudoElement =
/**
* A part name selector.
*/
- partName: ViewTransitionPartName;
+ part: ViewTransitionPartSelector;
}
| {
kind: "view-transition-image-pair";
/**
* A part name selector.
*/
- partName: ViewTransitionPartName;
+ part: ViewTransitionPartSelector;
}
| {
kind: "view-transition-old";
/**
* A part name selector.
*/
- partName: ViewTransitionPartName;
+ part: ViewTransitionPartSelector;
}
| {
kind: "view-transition-new";
/**
* A part name selector.
*/
- partName: ViewTransitionPartName;
+ part: ViewTransitionPartSelector;
+ }
+ | {
+ /**
+ * A form control identifier.
+ */
+ identifier: String;
+ kind: "picker-function";
+ }
+ | {
+ kind: "picker-icon";
+ }
+ | {
+ kind: "checkmark";
+ }
+ | {
+ kind: "grammar-error";
+ }
+ | {
+ kind: "spelling-error";
}
| {
kind: "custom";
@@ -6653,6 +7032,10 @@ export type KeyframeSelector =
}
| {
type: "to";
+ }
+ | {
+ type: "timeline-range-percentage";
+ value: TimelineRangePercentage;
};
/**
* KeyframesName
@@ -6682,7 +7065,7 @@ export type FontFaceProperty =
}
| {
type: "font-style";
- value: FontStyle;
+ value: FontStyle2;
}
| {
type: "font-weight";
@@ -6756,6 +7139,29 @@ export type FontTechnology =
| "variations"
| "palettes"
| "incremental";
+/**
+ * A value for the [font-style](https://w3c.github.io/csswg-drafts/css-fonts/#descdef-font-face-font-style) descriptor in an `@font-face` rule.
+ */
+export type FontStyle2 =
+ | {
+ type: "normal";
+ }
+ | {
+ type: "italic";
+ }
+ | {
+ type: "oblique";
+ value: Size2DFor_Angle;
+ };
+/**
+ * A generic value that represents a value with two components, e.g. a border radius.
+ *
+ * When serialized, only a single component will be written if both are equal.
+ *
+ * @minItems 2
+ * @maxItems 2
+ */
+export type Size2DFor_Angle = [Angle, Angle];
/**
* A generic value that represents a value with two components, e.g. a border radius.
*
@@ -6810,6 +7216,17 @@ export type BasePalette =
type: "integer";
value: number;
};
+/**
+ * The name of the `@font-feature-values` sub-rule. font-feature-value-type = <@stylistic> | <@historical-forms> | <@styleset> | <@character-variant> | <@swash> | <@ornaments> | <@annotation>
+ */
+export type FontFeatureSubruleType =
+ | "stylistic"
+ | "historical-forms"
+ | "styleset"
+ | "character-variant"
+ | "swash"
+ | "ornaments"
+ | "annotation";
/**
* A [page margin box](https://www.w3.org/TR/css-page-3/#margin-boxes).
*/
@@ -6856,6 +7273,10 @@ export type ParsedComponent =
type: "length-percentage";
value: DimensionPercentageFor_LengthValue;
}
+ | {
+ type: "string";
+ value: String;
+ }
| {
type: "color";
value: CssColor;
@@ -6914,8 +7335,8 @@ export type ParsedComponent =
};
}
| {
- type: "token";
- value: Token;
+ type: "token-list";
+ value: TokenOrValue[];
};
/**
* A [multiplier](https://drafts.css-houdini.org/css-properties-values-api/#multipliers) for a [SyntaxComponent](SyntaxComponent). Indicates whether and how the component may be repeated.
@@ -6957,6 +7378,9 @@ export type SyntaxComponentKind =
| {
type: "length-percentage";
}
+ | {
+ type: "string";
+ }
| {
type: "color";
}
@@ -7016,6 +7440,14 @@ export type ContainerCondition = | {
| {
type: "style";
value: StyleQuery;
+ }
+| {
+ type: "scroll-state";
+ value: ScrollStateQuery;
+ }
+| {
+ type: "unknown";
+ value: TokenOrValue[];
};
/**
* A generic media feature or container feature.
@@ -7089,9 +7521,13 @@ export type ContainerSizeFeatureId = "width" | "height" | "inline-size" | "block
* Represents a style query within a container condition.
*/
export type StyleQuery = | {
- type: "feature";
+ type: "declaration";
value: D;
}
+| {
+ type: "property";
+ value: PropertyId;
+ }
| {
type: "not";
value: StyleQuery;
@@ -7107,6 +7543,119 @@ export type StyleQuery = | {
operator: Operator;
type: "operation";
};
+/**
+ * Represents a scroll state query within a container condition.
+ */
+export type ScrollStateQuery =
+ | {
+ type: "feature";
+ value: QueryFeatureFor_ScrollStateFeatureId;
+ }
+ | {
+ type: "not";
+ value: ScrollStateQuery;
+ }
+ | {
+ /**
+ * The conditions for the operator.
+ */
+ conditions: ScrollStateQuery[];
+ /**
+ * The operator for the conditions.
+ */
+ operator: Operator;
+ type: "operation";
+ };
+/**
+ * A generic media feature or container feature.
+ */
+export type QueryFeatureFor_ScrollStateFeatureId =
+ | {
+ /**
+ * The name of the feature.
+ */
+ name: MediaFeatureNameFor_ScrollStateFeatureId;
+ type: "plain";
+ /**
+ * The feature value.
+ */
+ value: MediaFeatureValue;
+ }
+ | {
+ /**
+ * The name of the feature.
+ */
+ name: MediaFeatureNameFor_ScrollStateFeatureId;
+ type: "boolean";
+ }
+ | {
+ /**
+ * The name of the feature.
+ */
+ name: MediaFeatureNameFor_ScrollStateFeatureId;
+ /**
+ * A comparator.
+ */
+ operator: MediaFeatureComparison;
+ type: "range";
+ /**
+ * The feature value.
+ */
+ value: MediaFeatureValue;
+ }
+ | {
+ /**
+ * The end value.
+ */
+ end: MediaFeatureValue;
+ /**
+ * A comparator for the end value.
+ */
+ endOperator: MediaFeatureComparison;
+ /**
+ * The name of the feature.
+ */
+ name: MediaFeatureNameFor_ScrollStateFeatureId;
+ /**
+ * A start value.
+ */
+ start: MediaFeatureValue;
+ /**
+ * A comparator for the start value.
+ */
+ startOperator: MediaFeatureComparison;
+ type: "interval";
+ };
+/**
+ * A media feature name.
+ */
+export type MediaFeatureNameFor_ScrollStateFeatureId = ScrollStateFeatureId | String | String;
+/**
+ * A container query scroll state feature identifier.
+ */
+export type ScrollStateFeatureId = "stuck" | "snapped" | "scrollable" | "scrolled";
+/**
+ * A property within a `@view-transition` rule.
+ *
+ * See [ViewTransitionRule](ViewTransitionRule).
+ */
+export type ViewTransitionProperty =
+ | {
+ property: "navigation";
+ value: Navigation;
+ }
+ | {
+ property: "types";
+ value: NoneOrCustomIdentList;
+ }
+ | {
+ property: "custom";
+ value: CustomProperty;
+ };
+/**
+ * A value for the [navigation](https://drafts.csswg.org/css-view-transitions-2/#view-transition-navigation-descriptor) property in a `@view-transition` rule.
+ */
+export type Navigation = "none" | "auto";
export type DefaultAtRule = null;
/**
@@ -7151,7 +7700,7 @@ export interface MediaRule {
/**
* The location of the rule in the source file.
*/
- loc: Location;
+ loc: Location2;
/**
* The media query list.
*/
@@ -7162,17 +7711,21 @@ export interface MediaRule {
rules: Rule[];
}
/**
- * A line and column position within a source file.
+ * A source location.
*/
-export interface Location {
+export interface Location2 {
/**
- * The column number, starting from 1.
+ * The column number within a line, starting at 1 for first the character of the line. Column numbers are counted in UTF-16 code units.
*/
column: number;
/**
- * The line number, starting from 1.
+ * The line number, starting at 0.
*/
line: number;
+ /**
+ * The index of the source file within the source map.
+ */
+ source_index: number;
}
/**
* A [media query list](https://drafts.csswg.org/mediaqueries/#mq-list).
@@ -7240,6 +7793,19 @@ export interface Url {
*/
url: String;
}
+/**
+ * A line and column position within a source file.
+ */
+export interface Location {
+ /**
+ * The column number, starting from 1.
+ */
+ column: number;
+ /**
+ * The line number, starting from 1.
+ */
+ line: number;
+}
/**
* A CSS variable reference.
*/
@@ -7294,7 +7860,7 @@ export interface ImportRule {
/**
* The location of the rule in the source file.
*/
- loc: Location;
+ loc: Location2;
/**
* A media query.
*/
@@ -7319,7 +7885,7 @@ export interface StyleRule {
/**
* The location of the rule in the source file.
*/
- loc: Location;
+ loc: Location2;
/**
* Nested rules within the style rule.
*/
@@ -7813,7 +8379,7 @@ export interface GenericBorderFor_LineStyle {
/**
* A generic type that represents the `border` and `outline` shorthand properties.
*/
-export interface GenericBorderFor_OutlineStyle {
+export interface GenericBorderFor_OutlineStyleAnd_11 {
/**
* The border color.
*/
@@ -7941,6 +8507,8 @@ export interface GridAutoFlow {
/**
* A value for the [grid-template](https://drafts.csswg.org/css-grid-2/#explicit-grid-shorthand) shorthand property.
*
+ * none | [ <'grid-template-rows'> / <'grid-template-columns'> ] | [ ? ? ? ]+ [ / ]?
+ *
* If `areas` is not `None`, then `rows` must also not be `None`.
*/
export interface GridTemplate {
@@ -7960,6 +8528,8 @@ export interface GridTemplate {
/**
* A value for the [grid](https://drafts.csswg.org/css-grid-2/#grid-shorthand) shorthand property.
*
+ * <'grid-template'> | <'grid-template-rows'> / [ auto-flow && dense? ] <'grid-auto-columns'>? | [ auto-flow && dense? ] <'grid-auto-rows'>? / <'grid-template-columns'>
+ *
* Explicit and implicit values may not be combined.
*/
export interface Grid {
@@ -7989,7 +8559,7 @@ export interface Grid {
rows: TrackSizing;
}
/**
- * A value for the [grid-row](https://drafts.csswg.org/css-grid-2/#propdef-grid-row) shorthand property.
+ * A value for the [grid-row](https://drafts.csswg.org/css-grid-2/#propdef-grid-row) shorthand property. ]