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 ac107a13e..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,6 +109,9 @@ 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@sha256:c22284b2d79092d3e885f64ede00f6afdeb2ccef7e2b6e78be52e7909091cd57
@@ -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: |
@@ -162,7 +179,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Build FreeBSD
- uses: cross-platform-actions/action@v0.23.0
+ uses: cross-platform-actions/action@v0.25.0
env:
DEBUG: napi:*
RUSTUP_HOME: /usr/local/rustup
@@ -170,7 +187,7 @@ 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'
@@ -198,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: |
@@ -229,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
@@ -247,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 97cbc6b1b..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.7"
+version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
+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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
-
-[[package]]
-name = "anyhow"
-version = "1.0.75"
+version = "1.0.10"
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,33 +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.15.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "405bbd46590a441abe5db3e5c8af005aa42e640803fecb51912703e93e4ce8d3"
+checksum = "8dd48a6ca358df4f7000e3fb5f08738b1b91a0e5d5f862e2f77b2b14647547f5"
dependencies = [
- "ahash 0.8.7",
- "anyhow",
+ "ahash 0.8.12",
+ "browserslist-data",
"chrono",
"either",
- "indexmap 2.2.2",
- "itertools 0.12.1",
+ "itertools 0.13.0",
"nom",
- "once_cell",
- "quote",
"serde",
"serde_json",
- "string_cache",
- "string_cache_codegen",
"thiserror",
]
[[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",
@@ -178,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",
@@ -195,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",
@@ -205,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"
@@ -231,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]]
@@ -246,9 +249,9 @@ 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",
@@ -326,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"
@@ -382,7 +376,7 @@ dependencies = [
"cssparser-macros",
"dtoa-short",
"itoa",
- "phf 0.11.2",
+ "phf",
"serde",
"smallvec",
]
@@ -403,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]]
@@ -423,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",
@@ -431,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"
@@ -444,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"
@@ -464,24 +464,24 @@ 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"
@@ -491,9 +491,9 @@ 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",
@@ -501,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"
@@ -514,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"
@@ -533,45 +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",
"libc",
- "wasi",
+ "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",
]
@@ -582,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 = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "heck"
@@ -608,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",
@@ -631,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",
]
@@ -658,12 +660,12 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.2.2"
+version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
dependencies = [
"equivalent",
- "hashbrown 0.14.2",
+ "hashbrown 0.15.2",
"serde",
]
@@ -684,27 +686,18 @@ dependencies = [
[[package]]
name = "itertools"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "itertools"
-version = "0.12.1"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+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"
@@ -729,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.54"
+version = "1.0.0-alpha.70"
dependencies = [
- "ahash 0.8.7",
+ "ahash 0.8.12",
"assert_cmd",
"assert_fs",
"atty",
- "bitflags 2.4.1",
+ "bitflags 2.6.0",
"browserslist-rs",
"clap",
"const-str",
@@ -774,7 +768,8 @@ dependencies = [
"cssparser-color",
"dashmap",
"data-encoding",
- "getrandom",
+ "getrandom 0.3.3",
+ "indexmap 2.7.0",
"indoc",
"itertools 0.10.5",
"jemallocator",
@@ -782,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",
@@ -795,8 +792,9 @@ 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",
@@ -804,7 +802,7 @@ dependencies = [
[[package]]
name = "lightningcss-napi"
-version = "0.1.0"
+version = "0.4.7"
dependencies = [
"crossbeam-channel",
"cssparser",
@@ -813,6 +811,7 @@ dependencies = [
"parcel_sourcemap",
"rayon",
"serde",
+ "serde-content",
"serde-detach",
"serde_bytes",
"smallvec",
@@ -841,15 +840,15 @@ dependencies = [
[[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",
@@ -857,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"
@@ -869,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"
@@ -890,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]]
@@ -911,23 +901,23 @@ checksum = "ebd4419172727423cf30351406c54f6cc1b354a2cfb4f1dba3e6cd07f6d5522b"
[[package]]
name = "napi-derive"
-version = "2.15.3"
+version = "2.16.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e56bd9f0bd84c1f138c5cb22bbf394f75d796b24dad689599ca94cf94e61cc21"
+checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c"
dependencies = [
"cfg-if",
"convert_case",
"napi-derive-backend",
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.90",
]
[[package]]
name = "napi-derive-backend"
-version = "1.0.61"
+version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d03b8f403a37007cad225039fc0323b961bb40d697eea744140920ebb689ff1d"
+checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf"
dependencies = [
"convert_case",
"once_cell",
@@ -935,24 +925,18 @@ dependencies = [
"quote",
"regex",
"semver",
- "syn 2.0.39",
+ "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"
@@ -971,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.19.0"
+version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "os_str_bytes"
@@ -998,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",
@@ -1027,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",
@@ -1051,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"
@@ -1078,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]]
@@ -1107,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",
]
@@ -1117,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]]
@@ -1142,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"
@@ -1170,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"
@@ -1222,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",
]
@@ -1251,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"
@@ -1270,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",
]
@@ -1290,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",
@@ -1306,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",
@@ -1316,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",
@@ -1337,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",
@@ -1348,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",
@@ -1380,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",
@@ -1404,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"
@@ -1419,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",
@@ -1432,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]]
@@ -1456,19 +1404,29 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]]
name = "semver"
-version = "1.0.22"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
+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"
@@ -1481,46 +1439,62 @@ dependencies = [
[[package]]
name = "serde_bytes"
-version = "0.11.12"
+version = "0.11.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff"
+checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
dependencies = [
"serde",
]
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "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"
@@ -1532,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"
@@ -1544,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",
]
@@ -1568,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"
@@ -1613,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",
@@ -1630,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",
]
@@ -1718,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.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
+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"
@@ -1757,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",
@@ -1771,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",
@@ -1808,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"
@@ -1843,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]]
@@ -1858,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",
@@ -1891,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 = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+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"
@@ -1946,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.32"
+version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
+checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.32"
+version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
+checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.39",
+ "syn 2.0.90",
]
diff --git a/Cargo.toml b/Cargo.toml
index 6806ca01d..c113a3921 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,17 +6,17 @@ members = [
"c",
"derive",
"static-self",
- "static-self-derive"
+ "static-self-derive",
]
[package]
authors = ["Devon Govett "]
name = "lightningcss"
-version = "1.0.0-alpha.54"
+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]
@@ -34,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"
@@ -61,23 +74,26 @@ lazy_static = "1.4.0"
const-str = "0.3.1"
pathdiff = "0.2.1"
ahash = "0.8.7"
-paste = "1.0.12"
+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.15.0", 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.2", features = ["custom"], default-features = false }
+getrandom = { version = "0.3", default-features = false }
[dev-dependencies]
indoc = "1.0.3"
@@ -85,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 443d34ba8..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.15.0" }
+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 ba689d967..759a18dbe 100644
--- a/c/src/lib.rs
+++ b/c/src/lib.rs
@@ -281,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
index 028a34505..789062ea6 100644
--- a/napi/Cargo.toml
+++ b/napi/Cargo.toml
@@ -1,7 +1,7 @@
[package]
authors = ["Devon Govett "]
name = "lightningcss-napi"
-version = "0.1.0"
+version = "0.4.7"
description = "Node-API bindings for Lightning CSS"
license = "MPL-2.0"
repository = "https://github.com/parcel-bundler/lightningcss"
@@ -13,13 +13,21 @@ visitor = ["lightningcss/visitor"]
bundler = ["dep:crossbeam-channel", "dep:rayon"]
[dependencies]
-serde = { version = "1.0.123", features = ["derive"] }
+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 = { path = "../", features = ["nodejs", "serde"] }
+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"]}
+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/napi/src/lib.rs b/napi/src/lib.rs
index 8127b18e2..f43a8d46e 100644
--- a/napi/src/lib.rs
+++ b/napi/src/lib.rs
@@ -24,6 +24,7 @@ mod at_rule_parser;
mod threadsafe_function;
#[cfg(feature = "visitor")]
mod transformer;
+mod utils;
#[cfg(feature = "visitor")]
use transformer::JsVisitor;
@@ -34,6 +35,8 @@ struct JsVisitor;
#[cfg(feature = "visitor")]
use lightningcss::visitor::Visit;
+use utils::get_named_property;
+
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct TransformResult<'i> {
@@ -73,7 +76,7 @@ impl<'i> TransformResult<'i> {
#[cfg(feature = "visitor")]
fn get_visitor(env: Env, opts: &JsObject) -> Option {
- if let Ok(visitor) = opts.get_named_property::("visitor") {
+ if let Ok(visitor) = get_named_property::(opts, "visitor") {
Some(JsVisitor::new(env, visitor))
} else {
None
@@ -118,8 +121,8 @@ pub fn transform_style_attribute(ctx: CallContext) -> napi::Result {
mod bundle {
use super::*;
use crossbeam_channel::{self, Receiver, Sender};
- use lightningcss::bundler::FileProvider;
- use napi::{Env, JsFunction, JsString, NapiRaw};
+ 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;
@@ -166,6 +169,7 @@ mod bundle {
// 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 {
@@ -200,9 +204,9 @@ mod bundle {
}
}
- fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
+ fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
if let Some(resolve) = &self.resolve {
- return CHANNEL.with(|channel| {
+ return RESOLVER_CHANNEL.with(|channel| {
let message = ResolveMessage {
specifier: specifier.to_owned(),
originating_file: originating_file.to_str().unwrap().to_owned(),
@@ -210,22 +214,18 @@ mod bundle {
};
resolve.call(message, ThreadsafeFunctionCallMode::Blocking);
- let result = channel.1.recv().unwrap();
- match result {
- Ok(result) => Ok(PathBuf::from_str(&result).unwrap()),
- Err(e) => Err(e),
- }
+ channel.1.recv().unwrap()
});
}
- Ok(originating_file.with_file_name(specifier))
+ Ok(originating_file.with_file_name(specifier).into())
}
}
struct ResolveMessage {
specifier: String,
originating_file: String,
- tx: Sender>,
+ tx: Sender>,
}
struct ReadMessage {
@@ -238,17 +238,20 @@ mod bundle {
tx: Sender>,
}
- fn await_promise(env: Env, result: JsUnknown, tx: Sender>) -> napi::Result<()> {
+ 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 = result.get_named_property("then")?;
+ let then: JsFunction = get_named_property(&result, "then")?;
let tx2 = tx.clone();
let cb = env.create_function_from_closure("callback", move |ctx| {
- let res = ctx.get::(0)?.into_utf8()?;
- let s = res.into_owned()?;
- tx.send(Ok(s)).unwrap();
+ 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| {
@@ -258,10 +261,8 @@ mod bundle {
})?;
then.call(Some(&result), &[cb, eb])?;
} else {
- let result: JsString = result.try_into()?;
- let utf8 = result.into_utf8()?;
- let s = utf8.into_owned()?;
- tx.send(Ok(s)).unwrap();
+ let result = parse(result)?;
+ tx.send(Ok(result)).unwrap();
}
Ok(())
@@ -271,10 +272,12 @@ mod bundle {
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)
+ 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<()> {
+ fn handle_error(tx: Sender>, res: napi::Result<()>) -> napi::Result<()> {
match res {
Ok(_) => Ok(()),
Err(e) => {
@@ -292,7 +295,9 @@ mod bundle {
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)
+ 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<()> {
@@ -306,9 +311,9 @@ mod bundle {
let config: BundleConfig = ctx.env.from_js_value(&opts)?;
- if let Ok(resolver) = opts.get_named_property::("resolver") {
+ if let Ok(resolver) = get_named_property::(&opts, "resolver") {
let read = if resolver.has_named_property("read")? {
- let read = resolver.get_named_property::("read")?;
+ let read = get_named_property::(&resolver, "read")?;
Some(ThreadsafeFunction::create(
ctx.env.raw(),
unsafe { read.raw() },
@@ -320,7 +325,7 @@ mod bundle {
};
let resolve = if resolver.has_named_property("resolve")? {
- let resolve = resolver.get_named_property::("resolve")?;
+ let resolve = get_named_property::(&resolver, "resolve")?;
Some(ThreadsafeFunction::create(
ctx.env.raw(),
unsafe { resolve.raw() },
@@ -418,19 +423,19 @@ mod bundle {
#[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, PathBuf};
- use std::str::FromStr;
+ 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 = opts.get_named_property::("resolver")?;
- let read = resolver.get_named_property::("read")?;
+ let resolver = get_named_property::(&opts, "resolver")?;
+ let read = get_named_property::(&resolver, "read")?;
let resolve = if resolver.has_named_property("resolve")? {
- let resolve = resolver.get_named_property::("resolve")?;
+ let resolve = get_named_property::(&resolver, "resolve")?;
Some(ctx.env.create_reference(resolve)?)
} else {
None
@@ -494,7 +499,7 @@ mod bundle {
);
}
- fn get_result(env: Env, mut value: JsUnknown) -> napi::Result {
+ 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();
@@ -504,13 +509,13 @@ mod bundle {
return Err(napi::Error::from(error));
}
if result.is_null() {
- return Err(napi::Error::new(napi::Status::GenericFailure, "No result".into()));
+ return Err(napi::Error::new(napi::Status::GenericFailure, "No result".to_string()));
}
value = unsafe { JsUnknown::from_raw(env.raw(), result)? };
}
- value.try_into()
+ Ok(value)
}
impl SourceProvider for JsSourceProvider {
@@ -520,7 +525,9 @@ mod bundle {
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)?.into_utf8()?.into_owned()?;
+ 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));
@@ -532,16 +539,17 @@ mod bundle {
Ok(unsafe { &*ptr })
}
- fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
+ 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)?.into_utf8()?;
- Ok(PathBuf::from_str(result.as_str()?).unwrap())
+ let result = get_result(self.env, result)?;
+ let result = self.env.from_js_value(result)?;
+ Ok(result)
} else {
- Ok(originating_file.with_file_name(specifier))
+ Ok(ResolveResult::File(originating_file.with_file_name(specifier)))
}
}
}
@@ -602,6 +610,11 @@ enum CssModulesOption {
struct CssModulesConfig {
pattern: Option,
dashed_idents: Option,
+ animation: Option,
+ container: Option,
+ grid: Option,
+ custom_idents: Option,
+ pure: Option,
}
#[cfg(feature = "bundler")]
@@ -710,6 +723,11 @@ fn compile<'i>(
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 {
@@ -837,6 +855,11 @@ fn compile_bundle<
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 {
diff --git a/napi/src/transformer.rs b/napi/src/transformer.rs
index 111ff2cd1..bab931f2d 100644
--- a/napi/src/transformer.rs
+++ b/napi/src/transformer.rs
@@ -24,7 +24,7 @@ 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,
@@ -99,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 {
@@ -112,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();
}
}
_ => {}
@@ -177,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 )|+;
}
@@ -190,12 +191,13 @@ 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())
}};
}
@@ -296,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",
@@ -308,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) {
@@ -750,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;
@@ -764,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,
@@ -806,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())
@@ -823,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 b85a64a9b..b5c7505c3 100644
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -9,8 +9,13 @@ publish = false
crate-type = ["cdylib"]
[dependencies]
-lightningcss-napi = { version = "0.1.0", path = "../napi", features = ["bundler", "visitor"] }
-napi = {version = "=2.10.3", default-features = false, features = ["compat-mode"]}
+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"
[target.'cfg(target_os = "macos")'.dependencies]
diff --git a/node/ast.d.ts b/node/ast.d.ts
index 25c9dd7f5..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.
@@ -1114,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.
*/
@@ -1935,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;
@@ -2066,6 +2116,12 @@ export type PropertyId =
property: "text-size-adjust";
vendorPrefix: VendorPrefix;
}
+ | {
+ property: "direction";
+ }
+ | {
+ property: "unicode-bidi";
+ }
| {
property: "box-decoration-break";
vendorPrefix: VendorPrefix;
@@ -2302,9 +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";
}
@@ -3277,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[];
@@ -3445,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;
@@ -3751,12 +3845,29 @@ 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";
value: UnparsedProperty;
@@ -4158,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";
@@ -4187,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";
@@ -4366,11 +4477,11 @@ export type WebKitGradientPointComponentFor_HorizontalPositionKeyword =
*/
export type NumberOrPercentage =
| {
- type: "percentage";
+ type: "number";
value: number;
}
| {
- type: "number";
+ type: "percentage";
value: number;
};
/**
@@ -4681,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.
@@ -5120,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 =
| {
@@ -5424,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.
*/
@@ -5462,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).
*/
@@ -5577,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.
*/
@@ -5597,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.
*/
@@ -5729,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.
*/
@@ -5927,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.
@@ -5950,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.
@@ -6222,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.
*/
@@ -6234,6 +6445,29 @@ export type ContainerNameList =
type: "names";
value: String[];
};
+/**
+ * A value for the [view-transition-name](https://drafts.csswg.org/css-view-transitions-1/#view-transition-name-prop) property.
+ */
+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.
*/
@@ -6557,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";
/**
@@ -6637,6 +6888,12 @@ export type PseudoElement =
| {
kind: "first-letter";
}
+ | {
+ kind: "details-content";
+ }
+ | {
+ kind: "target-text";
+ }
| {
kind: "selection";
vendorPrefix: VendorPrefix;
@@ -6688,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";
@@ -6756,6 +7032,10 @@ export type KeyframeSelector =
}
| {
type: "to";
+ }
+ | {
+ type: "timeline-range-percentage";
+ value: TimelineRangePercentage;
};
/**
* KeyframesName
@@ -6936,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).
*/
@@ -6982,6 +7273,10 @@ export type ParsedComponent =
type: "length-percentage";
value: DimensionPercentageFor_LengthValue;
}
+ | {
+ type: "string";
+ value: String;
+ }
| {
type: "color";
value: CssColor;
@@ -7040,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.
@@ -7083,6 +7378,9 @@ export type SyntaxComponentKind =
| {
type: "length-percentage";
}
+ | {
+ type: "string";
+ }
| {
type: "color";
}
@@ -7142,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.
@@ -7215,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;
@@ -7233,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;
/**
@@ -8084,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 {
@@ -8103,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 {
@@ -8132,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. [ / ]?
*/
export interface GridRow {
/**
@@ -8145,7 +8572,7 @@ export interface GridRow {
start: GridLine;
}
/**
- * A value for the [grid-row](https://drafts.csswg.org/css-grid-2/#propdef-grid-column) shorthand property.
+ * A value for the [grid-column](https://drafts.csswg.org/css-grid-2/#propdef-grid-column) shorthand property. [ / ]?
*/
export interface GridColumn {
/**
@@ -8158,7 +8585,7 @@ export interface GridColumn {
start: GridLine;
}
/**
- * A value for the [grid-area](https://drafts.csswg.org/css-grid-2/#propdef-grid-area) shorthand property.
+ * A value for the [grid-area](https://drafts.csswg.org/css-grid-2/#propdef-grid-area) shorthand property. [ / ]{0,3}
*/
export interface GridArea {
/**
@@ -8420,6 +8847,45 @@ export interface Transition {
*/
timingFunction: EasingFunction;
}
+/**
+ * The [scroll()](https://drafts.csswg.org/scroll-animations-1/#scroll-notation) function.
+ */
+export interface ScrollTimeline {
+ /**
+ * Specifies which axis of the scroll container to use as the progress for the timeline.
+ */
+ axis: ScrollAxis;
+ /**
+ * Specifies which element to use as the scroll container.
+ */
+ scroller: Scroller;
+}
+/**
+ * The [view()](https://drafts.csswg.org/scroll-animations-1/#view-notation) function.
+ */
+export interface ViewTimeline {
+ /**
+ * Specifies which axis of the scroll container to use as the progress for the timeline.
+ */
+ axis: ScrollAxis;
+ /**
+ * Provides an adjustment of the view progress visibility range.
+ */
+ inset: Size2DFor_LengthPercentageOrAuto;
+}
+/**
+ * A value for the [animation-range](https://drafts.csswg.org/scroll-animations/#animation-range) shorthand property.
+ */
+export interface AnimationRange {
+ /**
+ * The end of the animation's attachment range.
+ */
+ end: AnimationRangeEnd;
+ /**
+ * The start of the animation's attachment range.
+ */
+ start: AnimationRangeStart;
+}
/**
* A value for the [animation](https://drafts.csswg.org/css-animations/#animation) shorthand property.
*/
@@ -8452,6 +8918,10 @@ export interface Animation {
* The current play state of the animation.
*/
playState: AnimationPlayState;
+ /**
+ * The animation timeline.
+ */
+ timeline: AnimationTimeline;
/**
* The easing function for the animation.
*/
@@ -8489,23 +8959,6 @@ export interface Matrix3DForFloat {
m43: number;
m44: number;
}
-/**
- * A value for the [translate](https://drafts.csswg.org/css-transforms-2/#propdef-translate) property.
- */
-export interface Translate {
- /**
- * The x translation.
- */
- x: DimensionPercentageFor_LengthValue;
- /**
- * The y translation.
- */
- y: DimensionPercentageFor_LengthValue;
- /**
- * The z translation.
- */
- z: Length;
-}
/**
* A value for the [rotate](https://drafts.csswg.org/css-transforms-2/#propdef-rotate) property.
*/
@@ -8527,23 +8980,6 @@ export interface Rotate {
*/
z: number;
}
-/**
- * A value for the [scale](https://drafts.csswg.org/css-transforms-2/#propdef-scale) property.
- */
-export interface Scale {
- /**
- * Scale on the x axis.
- */
- x: NumberOrPercentage;
- /**
- * Scale on the y axis.
- */
- y: NumberOrPercentage;
- /**
- * Scale on the z axis.
- */
- z: NumberOrPercentage;
-}
/**
* A value for the [text-transform](https://www.w3.org/TR/2021/CRD-css-text-3-20210422/#text-transform-property) property.
*/
@@ -8937,6 +9373,19 @@ export interface AttrOperation {
operator: AttrSelectorOperator;
value: string;
}
+/**
+ * A [view transition part selector](https://w3c.github.io/csswg-drafts/css-view-transitions-1/#typedef-pt-name-selector).
+ */
+export interface ViewTransitionPartSelector {
+ /**
+ * A list of view transition classes.
+ */
+ classes: String[];
+ /**
+ * The view transition part name.
+ */
+ name?: ViewTransitionPartName | null;
+}
/**
* A [@keyframes](https://drafts.csswg.org/css-animations/#keyframes) rule.
*/
@@ -8973,6 +9422,19 @@ export interface Keyframe {
*/
selectors: KeyframeSelector[];
}
+/**
+ * A percentage of a given timeline range
+ */
+export interface TimelineRangePercentage {
+ /**
+ * The name of the timeline range.
+ */
+ name: TimelineRangeName;
+ /**
+ * The percentage progress between the start and end of the range.
+ */
+ percentage: number;
+}
/**
* A [@font-face](https://drafts.csswg.org/css-fonts/#font-face-rule) rule.
*/
@@ -9048,6 +9510,44 @@ export interface OverrideColors {
*/
index: number;
}
+/**
+ * A [@font-feature-values](https://drafts.csswg.org/css-fonts/#font-feature-values) rule.
+ */
+export interface FontFeatureValuesRule {
+ /**
+ * The location of the rule in the source file.
+ */
+ loc: Location2;
+ /**
+ * The name of the font feature values.
+ */
+ name: String[];
+ /**
+ * The rules within the `@font-feature-values` rule.
+ */
+ rules: {
+ [k: string]: FontFeatureSubrule;
+ };
+}
+/**
+ * A sub-rule of `@font-feature-values` https://drafts.csswg.org/css-fonts/#font-feature-values-syntax
+ */
+export interface FontFeatureSubrule {
+ /**
+ * The declarations within the `@font-feature-values` sub-rules.
+ */
+ declarations: {
+ [k: string]: number[];
+ };
+ /**
+ * The location of the rule in the source file.
+ */
+ loc: Location2;
+ /**
+ * The name of the `@font-feature-values` sub-rule.
+ */
+ name: FontFeatureSubruleType;
+}
/**
* A [@page](https://www.w3.org/TR/css-page-3/#at-page-rule) rule.
*/
@@ -9180,6 +9680,19 @@ export interface NestingRule {
*/
style: StyleRule;
}
+/**
+ * A [nested declarations](https://drafts.csswg.org/css-nesting/#nested-declarations-rule) rule.
+ */
+export interface NestedDeclarationsRule {
+ /**
+ * The style rule that defines the selector and declarations for the `@nest` rule.
+ */
+ declarations: DeclarationBlock;
+ /**
+ * The location of the rule in the source file.
+ */
+ loc: Location2;
+}
/**
* A [@viewport](https://drafts.csswg.org/css-device-adapt/#atviewport-rule) rule.
*/
@@ -9293,7 +9806,7 @@ export interface ContainerRule {
/**
* The container condition.
*/
- condition: ContainerCondition;
+ condition?: ContainerCondition | null;
/**
* The location of the rule in the source file.
*/
@@ -9343,6 +9856,19 @@ export interface StartingStyleRule {
*/
rules: Rule[];
}
+/**
+ * A [@view-transition](https://drafts.csswg.org/css-view-transitions-2/#view-transition-rule) rule.
+ */
+export interface ViewTransitionRule {
+ /**
+ * The location of the rule in the source file.
+ */
+ loc: Location2;
+ /**
+ * Declarations in the `@view-transition` rule.
+ */
+ properties: ViewTransitionProperty[];
+}
/**
* An unknown at-rule, stored as raw tokens.
*/
diff --git a/node/composeVisitors.js b/node/composeVisitors.js
index 850058d0b..f29934905 100644
--- a/node/composeVisitors.js
+++ b/node/composeVisitors.js
@@ -1,20 +1,30 @@
// @ts-check
/** @typedef {import('./index').Visitor} Visitor */
+/** @typedef {import('./index').VisitorFunction} VisitorFunction */
/**
* Composes multiple visitor objects into a single one.
- * @param {Visitor[]} visitors
- * @return {Visitor}
+ * @param {(Visitor | VisitorFunction)[]} visitors
+ * @return {Visitor | VisitorFunction}
*/
function composeVisitors(visitors) {
if (visitors.length === 1) {
return visitors[0];
}
+
+ if (visitors.some(v => typeof v === 'function')) {
+ return (opts) => {
+ let v = visitors.map(v => typeof v === 'function' ? v(opts) : v);
+ return composeVisitors(v);
+ };
+ }
/** @type Visitor */
let res = {};
- composeObjectVisitors(res, visitors, 'Rule', ruleVisitor, wrapUnknownAtRule);
- composeObjectVisitors(res, visitors, 'RuleExit', ruleVisitor, wrapUnknownAtRule);
+ composeSimpleVisitors(res, visitors, 'StyleSheet');
+ composeSimpleVisitors(res, visitors, 'StyleSheetExit');
+ composeObjectVisitors(res, visitors, 'Rule', ruleVisitor, wrapCustomAndUnknownAtRule);
+ composeObjectVisitors(res, visitors, 'RuleExit', ruleVisitor, wrapCustomAndUnknownAtRule);
composeObjectVisitors(res, visitors, 'Declaration', declarationVisitor, wrapCustomProperty);
composeObjectVisitors(res, visitors, 'DeclarationExit', declarationVisitor, wrapCustomProperty);
composeSimpleVisitors(res, visitors, 'Url');
@@ -45,8 +55,14 @@ function composeVisitors(visitors) {
module.exports = composeVisitors;
-function wrapUnknownAtRule(k, f) {
- return k === 'unknown' ? (value => f({ type: 'unknown', value })) : f;
+function wrapCustomAndUnknownAtRule(k, f) {
+ if (k === 'unknown') {
+ return (value => f({ type: 'unknown', value }));
+ }
+ if (k === 'custom') {
+ return (value => f({ type: 'custom', value }));
+ }
+ return f;
}
function wrapCustomProperty(k, f) {
@@ -66,6 +82,13 @@ function ruleVisitor(f, item) {
}
return v?.(item.value);
}
+ if (item.type === 'custom') {
+ let v = f.custom;
+ if (typeof v === 'object') {
+ v = v[item.value.name];
+ }
+ return v?.(item.value);
+ }
return f[item.type]?.(item);
}
return f?.(item);
@@ -351,7 +374,7 @@ function createArrayVisitor(visitors, apply) {
// For each value, call all visitors. If a visitor returns a new value,
// we start over, but skip the visitor that generated the value or saw
// it before (to avoid cycles). This way, visitors can be composed in any order.
- for (let v = 0; v < visitors.length;) {
+ for (let v = 0; v < visitors.length && i < arr.length;) {
if (seen.get(v)) {
v++;
continue;
diff --git a/node/flags.js b/node/flags.js
index 1759b4d97..a636a2045 100644
--- a/node/flags.js
+++ b/node/flags.js
@@ -21,7 +21,8 @@ exports.Features = {
DoublePositionGradients: 131072,
VendorPrefixes: 262144,
LogicalProperties: 524288,
+ LightDark: 1048576,
Selectors: 31,
MediaQueries: 448,
- Colors: 64512,
+ Colors: 1113088,
};
diff --git a/node/index.d.ts b/node/index.d.ts
index d138359b6..6d727d75a 100644
--- a/node/index.d.ts
+++ b/node/index.d.ts
@@ -1,4 +1,4 @@
-import type { Angle, CssColor, Rule, CustomProperty, EnvironmentVariable, Function, Image, LengthValue, MediaQuery, Declaration, Ratio, Resolution, Selector, SupportsCondition, Time, Token, TokenOrValue, UnknownAtRule, Url, Variable, StyleRule, DeclarationBlock, ParsedComponent, Multiplier, StyleSheet } from './ast';
+import type { Angle, CssColor, Rule, CustomProperty, EnvironmentVariable, Function, Image, LengthValue, MediaQuery, Declaration, Ratio, Resolution, Selector, SupportsCondition, Time, Token, TokenOrValue, UnknownAtRule, Url, Variable, StyleRule, DeclarationBlock, ParsedComponent, Multiplier, StyleSheet, Location2 } from './ast';
import { Targets, Features } from './targets';
export * from './ast';
@@ -63,7 +63,7 @@ export interface TransformOptions {
* For optimal performance, visitors should be as specific as possible about what types of values
* they care about so that JavaScript has to be called as little as possible.
*/
- visitor?: Visitor,
+ visitor?: Visitor | VisitorFunction,
/**
* Defines how to parse custom CSS at-rules. Each at-rule can have a prelude, defined using a CSS
* [syntax string](https://drafts.css-houdini.org/css-properties-values-api/#syntax-strings), and
@@ -138,7 +138,7 @@ interface CustomAtRule {
name: N,
prelude: R['prelude'] extends keyof MappedPrelude ? MappedPrelude[R['prelude']] : ParsedComponent,
body: FindByType>,
- loc: Location
+ loc: Location2
}
type CustomAtRuleBody = {
@@ -213,6 +213,13 @@ export interface Visitor {
EnvironmentVariableExit?: EnvironmentVariableVisitor | EnvironmentVariableVisitors;
}
+export type VisitorDependency = FileDependency | GlobDependency;
+export interface VisitorOptions {
+ addDependency: (dep: VisitorDependency) => void
+}
+
+export type VisitorFunction = (options: VisitorOptions) => Visitor;
+
export interface CustomAtRules {
[name: string]: CustomAtRuleDefinition
}
@@ -304,7 +311,17 @@ export interface CSSModulesConfig {
/** The pattern to use when renaming class names and other identifiers. Default is `[hash]_[local]`. */
pattern?: string,
/** Whether to rename dashed identifiers, e.g. custom properties. */
- dashedIdents?: boolean
+ dashedIdents?: boolean,
+ /** Whether to enable hashing for `@keyframes`. */
+ animation?: boolean,
+ /** Whether to enable hashing for CSS grid identifiers. */
+ grid?: boolean,
+ /** Whether to enable hashing for `@container` names. */
+ container?: boolean,
+ /** Whether to enable hashing for custom identifiers. */
+ customIdents?: boolean,
+ /** Whether to require at least one class or id selector in each rule. */
+ pure?: boolean
}
export type CSSModuleExports = {
@@ -348,7 +365,7 @@ export interface DependencyCSSModuleReference {
specifier: string
}
-export type Dependency = ImportDependency | UrlDependency;
+export type Dependency = ImportDependency | UrlDependency | FileDependency | GlobDependency;
export interface ImportDependency {
type: 'import',
@@ -374,6 +391,16 @@ export interface UrlDependency {
placeholder: string
}
+export interface FileDependency {
+ type: 'file',
+ filePath: string
+}
+
+export interface GlobDependency {
+ type: 'glob',
+ glob: string
+}
+
export interface SourceLocation {
/** The file path in which the dependency exists. */
filePath: string,
@@ -428,7 +455,7 @@ export interface TransformAttributeOptions {
* For optimal performance, visitors should be as specific as possible about what types of values
* they care about so that JavaScript has to be called as little as possible.
*/
- visitor?: Visitor
+ visitor?: Visitor | VisitorFunction
}
export interface TransformAttributeResult {
@@ -464,4 +491,4 @@ export declare function bundleAsync(options: BundleAsyn
/**
* Composes multiple visitor objects into a single one.
*/
-export declare function composeVisitors(visitors: Visitor[]): Visitor;
+export declare function composeVisitors(visitors: (Visitor | VisitorFunction)[]): Visitor | VisitorFunction;
diff --git a/node/index.js b/node/index.js
index a9f2f6d5f..6fe25aef4 100644
--- a/node/index.js
+++ b/node/index.js
@@ -1,6 +1,7 @@
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') {
@@ -12,16 +13,47 @@ if (process.platform === 'linux') {
parts.push('msvc');
}
-if (process.env.CSS_TRANSFORMER_WASM) {
- module.exports = require(`../pkg`);
-} else {
- try {
- module.exports = require(`lightningcss-${parts.join('-')}`);
- } catch (err) {
- module.exports = require(`../lightningcss.${parts.join('-')}.node`);
- }
+let native;
+try {
+ native = require(`lightningcss-${parts.join('-')}`);
+} catch (err) {
+ native = require(`../lightningcss.${parts.join('-')}.node`);
}
+module.exports.transform = wrap(native.transform);
+module.exports.transformStyleAttribute = wrap(native.transformStyleAttribute);
+module.exports.bundle = wrap(native.bundle);
+module.exports.bundleAsync = wrap(native.bundleAsync);
module.exports.browserslistToTargets = require('./browserslistToTargets');
module.exports.composeVisitors = require('./composeVisitors');
module.exports.Features = require('./flags').Features;
+
+function wrap(call) {
+ return (options) => {
+ if (typeof options.visitor === 'function') {
+ let deps = [];
+ options.visitor = options.visitor({
+ addDependency(dep) {
+ deps.push(dep);
+ }
+ });
+
+ let result = call(options);
+ if (result instanceof Promise) {
+ result = result.then(res => {
+ if (deps.length) {
+ res.dependencies ??= [];
+ res.dependencies.push(...deps);
+ }
+ return res;
+ });
+ } else if (deps.length) {
+ result.dependencies ??= [];
+ result.dependencies.push(...deps);
+ }
+ return result;
+ } else {
+ return call(options);
+ }
+ };
+}
diff --git a/node/src/lib.rs b/node/src/lib.rs
index 16030b51c..822944124 100644
--- a/node/src/lib.rs
+++ b/node/src/lib.rs
@@ -3,7 +3,7 @@
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
use napi::{CallContext, JsObject, JsUnknown};
-use napi_derive::{js_function, module_exports};
+use napi_derive::js_function;
#[js_function(1)]
fn transform(ctx: CallContext) -> napi::Result {
@@ -26,7 +26,7 @@ pub fn bundle_async(ctx: CallContext) -> napi::Result {
lightningcss_napi::bundle_async(ctx)
}
-#[cfg_attr(not(target_arch = "wasm32"), module_exports)]
+#[cfg_attr(not(target_arch = "wasm32"), napi_derive::module_exports)]
fn init(mut exports: JsObject) -> napi::Result<()> {
exports.create_named_method("transform", transform)?;
exports.create_named_method("transformStyleAttribute", transform_style_attribute)?;
@@ -41,12 +41,15 @@ fn init(mut exports: JsObject) -> napi::Result<()> {
#[cfg(target_arch = "wasm32")]
#[no_mangle]
-pub unsafe fn napi_register_wasm_v1(raw_env: napi::sys::napi_env, raw_exports: napi::sys::napi_value) {
- use napi::{Env, JsObject, NapiValue};
+pub fn register_module() {
+ unsafe fn register(raw_env: napi::sys::napi_env, raw_exports: napi::sys::napi_value) -> napi::Result<()> {
+ use napi::{Env, JsObject, NapiValue};
- let env = Env::from_raw(raw_env);
- let exports = JsObject::from_raw_unchecked(raw_env, raw_exports);
- init(exports);
+ let exports = JsObject::from_raw_unchecked(raw_env, raw_exports);
+ init(exports)
+ }
+
+ napi::bindgen_prelude::register_module_exports(register)
}
#[cfg(target_arch = "wasm32")]
diff --git a/node/targets.d.ts b/node/targets.d.ts
index c962f229f..ccc7c95f0 100644
--- a/node/targets.d.ts
+++ b/node/targets.d.ts
@@ -33,7 +33,8 @@ export const Features: {
DoublePositionGradients: 131072,
VendorPrefixes: 262144,
LogicalProperties: 524288,
+ LightDark: 1048576,
Selectors: 31,
MediaQueries: 448,
- Colors: 64512,
+ Colors: 1113088,
};
diff --git a/node/test/bundle.test.mjs b/node/test/bundle.test.mjs
index 50d113b57..4279e51c8 100644
--- a/node/test/bundle.test.mjs
+++ b/node/test/bundle.test.mjs
@@ -365,7 +365,7 @@ test('resolve return non-string', async () => {
}
if (!error) throw new Error(`\`testResolveReturnNonString()\` failed. Expected \`bundleAsync()\` to throw, but it did not.`);
- assert.equal(error.message, 'expect String, got: Number');
+ assert.equal(error.message, 'data did not match any variant of untagged enum ResolveResult');
assert.equal(error.fileName, 'tests/testdata/foo.css');
assert.equal(error.loc, {
line: 1,
@@ -414,4 +414,29 @@ test('should support throwing in visitors', async () => {
assert.equal(error.message, 'Some error');
});
+test('external import', async () => {
+ const { code: buffer } = await bundleAsync(/** @type {import('../index').BundleAsyncOptions} */ ({
+ filename: 'tests/testdata/has_external.css',
+ resolver: {
+ resolve(specifier, originatingFile) {
+ if (specifier === './does_not_exist.css' || specifier.startsWith('https:')) {
+ return {external: specifier};
+ }
+ return path.resolve(path.dirname(originatingFile), specifier);
+ }
+ }
+ }));
+ const code = buffer.toString('utf-8').trim();
+
+ const expected = `
+@import "https://fonts.googleapis.com/css2?family=Roboto&display=swap";
+@import "./does_not_exist.css";
+
+.b {
+ height: calc(100vh - 64px);
+}
+ `.trim();
+ if (code !== expected) throw new Error(`\`testResolver()\` failed. Expected:\n${expected}\n\nGot:\n${code}`);
+});
+
test.run();
diff --git a/node/test/composeVisitors.test.mjs b/node/test/composeVisitors.test.mjs
index bb95d9123..4379cf481 100644
--- a/node/test/composeVisitors.test.mjs
+++ b/node/test/composeVisitors.test.mjs
@@ -513,6 +513,87 @@ test('unknown rules', () => {
assert.equal(res.code.toString(), '.menu_link{background:#056ef0}');
});
+test('custom at rules', () => {
+ let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ @testA;
+ @testB;
+ `),
+ customAtRules: {
+ testA: {},
+ testB: {}
+ },
+ visitor: composeVisitors([
+ {
+ Rule: {
+ custom: {
+ testA(rule) {
+ return {
+ type: 'style',
+ value: {
+ loc: rule.loc,
+ selectors: [
+ [{ type: 'class', name: 'testA' }]
+ ],
+ declarations: {
+ declarations: [
+ {
+ property: 'color',
+ value: {
+ type: 'rgb',
+ r: 0xff,
+ g: 0x00,
+ b: 0x00,
+ alpha: 1,
+ }
+ }
+ ]
+ }
+ }
+ };
+ }
+ }
+ }
+ },
+ {
+ Rule: {
+ custom: {
+ testB(rule) {
+ return {
+ type: 'style',
+ value: {
+ loc: rule.loc,
+ selectors: [
+ [{ type: 'class', name: 'testB' }]
+ ],
+ declarations: {
+ declarations: [
+ {
+ property: 'color',
+ value: {
+ type: 'rgb',
+ r: 0x00,
+ g: 0xff,
+ b: 0x00,
+ alpha: 1,
+ }
+ }
+ ]
+ }
+ }
+ };
+ }
+ }
+ }
+ }
+ ])
+ });
+
+ assert.equal(res.code.toString(), '.testA{color:red}.testB{color:#0f0}');
+});
+
test('known rules', () => {
let declared = new Map();
let res = transform({
@@ -686,4 +767,94 @@ test('variables', () => {
assert.equal(res.code.toString(), 'body{padding:20px;width:600px}');
});
+test('StyleSheet', () => {
+ let styleSheetCalledCount = 0;
+ let styleSheetExitCalledCount = 0;
+ transform({
+ filename: 'test.css',
+ code: Buffer.from(`
+ body {
+ color: blue;
+ }
+ `),
+ visitor: composeVisitors([
+ {
+ StyleSheet() {
+ styleSheetCalledCount++
+ },
+ StyleSheetExit() {
+ styleSheetExitCalledCount++
+ }
+ },
+ {
+ StyleSheet() {
+ styleSheetCalledCount++
+ },
+ StyleSheetExit() {
+ styleSheetExitCalledCount++
+ }
+ }
+ ])
+ });
+ assert.equal(styleSheetCalledCount, 2);
+ assert.equal(styleSheetExitCalledCount, 2);
+});
+
+test('visitor function', () => {
+ let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ @dep "foo.js";
+ @dep2 "bar.js";
+
+ .foo {
+ width: 32px;
+ }
+ `),
+ visitor: composeVisitors([
+ ({addDependency}) => ({
+ Rule: {
+ unknown: {
+ dep(rule) {
+ let file = rule.prelude[0].value.value;
+ addDependency({
+ type: 'file',
+ filePath: file
+ });
+ return [];
+ }
+ }
+ }
+ }),
+ ({addDependency}) => ({
+ Rule: {
+ unknown: {
+ dep2(rule) {
+ let file = rule.prelude[0].value.value;
+ addDependency({
+ type: 'file',
+ filePath: file
+ });
+ return [];
+ }
+ }
+ }
+ })
+ ])
+ });
+
+ assert.equal(res.code.toString(), '.foo{width:32px}');
+ assert.equal(res.dependencies, [
+ {
+ type: 'file',
+ filePath: 'foo.js'
+ },
+ {
+ type: 'file',
+ filePath: 'bar.js'
+ }
+ ]);
+});
+
test.run();
diff --git a/node/test/visitor.test.mjs b/node/test/visitor.test.mjs
index 3a42a696b..149825b7d 100644
--- a/node/test/visitor.test.mjs
+++ b/node/test/visitor.test.mjs
@@ -249,6 +249,68 @@ test('specific environment variables', () => {
assert.equal(res.code.toString(), '@media (width<=600px){body{padding:20px}}');
});
+test('spacing with env substitution', () => {
+ // Test spacing for different cases when `env()` functions are replaced with actual values.
+ /** @type {Record} */
+ let tokens = {
+ '--var1': 'var(--foo)',
+ '--var2': 'var(--bar)',
+ '--function': 'scale(1.5)',
+ '--length1': '10px',
+ '--length2': '20px',
+ '--x': '4',
+ '--y': '12',
+ '--num1': '5',
+ '--num2': '10',
+ '--num3': '15',
+ '--counter': '2',
+ '--ident1': 'solid',
+ '--ident2': 'auto',
+ '--rotate': '45deg',
+ '--percentage1': '25%',
+ '--percentage2': '75%',
+ '--color': 'red',
+ '--color1': '#ff1234',
+ '--string1': '"hello"',
+ '--string2': '" world"'
+ };
+
+ let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ .test {
+ /* Asymmetric spacing - no space after var(). */
+ background: env(--var1) env(--var2);
+ border: env(--var1)env(--ident1);
+ transform: env(--function) env(--function);
+ /* Normal spacing between values. */
+ padding: env(--length1) env(--length2);
+ margin: env(--length1) env(--ident2);
+ outline: env(--color) env(--ident1);
+ /* Raw numbers that need spacing. */
+ cursor: url(cursor.png) env(--x) env(--y), auto;
+ stroke-dasharray: env(--num1) env(--num2) env(--num3);
+ counter-increment: myCounter env(--counter);
+ /* Mixed token types. */
+ background: linear-gradient(red env(--percentage1), blue env(--percentage2));
+ content: env(--string1) env(--string2);
+ /* Inside calc expressions. */
+ width: calc(env(--length1) - env(--length2));
+ }
+ `),
+ visitor: {
+ EnvironmentVariable(env) {
+ if (env.name.type === 'custom' && tokens[env.name.ident]) {
+ return { raw: tokens[env.name.ident] };
+ }
+ }
+ }
+ });
+
+ assert.equal(res.code.toString(), '.test{background:var(--foo) var(--bar);border:var(--foo)solid;transform:scale(1.5) scale(1.5);padding:10px 20px;margin:10px auto;outline:red solid;cursor:url(cursor.png) 4 12, auto;stroke-dasharray:5 10 15;counter-increment:myCounter 2;background:linear-gradient(red 25%, blue 75%);content:"hello" " world";width:calc(10px - 20px)}');
+});
+
test('url', () => {
// https://www.npmjs.com/package/postcss-url
let res = transform({
@@ -1108,4 +1170,119 @@ test('visit stylesheet', () => {
assert.equal(res.code.toString(), '.bar{width:80px}.foo{width:32px}');
});
+test('visitor function', () => {
+ let res = transform({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from(`
+ @dep "foo.js";
+
+ .foo {
+ width: 32px;
+ }
+ `),
+ visitor: ({addDependency}) => ({
+ Rule: {
+ unknown: {
+ dep(rule) {
+ let file = rule.prelude[0].value.value;
+ addDependency({
+ type: 'file',
+ filePath: file
+ });
+ return [];
+ }
+ }
+ }
+ })
+ });
+
+ assert.equal(res.code.toString(), '.foo{width:32px}');
+ assert.equal(res.dependencies, [{
+ type: 'file',
+ filePath: 'foo.js'
+ }]);
+});
+
+test('visitor function works with style attributes', () => {
+ let res = transformStyleAttribute({
+ filename: 'test.css',
+ minify: true,
+ code: Buffer.from('height: 12px'),
+ visitor: ({addDependency}) => ({
+ Length() {
+ addDependency({
+ type: 'file',
+ filePath: 'test.json'
+ });
+ }
+ })
+ });
+
+ assert.equal(res.dependencies, [{
+ type: 'file',
+ filePath: 'test.json'
+ }]);
+});
+
+test('visitor function works with bundler', () => {
+ let res = bundle({
+ filename: 'tests/testdata/a.css',
+ minify: true,
+ visitor: ({addDependency}) => ({
+ Length() {
+ addDependency({
+ type: 'file',
+ filePath: 'test.json'
+ });
+ }
+ })
+ });
+
+ assert.equal(res.dependencies, [
+ {
+ type: 'file',
+ filePath: 'test.json'
+ },
+ {
+ type: 'file',
+ filePath: 'test.json'
+ },
+ {
+ type: 'file',
+ filePath: 'test.json'
+ }
+ ]);
+});
+
+test('works with async bundler', async () => {
+ let res = await bundleAsync({
+ filename: 'tests/testdata/a.css',
+ minify: true,
+ visitor: ({addDependency}) => ({
+ Length() {
+ addDependency({
+ type: 'file',
+ filePath: 'test.json'
+ });
+ }
+ })
+ });
+
+ assert.equal(res.dependencies, [
+ {
+ type: 'file',
+ filePath: 'test.json'
+ },
+ {
+ type: 'file',
+ filePath: 'test.json'
+ },
+ {
+ type: 'file',
+ filePath: 'test.json'
+ }
+ ]);
+});
+
test.run();
diff --git a/package.json b/package.json
index d8fcb832a..af17c0a58 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "lightningcss",
- "version": "1.24.0",
+ "version": "1.31.1",
"license": "MPL-2.0",
"description": "A CSS parser, transformer, and minifier written in Rust",
"main": "node/index.js",
@@ -39,21 +39,21 @@
"node/*.flow"
],
"dependencies": {
- "detect-libc": "^1.0.3"
+ "detect-libc": "^2.0.3"
},
"devDependencies": {
- "@babel/parser": "^7.21.4",
- "@babel/traverse": "^7.21.4",
+ "@babel/parser": "7.21.4",
+ "@babel/traverse": "7.21.4",
"@codemirror/lang-css": "^6.0.1",
"@codemirror/lang-javascript": "^6.1.2",
"@codemirror/lint": "^6.1.0",
"@codemirror/theme-one-dark": "^6.1.0",
- "@mdn/browser-compat-data": "~5.5.0",
+ "@mdn/browser-compat-data": "~7.2.4",
"@napi-rs/cli": "^2.14.0",
- "autoprefixer": "^10.4.17",
- "caniuse-lite": "^1.0.30001585",
+ "autoprefixer": "^10.4.23",
+ "caniuse-lite": "^1.0.30001765",
"codemirror": "^6.0.1",
- "cssnano": "^5.0.8",
+ "cssnano": "^7.0.6",
"esbuild": "^0.19.8",
"flowgen": "^1.21.0",
"jest-diff": "^27.4.2",
@@ -73,7 +73,8 @@
"process": "^0.11.10",
"puppeteer": "^12.0.1",
"recast": "^0.22.0",
- "sharp": "^0.31.1",
+ "sharp": "^0.33.5",
+ "typescript": "^5.7.2",
"util": "^0.12.4",
"uvu": "^0.5.6"
},
diff --git a/patches/@babel+types+7.21.4.patch b/patches/@babel+types+7.26.3.patch
similarity index 89%
rename from patches/@babel+types+7.21.4.patch
rename to patches/@babel+types+7.26.3.patch
index 45b21d586..e672fb01f 100644
--- a/patches/@babel+types+7.21.4.patch
+++ b/patches/@babel+types+7.26.3.patch
@@ -1,8 +1,8 @@
diff --git a/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js b/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js
-index 19903eb..6bc04a8 100644
+index 31feb1e..a64b83d 100644
--- a/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js
+++ b/node_modules/@babel/types/lib/retrievers/getBindingIdentifiers.js
-@@ -59,6 +59,13 @@ getBindingIdentifiers.keys = {
+@@ -66,6 +66,13 @@ const keys = {
InterfaceDeclaration: ["id"],
TypeAlias: ["id"],
OpaqueType: ["id"],
diff --git a/patches/json-schema-to-typescript+11.0.2.patch b/patches/json-schema-to-typescript+11.0.5.patch
similarity index 99%
rename from patches/json-schema-to-typescript+11.0.2.patch
rename to patches/json-schema-to-typescript+11.0.5.patch
index 37b111733..b1d06ba68 100644
--- a/patches/json-schema-to-typescript+11.0.2.patch
+++ b/patches/json-schema-to-typescript+11.0.5.patch
@@ -1,5 +1,5 @@
diff --git a/node_modules/json-schema-to-typescript/dist/src/parser.js b/node_modules/json-schema-to-typescript/dist/src/parser.js
-index aec32ab..aafd1b5 100644
+index fa9d2e4..3f65449 100644
--- a/node_modules/json-schema-to-typescript/dist/src/parser.js
+++ b/node_modules/json-schema-to-typescript/dist/src/parser.js
@@ -1,6 +1,6 @@
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index a436857e5..1a2165581 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,3 +1,3 @@
[toolchain]
-channel = "1.76.0"
+channel = "1.92.0"
components = ["rustfmt", "clippy"]
diff --git a/scripts/build-ast.js b/scripts/build-ast.js
index 4e6bbebf5..883aa442d 100644
--- a/scripts/build-ast.js
+++ b/scripts/build-ast.js
@@ -55,6 +55,48 @@ compileFromFile('node/ast.json', {
if (path.node.name.startsWith('GenericBorderFor_LineStyleAnd_')) {
path.node.name = 'GenericBorderFor_LineStyle';
}
+ },
+ TSTypeAliasDeclaration(path) {
+ // Workaround for schemars not supporting untagged variants.
+ // https://github.com/GREsau/schemars/issues/222
+ if (
+ (path.node.id.name === 'Translate' || path.node.id.name === 'Scale') &&
+ path.node.typeAnnotation.type === 'TSUnionType' &&
+ path.node.typeAnnotation.types[1].type === 'TSTypeLiteral' &&
+ path.node.typeAnnotation.types[1].members[0].key.name === 'xyz'
+ ) {
+ path.get('typeAnnotation.types.1').replaceWith(path.node.typeAnnotation.types[1].members[0].typeAnnotation.typeAnnotation);
+ } else if (path.node.id.name === 'AnimationAttachmentRange' && path.node.typeAnnotation.type === 'TSUnionType') {
+ let types = path.node.typeAnnotation.types;
+ if (types[1].type === 'TSTypeLiteral' && types[1].members[0].key.name === 'lengthpercentage') {
+ path.get('typeAnnotation.types.1').replaceWith(path.node.typeAnnotation.types[1].members[0].typeAnnotation.typeAnnotation);
+ }
+
+ if (types[2].type === 'TSTypeLiteral' && types[2].members[0].key.name === 'timelinerange') {
+ path.get('typeAnnotation.types.2').replaceWith(path.node.typeAnnotation.types[2].members[0].typeAnnotation.typeAnnotation);
+ }
+ } else if (
+ path.node.id.name === 'NoneOrCustomIdentList' &&
+ path.node.typeAnnotation.type === 'TSUnionType' &&
+ path.node.typeAnnotation.types[1].type === 'TSTypeLiteral' &&
+ path.node.typeAnnotation.types[1].members[0].key.name === 'idents'
+ ) {
+ path.get('typeAnnotation.types.1').replaceWith(path.node.typeAnnotation.types[1].members[0].typeAnnotation.typeAnnotation);
+ } else if (
+ path.node.id.name === 'ViewTransitionGroup' &&
+ path.node.typeAnnotation.type === 'TSUnionType' &&
+ path.node.typeAnnotation.types[3].type === 'TSTypeLiteral' &&
+ path.node.typeAnnotation.types[3].members[0].key.name === 'custom'
+ ) {
+ path.get('typeAnnotation.types.3').replaceWith(path.node.typeAnnotation.types[3].members[0].typeAnnotation.typeAnnotation);
+ } else if (
+ path.node.id.name === 'ViewTransitionName' &&
+ path.node.typeAnnotation.type === 'TSUnionType' &&
+ path.node.typeAnnotation.types[2].type === 'TSTypeLiteral' &&
+ path.node.typeAnnotation.types[2].members[0].key.name === 'custom'
+ ) {
+ path.get('typeAnnotation.types.2').replaceWith(path.node.typeAnnotation.types[2].members[0].typeAnnotation.typeAnnotation);
+ }
}
});
diff --git a/scripts/build-npm.js b/scripts/build-npm.js
index dd4c91bc3..ef447a7ea 100644
--- a/scripts/build-npm.js
+++ b/scripts/build-npm.js
@@ -15,6 +15,9 @@ const triples = [
{
name: 'x86_64-pc-windows-msvc',
},
+ {
+ name: 'aarch64-pc-windows-msvc'
+ },
{
name: 'aarch64-apple-darwin',
},
@@ -35,6 +38,9 @@ const triples = [
},
{
name: 'x86_64-unknown-freebsd'
+ },
+ {
+ name: 'aarch64-linux-android'
}
];
const cpuToNodeArch = {
@@ -48,6 +54,7 @@ const sysToNodePlatform = {
freebsd: 'freebsd',
darwin: 'darwin',
windows: 'win32',
+ android: 'android'
};
let optionalDependencies = {};
diff --git a/scripts/build-prefixes.js b/scripts/build-prefixes.js
index 597e1dbdc..3a9f7945f 100644
--- a/scripts/build-prefixes.js
+++ b/scripts/build-prefixes.js
@@ -25,6 +25,7 @@ const MDN_BROWSER_MAPPING = {
firefox_android: 'firefox',
opera_android: 'opera',
safari_ios: 'ios_saf',
+ webview_ios: 'ios_saf',
samsunginternet_android: 'samsung',
webview_android: 'android',
oculus: null,
@@ -70,6 +71,10 @@ prefixes['any-pseudo'] = {
})
}
+// Safari 4-13 supports background-clip: text with a prefix.
+prefixes['background-clip'].browsers.push('safari 13');
+prefixes['background-clip'].browsers.push('ios_saf 4', 'ios_saf 13');
+
let flexSpec = {};
let oldGradient = {};
let p = new Map();
@@ -253,7 +258,7 @@ for (let feature of cssFeatures) {
addValue(compat, {}, 'custom-media-queries');
let mdnFeatures = {
- doublePositionGradients: mdn.css.types.image.gradient['radial-gradient'].doubleposition.__compat.support,
+ doublePositionGradients: mdn.css.types.gradient['radial-gradient'].doubleposition.__compat.support,
clampFunction: mdn.css.types.clamp.__compat.support,
placeSelf: mdn.css.properties['place-self'].__compat.support,
placeContent: mdn.css.properties['place-content'].__compat.support,
@@ -282,7 +287,7 @@ let mdnFeatures = {
logicalPaddingShorthand: mdn.css.properties['padding-inline'].__compat.support,
logicalInset: mdn.css.properties['inset-inline-start'].__compat.support,
logicalSize: mdn.css.properties['inline-size'].__compat.support,
- logicalTextAlign: mdn.css.properties['text-align']['flow_relative_values_start_and_end'].__compat.support,
+ logicalTextAlign: mdn.css.properties['text-align'].start.__compat.support,
labColors: mdn.css.types.color.lab.__compat.support,
oklabColors: mdn.css.types.color.oklab.__compat.support,
colorFunction: mdn.css.types.color.color.__compat.support,
@@ -319,7 +324,7 @@ let mdnFeatures = {
absFunction: mdn.css.types.abs.__compat.support,
signFunction: mdn.css.types.sign.__compat.support,
hypotFunction: mdn.css.types.hypot.__compat.support,
- gradientInterpolationHints: mdn.css.types.image.gradient['linear-gradient'].interpolation_hints.__compat.support,
+ gradientInterpolationHints: mdn.css.types.gradient['linear-gradient'].interpolation_hints.__compat.support,
borderImageRepeatRound: mdn.css.properties['border-image-repeat'].round.__compat.support,
borderImageRepeatSpace: mdn.css.properties['border-image-repeat'].space.__compat.support,
fontSizeRem: mdn.css.properties['font-size'].rem_values.__compat.support,
@@ -329,6 +334,29 @@ let mdnFeatures = {
fontStretchPercentage: mdn.css.properties['font-stretch'].percentage.__compat.support,
lightDark: mdn.css.types.color['light-dark'].__compat.support,
accentSystemColor: mdn.css.types.color['system-color'].accentcolor_accentcolortext.__compat.support,
+ animationTimelineShorthand: mdn.css.properties.animation['animation-timeline_included'].__compat.support,
+ viewTransition: mdn.css.selectors['view-transition'].__compat.support,
+ detailsContent: mdn.css.selectors['details-content'].__compat.support,
+ targetText: mdn.css.selectors['target-text'].__compat.support,
+ picker: mdn.css.selectors.picker.__compat.support,
+ pickerIcon: mdn.css.selectors['picker-icon'].__compat.support,
+ checkmark: mdn.css.selectors.checkmark.__compat.support,
+ grammarError: mdn.css.selectors['grammar-error'].__compat.support,
+ spellingError: mdn.css.selectors['spelling-error'].__compat.support,
+ statePseudoClass: Object.fromEntries(
+ Object.entries(mdn.css.selectors.state.__compat.support)
+ .map(([browser, value]) => {
+ // Chrome/Edge 90-124 supported old :--foo syntax which was removed.
+ // Only include full :state(foo) support from 125+.
+ if (Array.isArray(value)) {
+ value = value.filter(v => !v.partial_implementation)
+ } else if (value.partial_implementation) {
+ value = undefined;
+ }
+
+ return [browser, value];
+ })
+ ),
};
for (let key in mdn.css.types.length) {
@@ -343,13 +371,13 @@ for (let key in mdn.css.types.length) {
mdnFeatures[feat] = mdn.css.types.length[key].__compat.support;
}
-for (let key in mdn.css.types.image.gradient) {
+for (let key in mdn.css.types.gradient) {
if (key === '__compat') {
continue;
}
let feat = key.replace(/-([a-z])/g, (_, l) => l.toUpperCase());
- mdnFeatures[feat] = mdn.css.types.image.gradient[key].__compat.support;
+ mdnFeatures[feat] = mdn.css.types.gradient[key].__compat.support;
}
const nonStandardListStyleType = new Set([
@@ -376,7 +404,7 @@ for (let key in mdn.css.properties['list-style-type']) {
}
for (let key in mdn.css.properties['width']) {
- if (key === '__compat' || key === 'animatable') {
+ if (key === '__compat' || key === 'is_animatable') {
continue;
}
@@ -463,9 +491,10 @@ let flags = [
'DoublePositionGradients',
'VendorPrefixes',
'LogicalProperties',
+ 'LightDark',
['Selectors', ['Nesting', 'NotSelectorList', 'DirSelector', 'LangSelectorList', 'IsSelector']],
['MediaQueries', ['MediaIntervalSyntax', 'MediaRangeSyntax', 'CustomMediaQueries']],
- ['Colors', ['ColorFunction', 'OklabColors', 'LabColors', 'P3Colors', 'HexAlphaColors', 'SpaceSeparatedColorNotation']],
+ ['Colors', ['ColorFunction', 'OklabColors', 'LabColors', 'P3Colors', 'HexAlphaColors', 'SpaceSeparatedColorNotation', 'LightDark']],
];
let enumify = (f) => f.replace(/^@([a-z])/, (_, x) => 'At' + x.toUpperCase()).replace(/^::([a-z])/, (_, x) => 'PseudoElement' + x.toUpperCase()).replace(/^:([a-z])/, (_, x) => 'PseudoClass' + x.toUpperCase()).replace(/(^|-)([a-z])/g, (_, a, x) => x.toUpperCase())
@@ -636,7 +665,10 @@ impl Feature {
if self.is_compatible(browsers) {
return true
}
- browsers.${browser} = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.${browser} = None;
+ }
}\n`).join(' ')}
false
}
diff --git a/scripts/build-wasm.js b/scripts/build-wasm.js
index 9f69a9af8..718821da9 100644
--- a/scripts/build-wasm.js
+++ b/scripts/build-wasm.js
@@ -54,15 +54,21 @@ wasmPkg.type = 'module';
wasmPkg.main = 'index.mjs';
wasmPkg.module = 'index.mjs';
wasmPkg.exports = {
- types: './index.d.ts',
- node: {
- import: './wasm-node.mjs',
- require: './wasm-node.cjs',
+ '.': {
+ types: './index.d.ts',
+ node: {
+ import: './wasm-node.mjs',
+ require: './wasm-node.cjs'
+ },
+ default: {
+ import: './index.mjs',
+ require: './index.cjs'
+ }
},
- default: {
- import: './index.mjs',
- require: './index.cjs',
- }
+ // Allow esbuild to import the wasm file
+ // without copying it in the src directory.
+ // Simplifies loading it in the browser when used in a library.
+ './lightningcss_node.wasm': './lightningcss_node.wasm'
};
wasmPkg.types = 'index.d.ts';
wasmPkg.sideEffects = false;
diff --git a/selectors/Cargo.toml b/selectors/Cargo.toml
index c3dd94026..824b2f2bd 100644
--- a/selectors/Cargo.toml
+++ b/selectors/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "parcel_selectors"
-version = "0.26.4"
+version = "0.28.2"
authors = ["The Servo Project Developers"]
documentation = "https://docs.rs/parcel_selectors/"
description = "CSS Selectors matching for Rust - forked for lightningcss"
@@ -9,6 +9,7 @@ readme = "README.md"
keywords = ["css", "selectors"]
license = "MPL-2.0"
build = "build.rs"
+edition = "2021"
[lib]
name = "parcel_selectors"
@@ -24,14 +25,14 @@ serde = ["dep:serde", "smallvec/serde"]
[dependencies]
bitflags = "2.2.1"
cssparser = "0.33.0"
-fxhash = "0.2"
+rustc-hash = "2"
log = "0.4"
-phf = "0.10"
+phf = "0.11.2"
precomputed-hash = "0.1"
smallvec = "1.0"
-serde = { version = "1.0.123", features = ["derive"], optional = true }
-schemars = { version = "0.8.11", features = ["smallvec"], optional = true }
-static-self = { version = "0.1.0", path = "../static-self", optional = true }
+serde = { version = "1.0.201", features = ["derive"], optional = true }
+schemars = { version = "0.8.19", features = ["smallvec"], optional = true }
+static-self = { version = "0.1.2", path = "../static-self", optional = true }
[build-dependencies]
-phf_codegen = "0.10"
+phf_codegen = "0.11"
diff --git a/selectors/bloom.rs b/selectors/bloom.rs
index bbfbee45b..e9d2f7307 100644
--- a/selectors/bloom.rs
+++ b/selectors/bloom.rs
@@ -283,7 +283,7 @@ fn hash2(hash: u32) -> u32 {
#[test]
fn create_and_insert_some_stuff() {
- use fxhash::FxHasher;
+ use rustc_hash::FxHasher;
use std::hash::{Hash, Hasher};
use std::mem::transmute;
diff --git a/selectors/build.rs b/selectors/build.rs
index 945bb9bb6..787e2d80d 100644
--- a/selectors/build.rs
+++ b/selectors/build.rs
@@ -2,8 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-extern crate phf_codegen;
-
use std::env;
use std::fs::File;
use std::io::{BufWriter, Write};
diff --git a/selectors/lib.rs b/selectors/lib.rs
index 56217d284..2047b4e61 100644
--- a/selectors/lib.rs
+++ b/selectors/lib.rs
@@ -9,18 +9,8 @@
extern crate bitflags;
#[macro_use]
extern crate cssparser;
-extern crate fxhash;
#[macro_use]
extern crate log;
-extern crate phf;
-extern crate precomputed_hash;
-#[cfg(feature = "jsonschema")]
-extern crate schemars;
-#[cfg(feature = "serde")]
-extern crate serde;
-extern crate smallvec;
-#[cfg(feature = "into_owned")]
-extern crate static_self;
pub mod attr;
pub mod bloom;
diff --git a/selectors/matching.rs b/selectors/matching.rs
index ce3d7a59f..61f74a85c 100644
--- a/selectors/matching.rs
+++ b/selectors/matching.rs
@@ -60,7 +60,7 @@ impl ElementSelectorFlags {
}
/// Holds per-compound-selector data.
-struct LocalMatchingContext<'a, 'b: 'a, 'i, Impl: SelectorImpl<'i>> {
+struct LocalMatchingContext<'a, 'b, 'i, Impl: SelectorImpl<'i>> {
shared: &'a mut MatchingContext<'b, 'i, Impl>,
matches_hover_and_active_quirk: MatchesHoverAndActiveQuirk,
}
diff --git a/selectors/nth_index_cache.rs b/selectors/nth_index_cache.rs
index 2ca33e7bb..c5bb8db0a 100644
--- a/selectors/nth_index_cache.rs
+++ b/selectors/nth_index_cache.rs
@@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::tree::OpaqueElement;
-use fxhash::FxHashMap;
+use rustc_hash::FxHashMap;
/// A cache to speed up matching of nth-index-like selectors.
///
diff --git a/selectors/parser.rs b/selectors/parser.rs
index 25b1445b5..19563d5f9 100644
--- a/selectors/parser.rs
+++ b/selectors/parser.rs
@@ -197,7 +197,9 @@ pub enum SelectorParseErrorKind<'i> {
MissingNestingPrefix,
UnexpectedTokenInAttributeSelector(Token<'i>),
PseudoElementExpectedIdent(Token<'i>),
- UnsupportedPseudoClassOrElement(CowRcStr<'i>),
+ UnsupportedPseudoElement(CowRcStr<'i>),
+ UnsupportedPseudoClass(CowRcStr<'i>),
+ AmbiguousCssModuleClass(CowRcStr<'i>),
UnexpectedIdent(CowRcStr<'i>),
ExpectedNamespace(CowRcStr<'i>),
ExpectedBarInAttr(Token<'i>),
@@ -205,6 +207,7 @@ pub enum SelectorParseErrorKind<'i> {
InvalidQualNameInAttr(Token<'i>),
ExplicitNamespaceUnexpectedToken(Token<'i>),
ClassNeedsIdent(Token<'i>),
+ UnexpectedSelectorAfterPseudoElement(Token<'i>),
}
macro_rules! with_all_bounds {
@@ -310,7 +313,7 @@ pub trait Parser<'i> {
location: SourceLocation,
name: CowRcStr<'i>,
) -> Result<<'i>>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
- Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
+ Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
}
fn parse_non_ts_functional_pseudo_class<'t>(
@@ -318,7 +321,7 @@ pub trait Parser<'i> {
name: CowRcStr<'i>,
arguments: &mut CssParser<'i, 't>,
) -> Result<<'i>>::NonTSPseudoClass, ParseError<'i, Self::Error>> {
- Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
+ Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
}
fn parse_pseudo_element(
@@ -326,7 +329,7 @@ pub trait Parser<'i> {
location: SourceLocation,
name: CowRcStr<'i>,
) -> Result<<'i>>::PseudoElement, ParseError<'i, Self::Error>> {
- Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
+ Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name)))
}
fn parse_functional_pseudo_element<'t>(
@@ -334,7 +337,7 @@ pub trait Parser<'i> {
name: CowRcStr<'i>,
arguments: &mut CssParser<'i, 't>,
) -> Result<<'i>>::PseudoElement, ParseError<'i, Self::Error>> {
- Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
+ Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name)))
}
fn default_namespace(&self) -> Option<<'i>>::NamespaceUrl> {
@@ -877,12 +880,12 @@ impl<'i, Impl: SelectorImpl<'i>> Selector<'i, Impl> {
/// Returns an iterator over the entire sequence of simple selectors and
/// combinators, in matching order (from right to left).
#[inline]
- pub fn iter_raw_match_order(&self) -> slice::Iter<'i, Impl>> {
+ pub fn iter_raw_match_order(&self) -> slice::Iter<'_, Component<'i, Impl>> {
self.1.iter()
}
#[inline]
- pub fn iter_mut_raw_match_order(&mut self) -> slice::IterMut<'i, Impl>> {
+ pub fn iter_mut_raw_match_order(&mut self) -> slice::IterMut<'_, Component<'i, Impl>> {
self.1.iter_mut()
}
@@ -900,7 +903,7 @@ impl<'i, Impl: SelectorImpl<'i>> Selector<'i, Impl> {
/// combinators, in parse order (from left to right), starting from
/// `offset`.
#[inline]
- pub fn iter_raw_parse_order_from(&self, offset: usize) -> Rev<'i, Impl>>> {
+ pub fn iter_raw_parse_order_from(&self, offset: usize) -> Rev<'_, Component<'i, Impl>>> {
self.1[..self.len() - offset].iter().rev()
}
@@ -1006,7 +1009,7 @@ impl<'i, Impl: SelectorImpl<'i>> From<'i, Impl>>> for Selector<'i,
}
#[derive(Clone)]
-pub struct SelectorIter<'a, 'i, Impl: 'a + SelectorImpl<'i>> {
+pub struct SelectorIter<'a, 'i, Impl: SelectorImpl<'i>> {
iter: slice::Iter<'a, Component<'i, Impl>>,
next_combinator: Option,
}
@@ -1089,7 +1092,7 @@ impl<'a, 'i, Impl: SelectorImpl<'i>> fmt::Debug for SelectorIter<'a, 'i, Impl> {
}
/// An iterator over all simple selectors belonging to ancestors.
-struct AncestorIter<'a, 'i, Impl: 'a + SelectorImpl<'i>>(SelectorIter<'a, 'i, Impl>);
+struct AncestorIter<'a, 'i, Impl: SelectorImpl<'i>>(SelectorIter<'a, 'i, Impl>);
impl<'a, 'i, Impl: 'a + SelectorImpl<'i>> AncestorIter<'a, 'i, Impl> {
/// Creates an AncestorIter. The passed-in iterator is assumed to point to
/// the beginning of the child sequence, which will be skipped.
@@ -1299,7 +1302,7 @@ impl NthSelectorData {
/// Writes the beginning of the selector.
#[inline]
- fn write_start(&self, dest: &mut W, is_function: bool) -> fmt::Result {
+ pub fn write_start(&self, dest: &mut W, is_function: bool) -> fmt::Result {
dest.write_str(match self.ty {
NthType::Child if is_function => ":nth-child(",
NthType::Child => ":first-child",
@@ -1319,7 +1322,7 @@ impl NthSelectorData {
/// Serialize (part of the CSS Syntax spec, but currently only used here).
///
#[inline]
- fn write_affine(&self, dest: &mut W) -> fmt::Result {
+ pub fn write_affine(&self, dest: &mut W) -> fmt::Result {
match (self.a, self.b) {
(0, 0) => dest.write_char('0'),
@@ -2140,6 +2143,14 @@ where
}
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
+ // Input should be exhausted here.
+ let source_location = input.current_source_location();
+ if let Ok(next) = input.next() {
+ let next = next.clone();
+ return Err(
+ source_location.new_custom_error(SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(next)),
+ );
+ }
break;
}
@@ -2955,6 +2966,7 @@ where
Impl: SelectorImpl<'i>,
{
let start = input.state();
+ let token_location = input.current_source_location();
let token = match input.next_including_whitespace().map(|t| t.clone()) {
Ok(t) => t,
Err(..) => {
@@ -2966,14 +2978,18 @@ where
Ok(Some(match token {
Token::IDHash(id) => {
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+ return Err(token_location.new_custom_error(
+ SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::IDHash(id)),
+ ));
}
let id = Component::ID(id.into());
SimpleSelectorParseResult::SimpleSelector(id)
}
Token::Delim('.') => {
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+ return Err(token_location.new_custom_error(
+ SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::Delim('.')),
+ ));
}
let location = input.current_source_location();
let class = match *input.next_including_whitespace()? {
@@ -2988,7 +3004,9 @@ where
}
Token::SquareBracketBlock => {
if state.intersects(SelectorParsingState::AFTER_PSEUDO) {
- return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
+ return Err(token_location.new_custom_error(
+ SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::SquareBracketBlock),
+ ));
}
let attr = input.parse_nested_block(|input| parse_attribute_selector(parser, input))?;
SimpleSelectorParseResult::SimpleSelector(attr)
@@ -3336,7 +3354,7 @@ pub mod tests {
"active" => return Ok(PseudoClass::Active),
_ => {}
}
- Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
+ Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
}
fn parse_non_ts_functional_pseudo_class<'t>(
@@ -3351,7 +3369,7 @@ pub mod tests {
},
_ => {}
}
- Err(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
+ Err(parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name)))
}
fn parse_pseudo_element(
@@ -3364,7 +3382,7 @@ pub mod tests {
"after" => return Ok(PseudoElement::After),
_ => {}
}
- Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)))
+ Err(location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoElement(name)))
}
fn default_namespace(&self) -> Option {
@@ -3376,7 +3394,7 @@ pub mod tests {
}
}
- fn parse<'i>(input: &'i str) -> Result, SelectorParseError<'i>> {
+ fn parse<'i>(input: &'i str) -> Result<'i, DummySelectorImpl>, SelectorParseError<'i>> {
parse_ns(input, &DummyParser::default())
}
@@ -3911,6 +3929,20 @@ pub mod tests {
assert!(parse("foo:where()").is_err());
assert!(parse("foo:where(div, foo, .bar baz)").is_ok());
assert!(parse("foo:where(::before)").is_err());
+
+ assert!(parse("foo::details-content").is_ok());
+ assert!(parse("foo::target-text").is_ok());
+
+ assert!(parse("select::picker").is_err());
+ assert!(parse("::picker()").is_err());
+ assert!(parse("::picker(select)").is_ok());
+ assert!(parse("select::picker-icon").is_ok());
+ assert!(parse("option::checkmark").is_ok());
+
+ assert!(parse("::grammar-error").is_ok());
+ assert!(parse("::spelling-error").is_ok());
+ assert!(parse("::part(mypart)::grammar-error").is_ok());
+ assert!(parse("::part(mypart)::spelling-error").is_ok());
}
#[test]
diff --git a/src/bundler.rs b/src/bundler.rs
index d73545b8d..e5009a863 100644
--- a/src/bundler.rs
+++ b/src/bundler.rs
@@ -79,7 +79,7 @@ enum AtRuleParserValue<'a, T> {
struct BundleStyleSheet<'i, 'o, T> {
stylesheet: Option<'i, 'o, T>>,
- dependencies: Vec,
+ dependencies: Vec,
css_modules_deps: Vec,
parent_source_index: u32,
parent_dep_index: u32,
@@ -89,6 +89,33 @@ struct BundleStyleSheet<'i, 'o, T> {
loc: Location,
}
+#[derive(Debug, Clone)]
+enum Dependency {
+ File(u32),
+ External(String),
+}
+
+/// The result of [SourceProvider::resolve].
+#[derive(Debug)]
+#[cfg_attr(
+ any(feature = "serde", feature = "nodejs"),
+ derive(serde::Deserialize),
+ serde(rename_all = "lowercase")
+)]
+pub enum ResolveResult {
+ /// An external URL.
+ External(String),
+ /// A file path.
+ #[serde(untagged)]
+ File(PathBuf),
+}
+
+impl From for ResolveResult {
+ fn from(path: PathBuf) -> Self {
+ ResolveResult::File(path)
+ }
+}
+
/// A trait to provide the contents of files to a Bundler.
///
/// See [FileProvider](FileProvider) for an implementation that uses the
@@ -102,7 +129,7 @@ pub trait SourceProvider: Send + Sync {
/// Resolves the given import specifier to a file path given the file
/// which the import originated from.
- fn resolve(&self, specifier: &str, originating_file: &Path) -> Result;
+ fn resolve(&self, specifier: &str, originating_file: &Path) -> Result;
}
/// Provides an implementation of [SourceProvider](SourceProvider)
@@ -136,9 +163,9 @@ impl SourceProvider for FileProvider {
Ok(unsafe { &*ptr })
}
- fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
+ fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
// Assume the specifier is a relative file path and join it with current path.
- Ok(originating_file.with_file_name(specifier))
+ Ok(originating_file.with_file_name(specifier).into())
}
}
@@ -162,6 +189,11 @@ pub enum BundleErrorKind<'i, T: std::error::Error> {
UnsupportedLayerCombination,
/// Unsupported media query boolean logic was encountered.
UnsupportedMediaBooleanLogic,
+ /// An external module was referenced with a CSS module "from" clause.
+ ReferencedExternalModuleWithCssModuleFrom,
+ /// An external `@import` was found after a bundled `@import`.
+ /// This may result in unintended selector order.
+ ExternalImportAfterBundledImport,
/// A custom resolver error.
ResolverError(#[cfg_attr(any(feature = "serde", feature = "nodejs"), serde(skip))] T),
}
@@ -183,6 +215,13 @@ impl<'i, T: std::error::Error> std::fmt::Display for BundleErrorKind<'i, T> {
UnsupportedImportCondition => write!(f, "Unsupported import condition"),
UnsupportedLayerCombination => write!(f, "Unsupported layer combination in @import"),
UnsupportedMediaBooleanLogic => write!(f, "Unsupported boolean logic in @import media query"),
+ ReferencedExternalModuleWithCssModuleFrom => {
+ write!(f, "Referenced external module with CSS module \"from\" clause")
+ }
+ ExternalImportAfterBundledImport => write!(
+ f,
+ "An external `@import` was found after a bundled `@import`. This may result in unintended selector order."
+ ),
ResolverError(err) => std::fmt::Display::fmt(&err, f),
}
}
@@ -265,7 +304,7 @@ where
// Phase 3: concatenate.
let mut rules: Vec<'a, T::AtRule>> = Vec::new();
- self.inline(&mut rules);
+ self.inline(&mut rules)?;
let sources = self
.stylesheets
@@ -293,6 +332,23 @@ where
.flat_map(|s| s.stylesheet.as_ref().unwrap().license_comments.iter().cloned())
.collect();
+ if let Some(config) = &self.options.css_modules {
+ if config.pattern.has_content_hash() {
+ stylesheet.content_hashes = Some(
+ self
+ .stylesheets
+ .get_mut()
+ .unwrap()
+ .iter()
+ .flat_map(|s| {
+ let s = s.stylesheet.as_ref().unwrap();
+ s.content_hashes.as_ref().unwrap().iter().cloned()
+ })
+ .collect(),
+ );
+ }
+ }
+
Ok(stylesheet)
}
@@ -411,7 +467,7 @@ where
}
// Collect and load dependencies for this stylesheet in parallel.
- let dependencies: Result, _> = stylesheet
+ let dependencies: Result, _> = stylesheet
.rules
.0
.par_iter_mut()
@@ -467,16 +523,19 @@ where
};
let result = match self.fs.resolve(&specifier, file) {
- Ok(path) => self.load_file(
- &path,
- ImportRule {
- layer,
- media,
- supports: combine_supports(rule.supports.clone(), &import.supports),
- url: "".into(),
- loc: import.loc,
- },
- ),
+ Ok(ResolveResult::File(path)) => self
+ .load_file(
+ &path,
+ ImportRule {
+ layer,
+ media,
+ supports: combine_supports(rule.supports.clone(), &import.supports),
+ url: "".into(),
+ loc: import.loc,
+ },
+ )
+ .map(Dependency::File),
+ Ok(ResolveResult::External(url)) => Ok(Dependency::External(url)),
Err(err) => Err(Error {
kind: BundleErrorKind::ResolverError(err),
loc: Some(ErrorLocation::new(
@@ -563,7 +622,7 @@ where
) -> Option<'a, P::Error>>>> {
if let Some(Specifier::File(f)) = specifier {
let result = match self.fs.resolve(&f, file) {
- Ok(path) => {
+ Ok(ResolveResult::File(path)) => {
let res = self.load_file(
&path,
ImportRule {
@@ -585,6 +644,13 @@ where
res
}
+ Ok(ResolveResult::External(_)) => Err(Error {
+ kind: BundleErrorKind::ReferencedExternalModuleWithCssModuleFrom,
+ loc: Some(ErrorLocation::new(
+ style_loc,
+ self.find_filename(style_loc.source_index),
+ )),
+ }),
Err(err) => Err(Error {
kind: BundleErrorKind::ResolverError(err),
loc: Some(ErrorLocation::new(
@@ -629,7 +695,9 @@ where
}
for i in 0..stylesheets[source_index as usize].dependencies.len() {
- let dep_source_index = stylesheets[source_index as usize].dependencies[i];
+ let Dependency::File(dep_source_index) = stylesheets[source_index as usize].dependencies[i] else {
+ continue;
+ };
let resolved = &mut stylesheets[dep_source_index as usize];
// In browsers, every instance of an @import is evaluated, so we preserve the last.
@@ -642,14 +710,16 @@ where
}
}
- fn inline(&mut self, dest: &mut Vec<'a, T::AtRule>>) {
- process(self.stylesheets.get_mut().unwrap(), 0, dest);
-
- fn process<'a, T>(
+ fn inline(
+ &mut self,
+ dest: &mut Vec<'a, T::AtRule>>,
+ ) -> Result<(), Error<'a, P::Error>>> {
+ fn process<'a, T, E: std::error::Error>(
stylesheets: &mut Vec<'a, '_, T>>,
source_index: u32,
dest: &mut Vec<'a, T>>,
- ) {
+ filename: &String,
+ ) -> Result<(), Error<'a, E>>> {
let stylesheet = &mut stylesheets[source_index as usize];
let mut rules = std::mem::take(&mut stylesheet.stylesheet.as_mut().unwrap().rules.0);
@@ -661,26 +731,47 @@ where
// Include the dependency if this is the first instance as computed earlier.
if resolved.parent_source_index == source_index && resolved.parent_dep_index == dep_index as u32 {
- process(stylesheets, dep_source_index, dest);
+ process(stylesheets, dep_source_index, dest, filename)?;
}
dep_index += 1;
}
let mut import_index = 0;
+ let mut has_bundled_import = false;
for rule in &mut rules {
match rule {
- CssRule::Import(_) => {
- let dep_source_index = stylesheets[source_index as usize].dependencies[import_index];
- let resolved = &stylesheets[dep_source_index as usize];
-
- // Include the dependency if this is the last instance as computed earlier.
- if resolved.parent_source_index == source_index && resolved.parent_dep_index == dep_index {
- process(stylesheets, dep_source_index, dest);
+ CssRule::Import(import_rule) => {
+ let dep_source = &stylesheets[source_index as usize].dependencies[import_index];
+ match dep_source {
+ Dependency::File(dep_source_index) => {
+ let resolved = &stylesheets[*dep_source_index as usize];
+
+ // Include the dependency if this is the last instance as computed earlier.
+ if resolved.parent_source_index == source_index && resolved.parent_dep_index == dep_index {
+ has_bundled_import = true;
+ process(stylesheets, *dep_source_index, dest, filename)?;
+ }
+
+ *rule = CssRule::Ignored;
+ dep_index += 1;
+ }
+ Dependency::External(url) => {
+ if has_bundled_import {
+ return Err(Error {
+ kind: BundleErrorKind::ExternalImportAfterBundledImport,
+ loc: Some(ErrorLocation {
+ filename: filename.clone(),
+ line: import_rule.loc.line,
+ column: import_rule.loc.column,
+ }),
+ });
+ }
+ import_rule.url = url.to_owned().into();
+ let imp = std::mem::replace(rule, CssRule::Ignored);
+ dest.push(imp);
+ }
}
-
- *rule = CssRule::Ignored;
- dep_index += 1;
import_index += 1;
}
CssRule::LayerStatement(_) => {
@@ -722,7 +813,10 @@ where
}
dest.extend(rules);
+ Ok(())
}
+
+ process(self.stylesheets.get_mut().unwrap(), 0, dest, &self.options.filename)
}
}
@@ -806,8 +900,12 @@ mod tests {
Ok(self.map.get(file).unwrap())
}
- fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
- Ok(originating_file.with_file_name(specifier))
+ fn resolve(&self, specifier: &str, originating_file: &Path) -> Result {
+ if specifier.starts_with("https:") {
+ Ok(ResolveResult::External(specifier.to_owned()))
+ } else {
+ Ok(originating_file.with_file_name(specifier).into())
+ }
}
}
@@ -826,9 +924,9 @@ mod tests {
/// Resolve by stripping a `foo:` prefix off any import. Specifiers without
/// this prefix fail with an error.
- fn resolve(&self, specifier: &str, _originating_file: &Path) -> Result {
+ fn resolve(&self, specifier: &str, _originating_file: &Path) -> Result {
if specifier.starts_with("foo:") {
- Ok(Path::new(&specifier["foo:".len()..]).to_path_buf())
+ Ok(Path::new(&specifier["foo:".len()..]).to_path_buf().into())
} else {
let err = std::io::Error::new(
std::io::ErrorKind::NotFound,
@@ -866,6 +964,15 @@ mod tests {
fs: P,
entry: &str,
project_root: Option<&str>,
+ ) -> (String, CssModuleExports) {
+ bundle_css_module_with_pattern(fs, entry, project_root, "[hash]_[local]")
+ }
+
+ fn bundle_css_module_with_pattern(
+ fs: P,
+ entry: &str,
+ project_root: Option<&str>,
+ pattern: &'static str,
) -> (String, CssModuleExports) {
let mut bundler = Bundler::new(
&fs,
@@ -873,6 +980,7 @@ mod tests {
ParserOptions {
css_modules: Some(css_modules::Config {
dashed_idents: true,
+ pattern: css_modules::Pattern::parse(pattern).unwrap(),
..Default::default()
}),
..ParserOptions::default()
@@ -1521,6 +1629,49 @@ mod tests {
"#}
);
+ let res = bundle(
+ TestProvider {
+ map: fs! {
+ "/a.css": r#"
+ @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
+ @import './b.css';
+ "#,
+ "/b.css": r#"
+ .b { color: green }
+ "#
+ },
+ },
+ "/a.css",
+ );
+ assert_eq!(
+ res,
+ indoc! { r#"
+ @import "https://fonts.googleapis.com/css2?family=Roboto&display=swap";
+
+ .b {
+ color: green;
+ }
+ "#}
+ );
+
+ error_test(
+ TestProvider {
+ map: fs! {
+ "/a.css": r#"
+ @import './b.css';
+ @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');
+ "#,
+ "/b.css": r#"
+ .b { color: green }
+ "#
+ },
+ },
+ "/a.css",
+ Some(Box::new(|err| {
+ assert!(matches!(err, BundleErrorKind::ExternalImportAfterBundledImport));
+ })),
+ );
+
error_test(
TestProvider {
map: fs! {
@@ -1978,6 +2129,35 @@ mod tests {
Some("/x/y/z"),
);
assert_eq!(code, expected);
+
+ let (code, _) = bundle_css_module_with_pattern(
+ TestProvider {
+ map: fs! {
+ "/a.css": r#"
+ @import "b.css";
+ .a { color: red }
+ "#,
+ "/b.css": r#"
+ .a { color: green }
+ "#
+ },
+ },
+ "/a.css",
+ None,
+ "[content-hash]-[local]",
+ );
+ assert_eq!(
+ code,
+ indoc! { r#"
+ .do5n2W-a {
+ color: green;
+ }
+
+ .pP97eq-a {
+ color: red;
+ }
+ "#}
+ );
}
#[test]
diff --git a/src/compat.rs b/src/compat.rs
index 1a4bec654..f0c828d75 100644
--- a/src/compat.rs
+++ b/src/compat.rs
@@ -10,11 +10,14 @@ pub enum Feature {
AfarListStyleType,
AmharicAbegedeListStyleType,
AmharicListStyleType,
+ AnchorSizeSize,
+ AnimationTimelineShorthand,
AnyLink,
AnyPseudo,
ArabicIndicListStyleType,
ArmenianListStyleType,
AsterisksListStyleType,
+ AutoSize,
Autofill,
BengaliListStyleType,
BinaryListStyleType,
@@ -25,6 +28,7 @@ pub enum Feature {
CapUnit,
CaseInsensitive,
ChUnit,
+ Checkmark,
CircleListStyleType,
CjkDecimalListStyleType,
CjkEarthlyBranchListStyleType,
@@ -39,6 +43,7 @@ pub enum Feature {
DecimalLeadingZeroListStyleType,
DecimalListStyleType,
DefaultPseudo,
+ DetailsContent,
DevanagariListStyleType,
Dialog,
DirSelector,
@@ -82,6 +87,7 @@ pub enum Feature {
Gencontent,
GeorgianListStyleType,
GradientInterpolationHints,
+ GrammarError,
GujaratiListStyleType,
GurmukhiListStyleType,
HasSelector,
@@ -94,7 +100,6 @@ pub enum Feature {
ImageSet,
InOutOfRange,
IndeterminatePseudo,
- IsAnimatableSize,
IsSelector,
JapaneseFormalListStyleType,
JapaneseInformalListStyleType,
@@ -138,10 +143,10 @@ pub enum Feature {
MinFunction,
ModFunction,
MongolianListStyleType,
- MozAvailableSize,
MyanmarListStyleType,
Namespaces,
Nesting,
+ NoneListStyleType,
NotSelectorList,
NthChildOf,
OctalListStyleType,
@@ -153,6 +158,8 @@ pub enum Feature {
P3Colors,
PartPseudo,
PersianListStyleType,
+ Picker,
+ PickerIcon,
PlaceContent,
PlaceItems,
PlaceSelf,
@@ -182,11 +189,14 @@ pub enum Feature {
SimpChineseInformalListStyleType,
SomaliListStyleType,
SpaceSeparatedColorNotation,
+ SpellingError,
SquareListStyleType,
+ StatePseudoClass,
StretchSize,
StringListStyleType,
SymbolsListStyleType,
TamilListStyleType,
+ TargetText,
TeluguListStyleType,
TextDecorationThicknessPercent,
TextDecorationThicknessShorthand,
@@ -208,6 +218,7 @@ pub enum Feature {
VbUnit,
VhUnit,
ViUnit,
+ ViewTransition,
ViewportPercentageUnitsDynamic,
ViewportPercentageUnitsLarge,
ViewportPercentageUnitsSmall,
@@ -441,7 +452,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -533,7 +544,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -578,7 +589,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -623,11 +634,16 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
- if browsers.ie.is_some() || browsers.samsung.is_some() {
+ if let Some(version) = browsers.samsung {
+ if version < 1638400 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
return false;
}
}
@@ -663,7 +679,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -708,7 +724,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -753,7 +769,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -798,7 +814,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -890,7 +906,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -935,7 +951,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -999,23 +1015,13 @@ impl Feature {
return false;
}
}
- if let Some(version) = browsers.safari {
- if version < 721152 {
- return false;
- }
- }
if let Some(version) = browsers.opera {
if version < 4718592 {
return false;
}
}
- if let Some(version) = browsers.ios_saf {
- if version < 721664 {
- return false;
- }
- }
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1024,7 +1030,7 @@ impl Feature {
return false;
}
}
- if browsers.ie.is_some() {
+ if browsers.ie.is_some() || browsers.ios_saf.is_some() || browsers.safari.is_some() {
return false;
}
}
@@ -1060,7 +1066,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1150,7 +1156,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1195,7 +1201,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1245,7 +1251,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1332,7 +1338,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1377,7 +1383,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1422,11 +1428,16 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
- if browsers.ie.is_some() || browsers.samsung.is_some() {
+ if let Some(version) = browsers.samsung {
+ if version < 1638400 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
return false;
}
}
@@ -1462,7 +1473,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1507,7 +1518,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1552,7 +1563,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1619,7 +1630,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 7929856 {
+ if version < 9371648 {
return false;
}
}
@@ -1632,7 +1643,7 @@ impl Feature {
return false;
}
}
- Feature::CustomMediaQueries | Feature::FitContentFunctionSize | Feature::StretchSize => return false,
+ Feature::CustomMediaQueries | Feature::FitContentFunctionSize => return false,
Feature::DoublePositionGradients => {
if let Some(version) = browsers.chrome {
if version < 4653056 {
@@ -2205,7 +2216,7 @@ impl Feature {
}
}
if let Some(version) = browsers.samsung {
- if version < 327680 {
+ if version < 458752 {
return false;
}
}
@@ -2509,6 +2520,16 @@ impl Feature {
return false;
}
}
+ if let Some(version) = browsers.safari {
+ if version < 1704448 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1704448 {
+ return false;
+ }
+ }
if let Some(version) = browsers.samsung {
if version < 917504 {
return false;
@@ -2519,7 +2540,7 @@ impl Feature {
return false;
}
}
- if browsers.ie.is_some() || browsers.ios_saf.is_some() || browsers.safari.is_some() {
+ if browsers.ie.is_some() {
return false;
}
}
@@ -2645,7 +2666,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 2424832 {
+ if version < 263168 {
return false;
}
}
@@ -2764,6 +2785,16 @@ impl Feature {
return false;
}
}
+ if let Some(version) = browsers.safari {
+ if version < 1048576 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1048576 {
+ return false;
+ }
+ }
if let Some(version) = browsers.samsung {
if version < 655360 {
return false;
@@ -2774,7 +2805,7 @@ impl Feature {
return false;
}
}
- if browsers.ie.is_some() || browsers.ios_saf.is_some() || browsers.safari.is_some() {
+ if browsers.ie.is_some() {
return false;
}
}
@@ -2868,17 +2899,27 @@ impl Feature {
return false;
}
}
- Feature::RoundFunction
- | Feature::RemFunction
- | Feature::ModFunction
- | Feature::AbsFunction
- | Feature::SignFunction
- | Feature::HypotFunction => {
+ Feature::RoundFunction | Feature::RemFunction | Feature::ModFunction => {
+ if let Some(version) = browsers.chrome {
+ if version < 8192000 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 8192000 {
+ return false;
+ }
+ }
if let Some(version) = browsers.firefox {
if version < 7733248 {
return false;
}
}
+ if let Some(version) = browsers.opera {
+ if version < 5439488 {
+ return false;
+ }
+ }
if let Some(version) = browsers.safari {
if version < 984064 {
return false;
@@ -2889,13 +2930,102 @@ impl Feature {
return false;
}
}
- if browsers.android.is_some()
- || browsers.chrome.is_some()
- || browsers.edge.is_some()
- || browsers.ie.is_some()
- || browsers.opera.is_some()
- || browsers.samsung.is_some()
- {
+ if let Some(version) = browsers.samsung {
+ if version < 1769472 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 8192000 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
+ return false;
+ }
+ }
+ Feature::AbsFunction | Feature::SignFunction => {
+ if let Some(version) = browsers.chrome {
+ if version < 9043968 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 9043968 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 7733248 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5963776 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 984064 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 984064 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 9043968 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() || browsers.samsung.is_some() {
+ return false;
+ }
+ }
+ Feature::HypotFunction => {
+ if let Some(version) = browsers.chrome {
+ if version < 7864320 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 7864320 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 7733248 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5242880 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 984064 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 984064 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1638400 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 7864320 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
return false;
}
}
@@ -2986,7 +3116,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 2424832 {
+ if version < 263168 {
return false;
}
}
@@ -3080,7 +3210,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 262144 {
+ if version < 2752512 {
return false;
}
}
@@ -3271,19 +3401,42 @@ impl Feature {
return false;
}
}
+ if let Some(version) = browsers.edge {
+ if version < 8060928 {
+ return false;
+ }
+ }
if let Some(version) = browsers.firefox {
if version < 7864320 {
return false;
}
}
- if browsers.android.is_some()
- || browsers.edge.is_some()
- || browsers.ie.is_some()
- || browsers.ios_saf.is_some()
- || browsers.opera.is_some()
- || browsers.safari.is_some()
- || browsers.samsung.is_some()
- {
+ if let Some(version) = browsers.opera {
+ if version < 5373952 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 1115392 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1115392 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1769472 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 8060928 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
return false;
}
}
@@ -3313,69 +3466,391 @@ impl Feature {
return false;
}
}
- Feature::QUnit => {
+ Feature::AnimationTimelineShorthand => {
if let Some(version) = browsers.chrome {
- if version < 4128768 {
+ if version < 7536640 {
return false;
}
}
if let Some(version) = browsers.edge {
- if version < 5177344 {
- return false;
- }
- }
- if let Some(version) = browsers.firefox {
- if version < 3211264 {
+ if version < 7536640 {
return false;
}
}
if let Some(version) = browsers.opera {
- if version < 3014656 {
- return false;
- }
- }
- if let Some(version) = browsers.safari {
- if version < 852224 {
- return false;
- }
- }
- if let Some(version) = browsers.ios_saf {
- if version < 852992 {
+ if version < 5046272 {
return false;
}
}
if let Some(version) = browsers.samsung {
- if version < 524288 {
+ if version < 1507328 {
return false;
}
}
if let Some(version) = browsers.android {
- if version < 4128768 {
+ if version < 7536640 {
return false;
}
}
- if browsers.ie.is_some() {
+ if browsers.firefox.is_some()
+ || browsers.ie.is_some()
+ || browsers.ios_saf.is_some()
+ || browsers.safari.is_some()
+ {
return false;
}
}
- Feature::CapUnit => {
+ Feature::ViewTransition => {
if let Some(version) = browsers.chrome {
- if version < 7667712 {
+ if version < 7143424 {
return false;
}
}
if let Some(version) = browsers.edge {
- if version < 7667712 {
+ if version < 7143424 {
return false;
}
}
if let Some(version) = browsers.firefox {
- if version < 6356992 {
+ if version < 9437184 {
return false;
}
}
if let Some(version) = browsers.opera {
- if version < 5111808 {
+ if version < 4849664 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 1179648 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1179648 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1376256 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 7143424 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
+ return false;
+ }
+ }
+ Feature::DetailsContent => {
+ if let Some(version) = browsers.chrome {
+ if version < 8585216 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 8585216 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 9371648 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5701632 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 1180672 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1180672 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1900544 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 8585216 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
+ return false;
+ }
+ }
+ Feature::TargetText => {
+ if let Some(version) = browsers.chrome {
+ if version < 5832704 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 5832704 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 8585216 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 4128768 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 1180160 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1180160 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 983040 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 5832704 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
+ return false;
+ }
+ }
+ Feature::Picker => {
+ if let Some(version) = browsers.chrome {
+ if version < 8847360 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 8847360 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5832704 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1900544 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 8847360 {
+ return false;
+ }
+ }
+ if browsers.firefox.is_some()
+ || browsers.ie.is_some()
+ || browsers.ios_saf.is_some()
+ || browsers.safari.is_some()
+ {
+ return false;
+ }
+ }
+ Feature::PickerIcon | Feature::Checkmark => {
+ if let Some(version) = browsers.chrome {
+ if version < 8716288 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 8716288 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5767168 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1900544 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 8716288 {
+ return false;
+ }
+ }
+ if browsers.firefox.is_some()
+ || browsers.ie.is_some()
+ || browsers.ios_saf.is_some()
+ || browsers.safari.is_some()
+ {
+ return false;
+ }
+ }
+ Feature::GrammarError | Feature::SpellingError => {
+ if let Some(version) = browsers.chrome {
+ if version < 7929856 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 7929856 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5308416 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 1115136 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1115136 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1638400 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 7929856 {
+ return false;
+ }
+ }
+ if browsers.firefox.is_some() || browsers.ie.is_some() {
+ return false;
+ }
+ }
+ Feature::StatePseudoClass => {
+ if let Some(version) = browsers.chrome {
+ if version < 8192000 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 8192000 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 8257536 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5439488 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 1115136 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 1115136 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 1769472 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 8192000 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
+ return false;
+ }
+ }
+ Feature::QUnit => {
+ if let Some(version) = browsers.chrome {
+ if version < 4128768 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 5177344 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 3211264 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 3014656 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.safari {
+ if version < 852224 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.ios_saf {
+ if version < 852992 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.samsung {
+ if version < 524288 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.android {
+ if version < 4128768 {
+ return false;
+ }
+ }
+ if browsers.ie.is_some() {
+ return false;
+ }
+ }
+ Feature::CapUnit => {
+ if let Some(version) = browsers.chrome {
+ if version < 7733248 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.edge {
+ if version < 7733248 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 6356992 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.opera {
+ if version < 5177344 {
return false;
}
}
@@ -3389,12 +3864,17 @@ impl Feature {
return false;
}
}
+ if let Some(version) = browsers.samsung {
+ if version < 1638400 {
+ return false;
+ }
+ }
if let Some(version) = browsers.android {
- if version < 7667712 {
+ if version < 7733248 {
return false;
}
}
- if browsers.ie.is_some() || browsers.samsung.is_some() {
+ if browsers.ie.is_some() {
return false;
}
}
@@ -3680,17 +4160,22 @@ impl Feature {
}
Feature::RcapUnit => {
if let Some(version) = browsers.chrome {
- if version < 7667712 {
+ if version < 7733248 {
return false;
}
}
if let Some(version) = browsers.edge {
- if version < 7667712 {
+ if version < 7733248 {
+ return false;
+ }
+ }
+ if let Some(version) = browsers.firefox {
+ if version < 9633792 {
return false;
}
}
if let Some(version) = browsers.opera {
- if version < 5111808 {
+ if version < 5177344 {
return false;
}
}
@@ -3704,12 +4189,17 @@ impl Feature {
return false;
}
}
+ if let Some(version) = browsers.samsung {
+ if version < 1638400 {
+ return false;
+ }
+ }
if let Some(version) = browsers.android {
- if version < 7667712 {
+ if version < 7733248 {
return false;
}
}
- if browsers.firefox.is_some() || browsers.ie.is_some() || browsers.samsung.is_some() {
+ if browsers.ie.is_some() {
return false;
}
}
@@ -3724,6 +4214,11 @@ impl Feature {
return false;
}
}
+ if let Some(version) = browsers.firefox {
+ if version < 9633792 {
+ return false;
+ }
+ }
if let Some(version) = browsers.opera {
if version < 4915200 {
return false;
@@ -3749,7 +4244,7 @@ impl Feature {
return false;
}
}
- if browsers.firefox.is_some() || browsers.ie.is_some() {
+ if browsers.ie.is_some() {
return false;
}
}
@@ -4120,7 +4615,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 2424832 {
+ if version < 263168 {
return false;
}
}
@@ -4167,7 +4662,7 @@ impl Feature {
}
}
if let Some(version) = browsers.android {
- if version < 2424832 {
+ if version < 263168 {
return false;
}
}
@@ -4670,14 +5165,16 @@ impl Feature {
| Feature::HiraganaListStyleType
| Feature::HiraganaIrohaListStyleType
| Feature::KatakanaListStyleType
- | Feature::KatakanaIrohaListStyleType => {
+ | Feature::KatakanaIrohaListStyleType
+ | Feature::NoneListStyleType
+ | Feature::AutoSize => {
if let Some(version) = browsers.chrome {
if version < 1179648 {
return false;
}
}
if let Some(version) = browsers.edge {
- if version < 5177344 {
+ if version < 786432 {
return false;
}
}
@@ -4686,6 +5183,11 @@ impl Feature {
return false;
}
}
+ if let Some(version) = browsers.ie {
+ if version < 720896 {
+ return false;
+ }
+ }
if let Some(version) = browsers.opera {
if version < 917504 {
return false;
@@ -4711,9 +5213,6 @@ impl Feature {
return false;
}
}
- if browsers.ie.is_some() {
- return false;
- }
}
Feature::KoreanHangulFormalListStyleType
| Feature::KoreanHanjaFormalListStyleType
@@ -4965,44 +5464,44 @@ impl Feature {
return false;
}
}
- Feature::FitContentSize => {
+ Feature::AnchorSizeSize => {
if let Some(version) = browsers.chrome {
- if version < 1638400 {
+ if version < 8192000 {
return false;
}
}
if let Some(version) = browsers.edge {
- if version < 5177344 {
+ if version < 8192000 {
return false;
}
}
if let Some(version) = browsers.firefox {
- if version < 262144 {
+ if version < 9633792 {
return false;
}
}
if let Some(version) = browsers.opera {
- if version < 917504 {
+ if version < 5439488 {
return false;
}
}
if let Some(version) = browsers.safari {
- if version < 458752 {
+ if version < 1703936 {
return false;
}
}
if let Some(version) = browsers.ios_saf {
- if version < 458752 {
+ if version < 1703936 {
return false;
}
}
if let Some(version) = browsers.samsung {
- if version < 66816 {
+ if version < 1769472 {
return false;
}
}
if let Some(version) = browsers.android {
- if version < 263168 {
+ if version < 8192000 {
return false;
}
}
@@ -5010,24 +5509,19 @@ impl Feature {
return false;
}
}
- Feature::IsAnimatableSize => {
+ Feature::FitContentSize => {
if let Some(version) = browsers.chrome {
- if version < 1703936 {
+ if version < 1638400 {
return false;
}
}
if let Some(version) = browsers.edge {
- if version < 786432 {
+ if version < 5177344 {
return false;
}
}
if let Some(version) = browsers.firefox {
- if version < 1048576 {
- return false;
- }
- }
- if let Some(version) = browsers.ie {
- if version < 720896 {
+ if version < 262144 {
return false;
}
}
@@ -5056,10 +5550,13 @@ impl Feature {
return false;
}
}
+ if browsers.ie.is_some() {
+ return false;
+ }
}
Feature::MaxContentSize => {
if let Some(version) = browsers.chrome {
- if version < 3014656 {
+ if version < 1638400 {
return false;
}
}
@@ -5089,12 +5586,12 @@ impl Feature {
}
}
if let Some(version) = browsers.samsung {
- if version < 327680 {
+ if version < 66816 {
return false;
}
}
if let Some(version) = browsers.android {
- if version < 3014656 {
+ if version < 263168 {
return false;
}
}
@@ -5147,49 +5644,54 @@ impl Feature {
return false;
}
}
- Feature::WebkitFillAvailableSize => {
+ Feature::StretchSize => {
if let Some(version) = browsers.chrome {
- if version < 1638400 {
+ if version < 9043968 {
return false;
}
}
if let Some(version) = browsers.edge {
- if version < 5177344 {
+ if version < 9043968 {
return false;
}
}
if let Some(version) = browsers.opera {
- if version < 917504 {
+ if version < 5963776 {
return false;
}
}
- if let Some(version) = browsers.safari {
- if version < 458752 {
+ if let Some(version) = browsers.android {
+ if version < 9043968 {
return false;
}
}
- if let Some(version) = browsers.ios_saf {
- if version < 458752 {
+ if browsers.firefox.is_some()
+ || browsers.ie.is_some()
+ || browsers.ios_saf.is_some()
+ || browsers.safari.is_some()
+ || browsers.samsung.is_some()
+ {
+ return false;
+ }
+ }
+ Feature::WebkitFillAvailableSize => {
+ if let Some(version) = browsers.firefox {
+ if version < 9568256 {
return false;
}
}
- if let Some(version) = browsers.samsung {
- if version < 327680 {
+ if let Some(version) = browsers.safari {
+ if version < 458752 {
return false;
}
}
- if let Some(version) = browsers.android {
- if version < 263168 {
+ if let Some(version) = browsers.ios_saf {
+ if version < 458752 {
return false;
}
}
- if browsers.firefox.is_some() || browsers.ie.is_some() {
- return false;
- }
- }
- Feature::MozAvailableSize => {
- if let Some(version) = browsers.firefox {
- if version < 262144 {
+ if let Some(version) = browsers.samsung {
+ if version < 327680 {
return false;
}
}
@@ -5197,10 +5699,7 @@ impl Feature {
|| browsers.chrome.is_some()
|| browsers.edge.is_some()
|| browsers.ie.is_some()
- || browsers.ios_saf.is_some()
|| browsers.opera.is_some()
- || browsers.safari.is_some()
- || browsers.samsung.is_some()
{
return false;
}
@@ -5238,63 +5737,90 @@ impl Feature {
if self.is_compatible(browsers) {
return true;
}
- browsers.android = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.android = None;
+ }
}
if targets.chrome.is_some() {
browsers.chrome = targets.chrome;
if self.is_compatible(browsers) {
return true;
}
- browsers.chrome = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.chrome = None;
+ }
}
if targets.edge.is_some() {
browsers.edge = targets.edge;
if self.is_compatible(browsers) {
return true;
}
- browsers.edge = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.edge = None;
+ }
}
if targets.firefox.is_some() {
browsers.firefox = targets.firefox;
if self.is_compatible(browsers) {
return true;
}
- browsers.firefox = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.firefox = None;
+ }
}
if targets.ie.is_some() {
browsers.ie = targets.ie;
if self.is_compatible(browsers) {
return true;
}
- browsers.ie = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.ie = None;
+ }
}
if targets.ios_saf.is_some() {
browsers.ios_saf = targets.ios_saf;
if self.is_compatible(browsers) {
return true;
}
- browsers.ios_saf = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.ios_saf = None;
+ }
}
if targets.opera.is_some() {
browsers.opera = targets.opera;
if self.is_compatible(browsers) {
return true;
}
- browsers.opera = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.opera = None;
+ }
}
if targets.safari.is_some() {
browsers.safari = targets.safari;
if self.is_compatible(browsers) {
return true;
}
- browsers.safari = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.safari = None;
+ }
}
if targets.samsung.is_some() {
browsers.samsung = targets.samsung;
if self.is_compatible(browsers) {
return true;
}
- browsers.samsung = None;
+ #[allow(unused_assignments)]
+ {
+ browsers.samsung = None;
+ }
}
false
diff --git a/src/css_modules.rs b/src/css_modules.rs
index b794bb431..ce7008df2 100644
--- a/src/css_modules.rs
+++ b/src/css_modules.rs
@@ -25,13 +25,41 @@ use std::hash::{Hash, Hasher};
use std::path::Path;
/// Configuration for CSS modules.
-#[derive(Default, Clone, Debug)]
+#[derive(Clone, Debug)]
pub struct Config<'i> {
/// The name pattern to use when renaming class names and other identifiers.
/// Default is `[hash]_[local]`.
pub pattern: Pattern<'i>,
/// Whether to rename dashed identifiers, e.g. custom properties.
pub dashed_idents: bool,
+ /// Whether to scope animation names.
+ /// Default is `true`.
+ pub animation: bool,
+ /// Whether to scope grid names.
+ /// Default is `true`.
+ pub grid: bool,
+ /// Whether to scope custom identifiers
+ /// Default is `true`.
+ pub custom_idents: bool,
+ /// Whether to scope container names.
+ /// Default is `true`.
+ pub container: bool,
+ /// Whether to check for pure CSS modules.
+ pub pure: bool,
+}
+
+impl<'i> Default for Config<'i> {
+ fn default() -> Self {
+ Config {
+ pattern: Default::default(),
+ dashed_idents: Default::default(),
+ animation: true,
+ grid: true,
+ container: true,
+ custom_idents: true,
+ pure: false,
+ }
+ }
}
/// A CSS modules class name pattern.
@@ -86,6 +114,7 @@ impl<'i> Pattern<'i> {
"[name]" => Segment::Name,
"[local]" => Segment::Local,
"[hash]" => Segment::Hash,
+ "[content-hash]" => Segment::ContentHash,
s => return Err(PatternParseError::UnknownPlaceholder(s.into(), start_idx)),
};
segments.push(segment);
@@ -105,8 +134,20 @@ impl<'i> Pattern<'i> {
Ok(Pattern { segments })
}
+ /// Whether the pattern contains any `[content-hash]` segments.
+ pub fn has_content_hash(&self) -> bool {
+ self.segments.iter().any(|s| matches!(s, Segment::ContentHash))
+ }
+
/// Write the substituted pattern to a destination.
- pub fn write(&self, hash: &str, path: &Path, local: &str, mut write: W) -> Result<(), E>
+ pub fn write(
+ &self,
+ hash: &str,
+ path: &Path,
+ local: &str,
+ content_hash: &str,
+ mut write: W,
+ ) -> Result<(), E>
where
W: FnMut(&str) -> Result<(), E>,
{
@@ -129,6 +170,9 @@ impl<'i> Pattern<'i> {
Segment::Hash => {
write(hash)?;
}
+ Segment::ContentHash => {
+ write(content_hash)?;
+ }
}
}
Ok(())
@@ -141,8 +185,9 @@ impl<'i> Pattern<'i> {
hash: &str,
path: &Path,
local: &str,
+ content_hash: &str,
) -> Result {
- self.write(hash, path, local, |s| res.write_str(s))?;
+ self.write(hash, path, local, content_hash, |s| res.write_str(s))?;
Ok(res)
}
}
@@ -160,6 +205,8 @@ pub enum Segment<'i> {
Local,
/// A hash of the file name.
Hash,
+ /// A hash of the file contents.
+ ContentHash,
}
/// A referenced name within a CSS module, e.g. via the `composes` property.
@@ -224,6 +271,7 @@ pub(crate) struct CssModule<'a, 'b, 'c> {
pub config: &'a Config<'b>,
pub sources: Vec<&'c Path>,
pub hashes: Vec,
+ pub content_hashes: &'a Option>,
pub exports_by_source_index: Vec,
pub references: &'a mut HashMap,
}
@@ -234,6 +282,7 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
sources: &'c Vec,
project_root: Option<&'c str>,
references: &'a mut HashMap,
+ content_hashes: &'a Option>,
) -> Self {
let project_root = project_root.map(|p| Path::new(p));
let sources: Vec<&Path> = sources.iter().map(|filename| Path::new(filename)).collect();
@@ -258,6 +307,7 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
exports_by_source_index: sources.iter().map(|_| HashMap::new()).collect(),
sources,
hashes,
+ content_hashes,
references,
}
}
@@ -274,6 +324,11 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
local,
+ if let Some(content_hashes) = &self.content_hashes {
+ &content_hashes[source_index as usize]
+ } else {
+ ""
+ },
)
.unwrap(),
composes: vec![],
@@ -293,6 +348,11 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
&local[2..],
+ if let Some(content_hashes) = &self.content_hashes {
+ &content_hashes[source_index as usize]
+ } else {
+ ""
+ },
)
.unwrap(),
composes: vec![],
@@ -315,6 +375,11 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
name,
+ if let Some(content_hashes) = &self.content_hashes {
+ &content_hashes[source_index as usize]
+ } else {
+ ""
+ },
)
.unwrap(),
composes: vec![],
@@ -344,6 +409,11 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
&self.hashes[*source_index as usize],
&self.sources[*source_index as usize],
&name[2..],
+ if let Some(content_hashes) = &self.content_hashes {
+ &content_hashes[*source_index as usize]
+ } else {
+ ""
+ },
)
.unwrap(),
)
@@ -364,6 +434,11 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
&name[2..],
+ if let Some(content_hashes) = &self.content_hashes {
+ &content_hashes[source_index as usize]
+ } else {
+ ""
+ },
)
.unwrap(),
composes: vec![],
@@ -406,6 +481,11 @@ impl<'a, 'b, 'c> CssModule<'a, 'b, 'c> {
&self.hashes[source_index as usize],
&self.sources[source_index as usize],
name.0.as_ref(),
+ if let Some(content_hashes) = &self.content_hashes {
+ &content_hashes[source_index as usize]
+ } else {
+ ""
+ },
)
.unwrap(),
},
diff --git a/src/declaration.rs b/src/declaration.rs
index 556fd152f..0dd3da619 100644
--- a/src/declaration.rs
+++ b/src/declaration.rs
@@ -1,16 +1,16 @@
//! CSS declarations.
use std::borrow::Cow;
-use std::collections::HashMap;
use std::ops::Range;
use crate::context::{DeclarationContext, PropertyHandlerContext};
-use crate::error::{ParserError, PrinterError};
+use crate::error::{ParserError, PrinterError, PrinterErrorKind};
use crate::parser::ParserOptions;
use crate::printer::Printer;
use crate::properties::box_shadow::BoxShadowHandler;
use crate::properties::custom::{CustomProperty, CustomPropertyName};
use crate::properties::masking::MaskHandler;
+use crate::properties::text::{Direction, UnicodeBidi};
use crate::properties::{
align::AlignHandler,
animation::AnimationHandler,
@@ -34,12 +34,15 @@ use crate::properties::{
ui::ColorSchemeHandler,
};
use crate::properties::{Property, PropertyId};
+use crate::selector::SelectorList;
use crate::traits::{PropertyHandler, ToCss};
use crate::values::ident::DashedIdent;
use crate::values::string::CowArcStr;
#[cfg(feature = "visitor")]
use crate::visitor::Visit;
use cssparser::*;
+use indexmap::IndexMap;
+use smallvec::SmallVec;
/// A CSS declaration block.
///
@@ -156,18 +159,70 @@ impl<'i> DeclarationBlock<'i> {
dest.whitespace()?;
dest.write_char('{')?;
dest.indent();
+ dest.newline()?;
+
+ self.to_css_declarations(dest, false, &parcel_selectors::SelectorList(SmallVec::new()), 0)?;
+
+ dest.dedent();
+ dest.newline()?;
+ dest.write_char('}')
+ }
+ pub(crate) fn has_printable_declarations(&self) -> bool {
+ if self.len() > 1 {
+ return true;
+ }
+
+ if self.declarations.len() == 1 {
+ !matches!(self.declarations[0], crate::properties::Property::Composes(_))
+ } else if self.important_declarations.len() == 1 {
+ !matches!(self.important_declarations[0], crate::properties::Property::Composes(_))
+ } else {
+ false
+ }
+ }
+
+ /// Writes the declarations to a CSS declaration block.
+ pub fn to_css_declarations(
+ &self,
+ dest: &mut Printer,
+ has_nested_rules: bool,
+ selectors: &SelectorList,
+ source_index: u32,
+ ) -> Result<(), PrinterError>
+ where
+ W: std::fmt::Write,
+ {
let mut i = 0;
let len = self.len();
macro_rules! write {
($decls: expr, $important: literal) => {
for decl in &$decls {
- dest.newline()?;
+ // The CSS modules `composes` property is handled specially, and omitted during printing.
+ // We need to add the classes it references to the list for the selectors in this rule.
+ if let crate::properties::Property::Composes(composes) = &decl {
+ if dest.is_nested() && dest.css_module.is_some() {
+ return Err(dest.error(PrinterErrorKind::InvalidComposesNesting, composes.loc));
+ }
+
+ if let Some(css_module) = &mut dest.css_module {
+ css_module
+ .handle_composes(&selectors, &composes, source_index)
+ .map_err(|e| dest.error(e, composes.loc))?;
+ continue;
+ }
+ }
+
+ if i > 0 {
+ dest.newline()?;
+ }
+
decl.to_css(dest, $important)?;
- if i != len - 1 || !dest.minify {
+ if i != len - 1 || !dest.minify || has_nested_rules {
dest.write_char(';')?;
}
+
i += 1;
}
};
@@ -175,10 +230,7 @@ impl<'i> DeclarationBlock<'i> {
write!(self.declarations, false);
write!(self.important_declarations, true);
-
- dest.dedent();
- dest.newline()?;
- dest.write_char('}')
+ Ok(())
}
}
@@ -515,7 +567,9 @@ pub(crate) struct DeclarationHandler<'i> {
color_scheme: ColorSchemeHandler,
fallback: FallbackHandler,
prefix: PrefixHandler,
- custom_properties: HashMap<'i>, usize>,
+ direction: Option,
+ unicode_bidi: Option,
+ custom_properties: IndexMap