From f6a7912199f680a3fe02beac0eb298ddc5dff1ee Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Sun, 1 Sep 2024 00:24:04 +0800 Subject: [PATCH 01/12] feat: init tailwindcss --- Cargo.lock | 319 +- .../tailwindcss/.github/workflows/build.yaml | 112 + .../tailwindcss/.github/workflows/ci.yaml | 36 + .../tailwindcss/.github/workflows/release.yml | 43 + packages/tailwindcss/.gitignore | 197 + packages/tailwindcss/Cargo.toml | 15 + packages/tailwindcss/crates/oxide/Cargo.toml | 22 + .../tailwindcss/crates/oxide/fuzz/.gitignore | 4 + .../tailwindcss/crates/oxide/fuzz/Cargo.lock | 531 +++ .../tailwindcss/crates/oxide/fuzz/Cargo.toml | 27 + .../crates/oxide/fuzz/fuzz_targets/parsing.rs | 31 + .../tailwindcss/crates/oxide/src/cursor.rs | 159 + .../tailwindcss/crates/oxide/src/fast_skip.rs | 89 + packages/tailwindcss/crates/oxide/src/glob.rs | 479 +++ packages/tailwindcss/crates/oxide/src/lib.rs | 318 ++ .../tailwindcss/crates/oxide/src/parser.rs | 1427 +++++++ .../crates/oxide/src/scanner/allowed_paths.rs | 64 + .../oxide/src/scanner/detect_sources.rs | 223 ++ .../scanner/fixtures/binary-extensions.txt | 264 ++ .../scanner/fixtures/ignored-extensions.txt | 6 + .../src/scanner/fixtures/ignored-files.txt | 3 + .../scanner/fixtures/template-extensions.txt | 61 + .../crates/oxide/src/scanner/mod.rs | 2 + .../tailwindcss/crates/oxide/tests/scanner.rs | 367 ++ .../tailwindcss/npm/darwin-arm64/README.md | 3 + .../tailwindcss/npm/darwin-arm64/package.json | 18 + packages/tailwindcss/npm/darwin-x64/README.md | 3 + .../tailwindcss/npm/darwin-x64/package.json | 18 + .../tailwindcss/npm/linux-arm64-gnu/README.md | 3 + .../npm/linux-arm64-gnu/package.json | 21 + .../npm/linux-arm64-musl/README.md | 3 + .../npm/linux-arm64-musl/package.json | 21 + .../tailwindcss/npm/linux-x64-gnu/README.md | 3 + .../npm/linux-x64-gnu/package.json | 21 + .../tailwindcss/npm/linux-x64-musl/README.md | 3 + .../npm/linux-x64-musl/package.json | 18 + .../npm/win32-arm64-msvc/README.md | 3 + .../npm/win32-arm64-msvc/package.json | 18 + .../tailwindcss/npm/win32-ia32-msvc/README.md | 3 + .../npm/win32-ia32-msvc/package.json | 18 + .../tailwindcss/npm/win32-x64-msvc/README.md | 3 + .../npm/win32-x64-msvc/package.json | 18 + packages/tailwindcss/options.d.ts | 3 + packages/tailwindcss/package.json | 43 + packages/tailwindcss/playground/README.md | 37 + .../tailwindcss/playground/farm.config.ts | 17 + packages/tailwindcss/playground/index.css | 65 + packages/tailwindcss/playground/index.html | 16 + packages/tailwindcss/playground/index.js | 1 + packages/tailwindcss/playground/package.json | 30 + .../tailwindcss/playground/postcss.config.js | 6 + .../tailwindcss/playground/public/favicon.ico | Bin 0 -> 4154 bytes .../playground/src/assets/logo.png | Bin 0 -> 16859 bytes .../playground/src/assets/react.svg | 1 + packages/tailwindcss/playground/src/index.css | 69 + packages/tailwindcss/playground/src/index.tsx | 10 + packages/tailwindcss/playground/src/main.css | 42 + packages/tailwindcss/playground/src/main.tsx | 32 + .../tailwindcss/playground/src/typings.d.ts | 3 + .../tailwindcss/playground/tailwind.config.js | 13 + packages/tailwindcss/playground/tsconfig.json | 25 + .../tailwindcss/playground/tsconfig.node.json | 11 + packages/tailwindcss/pnpm-lock.yaml | 3470 +++++++++++++++++ packages/tailwindcss/pnpm-workspace.yaml | 3 + packages/tailwindcss/scripts/func.js | 3 + packages/tailwindcss/scripts/index.d.ts | 3 + packages/tailwindcss/scripts/index.js | 124 + packages/tailwindcss/scripts/watch.sh | 3 + packages/tailwindcss/src/lib.rs | 58 + packages/tailwindcss/src/test.css | 0 pnpm-lock.yaml | 6 + 71 files changed, 9086 insertions(+), 5 deletions(-) create mode 100644 packages/tailwindcss/.github/workflows/build.yaml create mode 100644 packages/tailwindcss/.github/workflows/ci.yaml create mode 100644 packages/tailwindcss/.github/workflows/release.yml create mode 100644 packages/tailwindcss/.gitignore create mode 100644 packages/tailwindcss/Cargo.toml create mode 100644 packages/tailwindcss/crates/oxide/Cargo.toml create mode 100644 packages/tailwindcss/crates/oxide/fuzz/.gitignore create mode 100644 packages/tailwindcss/crates/oxide/fuzz/Cargo.lock create mode 100644 packages/tailwindcss/crates/oxide/fuzz/Cargo.toml create mode 100644 packages/tailwindcss/crates/oxide/fuzz/fuzz_targets/parsing.rs create mode 100644 packages/tailwindcss/crates/oxide/src/cursor.rs create mode 100644 packages/tailwindcss/crates/oxide/src/fast_skip.rs create mode 100644 packages/tailwindcss/crates/oxide/src/glob.rs create mode 100644 packages/tailwindcss/crates/oxide/src/lib.rs create mode 100644 packages/tailwindcss/crates/oxide/src/parser.rs create mode 100644 packages/tailwindcss/crates/oxide/src/scanner/allowed_paths.rs create mode 100644 packages/tailwindcss/crates/oxide/src/scanner/detect_sources.rs create mode 100644 packages/tailwindcss/crates/oxide/src/scanner/fixtures/binary-extensions.txt create mode 100644 packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-extensions.txt create mode 100644 packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-files.txt create mode 100644 packages/tailwindcss/crates/oxide/src/scanner/fixtures/template-extensions.txt create mode 100644 packages/tailwindcss/crates/oxide/src/scanner/mod.rs create mode 100644 packages/tailwindcss/crates/oxide/tests/scanner.rs create mode 100644 packages/tailwindcss/npm/darwin-arm64/README.md create mode 100644 packages/tailwindcss/npm/darwin-arm64/package.json create mode 100644 packages/tailwindcss/npm/darwin-x64/README.md create mode 100644 packages/tailwindcss/npm/darwin-x64/package.json create mode 100644 packages/tailwindcss/npm/linux-arm64-gnu/README.md create mode 100644 packages/tailwindcss/npm/linux-arm64-gnu/package.json create mode 100644 packages/tailwindcss/npm/linux-arm64-musl/README.md create mode 100644 packages/tailwindcss/npm/linux-arm64-musl/package.json create mode 100644 packages/tailwindcss/npm/linux-x64-gnu/README.md create mode 100644 packages/tailwindcss/npm/linux-x64-gnu/package.json create mode 100644 packages/tailwindcss/npm/linux-x64-musl/README.md create mode 100644 packages/tailwindcss/npm/linux-x64-musl/package.json create mode 100644 packages/tailwindcss/npm/win32-arm64-msvc/README.md create mode 100644 packages/tailwindcss/npm/win32-arm64-msvc/package.json create mode 100644 packages/tailwindcss/npm/win32-ia32-msvc/README.md create mode 100644 packages/tailwindcss/npm/win32-ia32-msvc/package.json create mode 100644 packages/tailwindcss/npm/win32-x64-msvc/README.md create mode 100644 packages/tailwindcss/npm/win32-x64-msvc/package.json create mode 100644 packages/tailwindcss/options.d.ts create mode 100644 packages/tailwindcss/package.json create mode 100644 packages/tailwindcss/playground/README.md create mode 100644 packages/tailwindcss/playground/farm.config.ts create mode 100644 packages/tailwindcss/playground/index.css create mode 100644 packages/tailwindcss/playground/index.html create mode 100644 packages/tailwindcss/playground/index.js create mode 100644 packages/tailwindcss/playground/package.json create mode 100644 packages/tailwindcss/playground/postcss.config.js create mode 100644 packages/tailwindcss/playground/public/favicon.ico create mode 100644 packages/tailwindcss/playground/src/assets/logo.png create mode 100644 packages/tailwindcss/playground/src/assets/react.svg create mode 100644 packages/tailwindcss/playground/src/index.css create mode 100644 packages/tailwindcss/playground/src/index.tsx create mode 100644 packages/tailwindcss/playground/src/main.css create mode 100644 packages/tailwindcss/playground/src/main.tsx create mode 100644 packages/tailwindcss/playground/src/typings.d.ts create mode 100644 packages/tailwindcss/playground/tailwind.config.js create mode 100644 packages/tailwindcss/playground/tsconfig.json create mode 100644 packages/tailwindcss/playground/tsconfig.node.json create mode 100644 packages/tailwindcss/pnpm-lock.yaml create mode 100644 packages/tailwindcss/pnpm-workspace.yaml create mode 100644 packages/tailwindcss/scripts/func.js create mode 100644 packages/tailwindcss/scripts/index.d.ts create mode 100644 packages/tailwindcss/scripts/index.js create mode 100644 packages/tailwindcss/scripts/watch.sh create mode 100644 packages/tailwindcss/src/lib.rs create mode 100644 packages/tailwindcss/src/test.css diff --git a/Cargo.lock b/Cargo.lock index 511ca3e..1afb043 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", + "regex-automata 0.4.7", "serde", ] @@ -510,6 +511,28 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403" +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -529,6 +552,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -545,6 +577,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "css-color" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42aaeae719fd78ce501d77c6cdf01f7e96f26bcd5617a4903a1c2b97e388543a" + [[package]] name = "csv" version = "1.3.0" @@ -631,6 +669,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.13.0" @@ -676,6 +720,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "farmfe_core" version = "0.6.4" @@ -807,6 +861,18 @@ dependencies = [ "serde", ] +[[package]] +name = "farmfe_plugin_tailwindcss" +version = "0.0.1" +dependencies = [ + "farmfe_core", + "farmfe_macro_plugin", + "farmfe_toolkit_plugin_types", + "tailwind-ast", + "tailwind-css", + "tailwindcss-oxide", +] + [[package]] name = "farmfe_plugin_url" version = "0.0.1" @@ -912,6 +978,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "fervid" version = "0.2.0" @@ -1106,6 +1178,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "glob-match" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985c9503b412198aa4197559e9a318524ebc4519c229bfa05a535828c950b9d" + [[package]] name = "globset" version = "0.4.14" @@ -1115,8 +1193,19 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags 1.3.2", + "ignore", + "walkdir", ] [[package]] @@ -1211,6 +1300,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.7", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -1285,6 +1390,15 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.11.0" @@ -1431,6 +1545,12 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "lock_api" version = "0.4.12" @@ -1447,6 +1567,15 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1568,6 +1697,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -1634,6 +1773,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "owo-colors" version = "4.0.0" @@ -1998,8 +2143,17 @@ checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -2010,9 +2164,15 @@ checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.4", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.4" @@ -2129,6 +2289,19 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -2265,6 +2438,15 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -4126,6 +4308,59 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tailwind-ast" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac41127bfd988693b7beaa0a49de45bd452ebeb3bafec2387a4de25bdca6420e" +dependencies = [ + "nom", +] + +[[package]] +name = "tailwind-css" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f319cee4a7ae6c915292d55d6d7ef10266512dec123283921055c6cd7562f1a5" +dependencies = [ + "css-color", + "itertools 0.10.5", + "log", + "nom", + "tailwind-ast", + "tailwind-error", + "xxhash-rust", +] + +[[package]] +name = "tailwind-error" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6223cefeaba484fd356eec1f7e31ea3eb18e0348a0fc6222f877e0d2a76b76b6" +dependencies = [ + "css-color", + "nom", +] + +[[package]] +name = "tailwindcss-oxide" +version = "0.1.0" +dependencies = [ + "bstr", + "crossbeam", + "dunce", + "fxhash", + "glob-match", + "globwalk", + "ignore", + "log", + "rayon", + "tempfile", + "tracing", + "tracing-subscriber", + "walkdir", +] + [[package]] name = "tap" version = "1.0.1" @@ -4142,6 +4377,19 @@ dependencies = [ "vec1", ] +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "textwrap" version = "0.16.1" @@ -4183,6 +4431,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.36" @@ -4288,6 +4546,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -4401,6 +4689,12 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vec1" version = "1.12.1" @@ -4585,6 +4879,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -4730,6 +5033,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "xxhash-rust" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a5cbf750400958819fb6178eaa83bee5cd9c29a26a40cc241df8c70fdd46984" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/packages/tailwindcss/.github/workflows/build.yaml b/packages/tailwindcss/.github/workflows/build.yaml new file mode 100644 index 0000000..7bf4898 --- /dev/null +++ b/packages/tailwindcss/.github/workflows/build.yaml @@ -0,0 +1,112 @@ +name: Building Rust Binding And Upload Artifacts +on: workflow_call + +jobs: + build: + name: Build and Upload Artifacts - ${{ matrix.settings.abi }} + runs-on: ${{ matrix.settings.os }} + strategy: + fail-fast: false + matrix: + settings: + - os: ubuntu-latest + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian + abi: linux-x64-gnu + build: >- + set -e && + unset CC_x86_64_unknown_linux_gnu && + unset CC && + npm run build -- --target x86_64-unknown-linux-gnu --abi linux-x64-gnu + - os: ubuntu-latest + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + abi: linux-x64-musl + build: >- + set -e && + unset CC_x86_64_unknown_linux_musl && + unset CC && + npm run build -- --target x86_64-unknown-linux-musl --abi linux-x64-musl + - os: windows-latest + abi: win32-x64-msvc + - os: macos-13 + abi: darwin-x64 + - os: macos-latest + abi: darwin-arm64 + + # cross compile + # windows. Note swc plugins is not supported on ia32 and arm64 + - os: windows-latest + abi: win32-ia32-msvc + target: i686-pc-windows-msvc + build: | + export CARGO_PROFILE_RELEASE_LTO=false + cargo install --locked cargo-xwin + npm run build -- --target i686-pc-windows-msvc --abi win32-ia32-msvc --cargo-flags="--no-default-features" + - os: windows-latest + abi: win32-arm64-msvc + target: aarch64-pc-windows-msvc + build: | + export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=256 + export CARGO_PROFILE_RELEASE_LTO=false + cargo install --locked cargo-xwin + npm run build -- --target aarch64-pc-windows-msvc --abi win32-arm64-msvc --cargo-flags="--no-default-features" + # linux + - os: ubuntu-latest + abi: linux-arm64-musl + target: aarch64-unknown-linux-musl + zig: true + - os: ubuntu-latest + abi: linux-arm64-gnu + target: aarch64-unknown-linux-gnu + zig: true + # - os: ubuntu-latest + # abi: darwin-x64 + # target: x86_64-apple-darwin + # osxcross: true + # zig: true + # - os: ubuntu-latest + # abi: darwin-arm64 + # target: aarch64-apple-darwin + # osxcross: true + # zig: true + steps: + - uses: actions/checkout@v3 + - name: Cache rust artifacts + uses: Swatinem/rust-cache@v2 + with: + shared-key: rust-build-${{ matrix.settings.abi }} + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install Dependencies + run: npm install -g pnpm@9.1.0 && pnpm i --frozen-lockfile + - run: rustup target add ${{ matrix.settings.target }} + if: ${{ matrix.settings.target }} + # Use the v1 of this action + - uses: mbround18/setup-osxcross@v1 + if: ${{ matrix.settings.osxcross }} + # This builds executables & sets env variables for rust to consume. + with: + osx-version: '12.3' + - uses: goto-bus-stop/setup-zig@v2 + if: ${{ matrix.settings.zig }} + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} + with: + image: ${{ matrix.settings.docker }} + options: -v ${{ env.HOME }}/.cargo/git:/root/.cargo/git -v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry -v ${{ github.workspace }}:/build -w /build + run: ${{ matrix.settings.build }} + - name: Default Build + if: ${{ !matrix.settings.docker && !matrix.settings.build }} + run: >- + npm run build -- --abi ${{ matrix.settings.abi }} ${{ matrix.settings.target && format('--target {0}', matrix.settings.target) || '' }} ${{ matrix.settings.zig && '--zig' || '' }} + shell: bash + - name: Build + if: ${{ !matrix.settings.docker && matrix.settings.build }} + run: ${{ matrix.settings.build }} + shell: bash + - name: Upload Plugin + uses: actions/upload-artifact@v3 + with: + name: ${{ github.sha }}-${{ matrix.settings.abi }}-plugin + path: npm/${{ matrix.settings.abi }}/index.farm diff --git a/packages/tailwindcss/.github/workflows/ci.yaml b/packages/tailwindcss/.github/workflows/ci.yaml new file mode 100644 index 0000000..5f229a8 --- /dev/null +++ b/packages/tailwindcss/.github/workflows/ci.yaml @@ -0,0 +1,36 @@ +name: Test Plugin +on: + pull_request: + branches: + - main + +jobs: + call-rust-build: + uses: ./.github/workflows/build.yaml + + test-artifacts: + name: Test Artifacts + needs: [call-rust-build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + # batch download artifacts + - uses: actions/download-artifact@v3 + with: + path: /tmp/artifacts + - name: Check Artifacts + run: | + for abi in linux-x64-gnu linux-x64-musl darwin-x64 win32-x64-msvc linux-arm64-musl linux-arm64-gnu darwin-arm64 win32-ia32-msvc win32-arm64-msvc + do + mv /tmp/artifacts/${{ github.sha }}-${abi}-plugin/* ./npm/${abi} + + test -f ./npm/${abi}/index.farm + done + - name: Setup Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + - name: Install Dependencies + run: npm install -g pnpm@9.1.0 + - name: Test Example + run: cd playground && pnpm i && pnpm build \ No newline at end of file diff --git a/packages/tailwindcss/.github/workflows/release.yml b/packages/tailwindcss/.github/workflows/release.yml new file mode 100644 index 0000000..cba1513 --- /dev/null +++ b/packages/tailwindcss/.github/workflows/release.yml @@ -0,0 +1,43 @@ +name: Publish packages and crates +on: + push: + branches: + - main + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + call-rust-build: + uses: ./.github/workflows/build.yaml + + release: + name: Release + needs: [call-rust-build] + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v3 + + - name: Setup Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + + # batch download artifacts + - uses: actions/download-artifact@v3 + with: + path: /tmp/artifacts + - name: Move Artifacts + run: | + for abi in linux-x64-gnu linux-x64-musl darwin-x64 win32-x64-msvc linux-arm64-musl linux-arm64-gnu darwin-arm64 win32-ia32-msvc win32-arm64-msvc + do + mv /tmp/artifacts/${{ github.sha }}-${abi}-plugin/* ./npm/${abi} + + test -f ./npm/${abi}/index.farm + done + + - name: Install Dependencies + run: npm install -g pnpm@9.1.0 && pnpm i --frozen-lockfile + + - name: Publish to npm + run: npm set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }} && npm publish \ No newline at end of file diff --git a/packages/tailwindcss/.gitignore b/packages/tailwindcss/.gitignore new file mode 100644 index 0000000..50e4f22 --- /dev/null +++ b/packages/tailwindcss/.gitignore @@ -0,0 +1,197 @@ +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# End of https://www.toptal.com/developers/gitignore/api/node + +# Created by https://www.toptal.com/developers/gitignore/api/macos +# Edit at https://www.toptal.com/developers/gitignore?templates=macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +# End of https://www.toptal.com/developers/gitignore/api/macos + +# Created by https://www.toptal.com/developers/gitignore/api/windows +# Edit at https://www.toptal.com/developers/gitignore?templates=windows + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows + +#Added by cargo + +/target +# Cargo.lock + +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +*.farm diff --git a/packages/tailwindcss/Cargo.toml b/packages/tailwindcss/Cargo.toml new file mode 100644 index 0000000..3544919 --- /dev/null +++ b/packages/tailwindcss/Cargo.toml @@ -0,0 +1,15 @@ +[package] +edition = "2021" +name = "farmfe_plugin_tailwindcss" +version = "0.0.1" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +farmfe_toolkit_plugin_types = { workspace = true } +farmfe_macro_plugin = { workspace = true } +farmfe_core = { workspace = true } +tailwind-css = "0.13.0" +tailwind-ast = "0.4.1" +tailwindcss-oxide = { path = "./crates/oxide" } diff --git a/packages/tailwindcss/crates/oxide/Cargo.toml b/packages/tailwindcss/crates/oxide/Cargo.toml new file mode 100644 index 0000000..063d1a2 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "tailwindcss-oxide" +version = "0.1.0" +edition = "2021" + +[dependencies] +bstr = "1.0.1" +globwalk = "0.8.1" +log = "0.4" +rayon = "1.5.3" +fxhash = "0.2.1" +crossbeam = "0.8.2" +tracing = { version = "0.1.37", features = [] } +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } +walkdir = "2.3.3" +ignore = "0.4.20" +glob-match = "0.2.1" +dunce = "1.0.5" + +[dev-dependencies] +tempfile = "3.5.0" + diff --git a/packages/tailwindcss/crates/oxide/fuzz/.gitignore b/packages/tailwindcss/crates/oxide/fuzz/.gitignore new file mode 100644 index 0000000..1a45eee --- /dev/null +++ b/packages/tailwindcss/crates/oxide/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/packages/tailwindcss/crates/oxide/fuzz/Cargo.lock b/packages/tailwindcss/crates/oxide/fuzz/Cargo.lock new file mode 100644 index 0000000..7c7ebc7 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/fuzz/Cargo.lock @@ -0,0 +1,531 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "arbitrary" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29d47fbf90d5149a107494b15a7dc8d69b351be2db3bb9691740e88ec17fd880" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +dependencies = [ + "memchr", + "regex-automata 0.4.5", + "serde", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581f5dba903aac52ea3feb5ec4810848460ee833876f1f9b0fdeab1f19091574" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata 0.4.5", + "regex-syntax 0.8.2", +] + +[[package]] +name = "globwalk" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc" +dependencies = [ + "bitflags", + "ignore", + "walkdir", +] + +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata 0.4.5", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fff891139ee62800da71b7fd5b508d570b9ad95e614a53c6f453ca08366038" +dependencies = [ + "arbitrary", + "cc", + "once_cell", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "regex-syntax 0.6.28", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.28", +] + +[[package]] +name = "regex-automata" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.196" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tailwindcss-oxide" +version = "0.1.0" +dependencies = [ + "bstr", + "crossbeam", + "fxhash", + "globwalk", + "ignore", + "lazy_static", + "log", + "rayon", + "tracing", + "tracing-subscriber", + "walkdir", +] + +[[package]] +name = "tailwindcss-oxide-fuzz" +version = "0.0.0" +dependencies = [ + "libfuzzer-sys", + "tailwindcss-oxide", +] + +[[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", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/packages/tailwindcss/crates/oxide/fuzz/Cargo.toml b/packages/tailwindcss/crates/oxide/fuzz/Cargo.toml new file mode 100644 index 0000000..b402014 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/fuzz/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "tailwindcss-oxide-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" + +[dependencies.tailwindcss-oxide] +path = ".." + +# Prevent this from interfering with workspaces +[workspace] +members = ["."] + +[profile.release] +debug = 1 + +[[bin]] +name = "parsing" +path = "fuzz_targets/parsing.rs" +test = false +doc = false diff --git a/packages/tailwindcss/crates/oxide/fuzz/fuzz_targets/parsing.rs b/packages/tailwindcss/crates/oxide/fuzz/fuzz_targets/parsing.rs new file mode 100644 index 0000000..02935a2 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/fuzz/fuzz_targets/parsing.rs @@ -0,0 +1,31 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use std::path::PathBuf; +use tailwindcss_oxide::candidate::scan_files; +use tailwindcss_oxide::candidate::Candidate; +use tailwindcss_oxide::location::Location; + +// fuzz_target!(|data: &[u8]| { +// if let Ok(s) = std::str::from_utf8(data) { +// let _ = parse_candidate_strings(s, false); +// } +// }); + +fuzz_target!(|data: &[u8]| { + if let Ok(s) = std::str::from_utf8(data) { + let _ = scan_files(s, false) + .into_iter() + .map(|(c, _)| { + Candidate::new( + c, + Location { + file: PathBuf::new(), + start: (0, 1), + end: (0, 1), + }, + ) + }) + .collect::>(); + } +}); diff --git a/packages/tailwindcss/crates/oxide/src/cursor.rs b/packages/tailwindcss/crates/oxide/src/cursor.rs new file mode 100644 index 0000000..0c422c6 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/cursor.rs @@ -0,0 +1,159 @@ +use std::{ascii::escape_default, fmt::Display}; + +#[derive(Debug, Clone)] +pub struct Cursor<'a> { + // The input we're scanning + pub input: &'a [u8], + + // The location of the cursor in the input + pub pos: usize, + + /// Is the cursor at the start of the input + pub at_start: bool, + + /// Is the cursor at the end of the input + pub at_end: bool, + + /// The previously consumed character + /// If `at_start` is true, this will be NUL + pub prev: u8, + + /// The current character + pub curr: u8, + + /// The upcoming character (if any) + /// If `at_end` is true, this will be NUL + pub next: u8, +} + +impl<'a> Cursor<'a> { + pub fn new(input: &'a [u8]) -> Self { + let mut cursor = Self { + input, + pos: 0, + at_start: true, + at_end: false, + prev: 0x00, + curr: 0x00, + next: 0x00, + }; + cursor.move_to(0); + cursor + } + + pub fn rewind_by(&mut self, amount: usize) { + self.move_to(self.pos.saturating_sub(amount)); + } + + pub fn advance_by(&mut self, amount: usize) { + self.move_to(self.pos.saturating_add(amount)); + } + + pub fn move_to(&mut self, pos: usize) { + let len = self.input.len(); + let pos = pos.clamp(0, len); + + self.pos = pos; + self.at_start = pos == 0; + self.at_end = pos + 1 >= len; + + self.prev = if pos > 0 { self.input[pos - 1] } else { 0x00 }; + self.curr = if pos < len { self.input[pos] } else { 0x00 }; + self.next = if pos + 1 < len { + self.input[pos + 1] + } else { + 0x00 + }; + } +} + +impl<'a> Display for Cursor<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let len = self.input.len().to_string(); + + let pos = format!("{: >len_count$}", self.pos, len_count = len.len()); + write!(f, "{}/{} ", pos, len)?; + + if self.at_start { + write!(f, "S ")?; + } else if self.at_end { + write!(f, "E ")?; + } else { + write!(f, "M ")?; + } + + fn to_str(c: u8) -> String { + if c == 0x00 { + "NUL".into() + } else { + format!("{:?}", escape_default(c).to_string()) + } + } + + write!( + f, + "[{} {} {}]", + to_str(self.prev), + to_str(self.curr), + to_str(self.next) + ) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_cursor() { + let mut cursor = Cursor::new(b"hello world"); + assert_eq!(cursor.pos, 0); + assert!(cursor.at_start); + assert!(!cursor.at_end); + assert_eq!(cursor.prev, 0x00); + assert_eq!(cursor.curr, b'h'); + assert_eq!(cursor.next, b'e'); + + cursor.advance_by(1); + assert_eq!(cursor.pos, 1); + assert!(!cursor.at_start); + assert!(!cursor.at_end); + assert_eq!(cursor.prev, b'h'); + assert_eq!(cursor.curr, b'e'); + assert_eq!(cursor.next, b'l'); + + // Advancing too far should stop at the end + cursor.advance_by(10); + assert_eq!(cursor.pos, 11); + assert!(!cursor.at_start); + assert!(cursor.at_end); + assert_eq!(cursor.prev, b'd'); + assert_eq!(cursor.curr, 0x00); + assert_eq!(cursor.next, 0x00); + + // Can't advance past the end + cursor.advance_by(1); + assert_eq!(cursor.pos, 11); + assert!(!cursor.at_start); + assert!(cursor.at_end); + assert_eq!(cursor.prev, b'd'); + assert_eq!(cursor.curr, 0x00); + assert_eq!(cursor.next, 0x00); + + cursor.rewind_by(1); + assert_eq!(cursor.pos, 10); + assert!(!cursor.at_start); + assert!(cursor.at_end); + assert_eq!(cursor.prev, b'l'); + assert_eq!(cursor.curr, b'd'); + assert_eq!(cursor.next, 0x00); + + cursor.rewind_by(10); + assert_eq!(cursor.pos, 0); + assert!(cursor.at_start); + assert!(!cursor.at_end); + assert_eq!(cursor.prev, 0x00); + assert_eq!(cursor.curr, b'h'); + assert_eq!(cursor.next, b'e'); + } +} diff --git a/packages/tailwindcss/crates/oxide/src/fast_skip.rs b/packages/tailwindcss/crates/oxide/src/fast_skip.rs new file mode 100644 index 0000000..488e61b --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/fast_skip.rs @@ -0,0 +1,89 @@ +use crate::cursor::Cursor; + +const STRIDE: usize = 16; +type Mask = [bool; STRIDE]; + +#[inline(always)] +pub fn fast_skip(cursor: &Cursor) -> Option { + // If we don't have enough bytes left to check then bail early + if cursor.pos + STRIDE >= cursor.input.len() { + return None; + } + + if !cursor.curr.is_ascii_whitespace() { + return None; + } + + let mut offset = 1; + + // SAFETY: We've already checked (indirectly) that this index is valid + let remaining = unsafe { cursor.input.get_unchecked(cursor.pos..) }; + + // NOTE: This loop uses primitives designed to be auto-vectorized + // Do not change this loop without benchmarking the results + // And checking the generated assembly using godbolt.org + for (i, chunk) in remaining.chunks_exact(STRIDE).enumerate() { + let value = load(chunk); + let is_whitespace = is_ascii_whitespace(value); + let is_all_whitespace = all_true(is_whitespace); + + if is_all_whitespace { + offset = (i + 1) * STRIDE; + } else { + break; + } + } + + Some(cursor.pos + offset) +} + +#[inline(always)] +fn load(input: &[u8]) -> [u8; STRIDE] { + let mut value = [0u8; STRIDE]; + value.copy_from_slice(input); + value +} + +#[inline(always)] +fn eq(input: [u8; STRIDE], val: u8) -> Mask { + let mut res = [false; STRIDE]; + for n in 0..STRIDE { + res[n] = input[n] == val + } + res +} + +#[inline(always)] +fn or(a: [bool; STRIDE], b: [bool; STRIDE]) -> [bool; STRIDE] { + let mut res = [false; STRIDE]; + for n in 0..STRIDE { + res[n] = a[n] | b[n]; + } + res +} + +#[inline(always)] +fn all_true(a: [bool; STRIDE]) -> bool { + let mut res = true; + for item in a.iter().take(STRIDE) { + res &= item; + } + res +} + +#[inline(always)] +fn is_ascii_whitespace(value: [u8; STRIDE]) -> [bool; STRIDE] { + let whitespace_1 = eq(value, b'\t'); + let whitespace_2 = eq(value, b'\n'); + let whitespace_3 = eq(value, b'\x0C'); + let whitespace_4 = eq(value, b'\r'); + let whitespace_5 = eq(value, b' '); + + or( + or( + or(or(whitespace_1, whitespace_2), whitespace_3), + whitespace_4, + ), + whitespace_5, + ) +} diff --git a/packages/tailwindcss/crates/oxide/src/glob.rs b/packages/tailwindcss/crates/oxide/src/glob.rs new file mode 100644 index 0000000..d74b60b --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/glob.rs @@ -0,0 +1,479 @@ +use glob_match::glob_match; +use std::iter; +use std::path::{Path, PathBuf}; + +use crate::GlobEntry; + +pub fn fast_glob( + patterns: &Vec, +) -> Result, std::io::Error> { + Ok(get_fast_patterns(patterns) + .into_iter() + .flat_map(|(base_path, patterns)| { + globwalk::GlobWalkerBuilder::from_patterns(base_path, &patterns) + .follow_links(true) + .build() + .unwrap() + .filter_map(Result::ok) + .map(|file| file.path().to_path_buf()) + })) +} + +/// This function attempts to optimize the glob patterns to improve performance. The problem is +/// that if you run the following command: +/// ```sh +/// tailwind --pwd ./project --content "{pages,components}/**/*.js" +/// ``` +/// Then the globwalk library will scan every single file and folder in the `./project` folder, +/// then it will check if the file matches the glob pattern and keep it if it does. This is very +/// slow, because if you have vendor folders (like node_modules), then this will take a while... +/// +/// Instead, we will optimize the pattern, and move as many directories as possible to the base +/// path. This will allow us to scope the globwalk library to only scan the directories that we +/// care about. +/// +/// This means, that the following command: +/// ```sh +/// tailwind --pwd ./project --content "{pages,components}/**/*.js" +/// ``` +/// +/// Will now conceptually do this instead behind the scenes: +/// ```sh +/// tailwind --pwd ./project/pages --content "**/*.js" +/// tailwind --pwd ./project/components --content "**/*.js" +/// ``` +pub fn get_fast_patterns(patterns: &Vec) -> Vec<(PathBuf, Vec)> { + let mut optimized_patterns: Vec<(PathBuf, Vec)> = vec![]; + + for pattern in patterns { + let base_path = PathBuf::from(&pattern.base); + let pattern = &pattern.pattern; + + let is_negated = pattern.starts_with('!'); + let mut pattern = pattern.clone(); + if is_negated { + pattern.remove(0); + } + + let mut folders = pattern.split('/').collect::>(); + + if folders.len() <= 1 { + // No paths we can simplify, so let's use it as-is. + optimized_patterns.push((base_path, vec![pattern])); + } else { + // We do have folders because `/` exists. Let's try to simplify the globs! + // Safety: We know that the length is greater than 1, so we can safely unwrap. + let file_pattern = folders.pop().unwrap(); + let all_folders = folders.clone(); + let mut temp_paths = vec![base_path]; + + let mut bail = false; + + for (i, folder) in folders.into_iter().enumerate() { + // There is a wildcard in the folder, so we have to bail now... 😢 But this also + // means that we can skip looking at the rest of the folders, so there is at least + // this small optimization we can apply! + if folder.contains('*') { + // Get all the remaining folders, attach the existing file_pattern so that this + // can now be the final pattern we use. + let mut remaining_folders = all_folders[i..].to_vec(); + remaining_folders.push(file_pattern); + + let pattern = remaining_folders.join("/"); + for path in &temp_paths { + optimized_patterns.push((path.to_path_buf(), vec![pattern.to_string()])); + } + + bail = true; + break; + } + + // The folder is very likely using an expandable pattern which we can expand! + if folder.contains('{') && folder.contains('}') { + let branches = expand_braces(folder); + + let existing_paths = temp_paths; + temp_paths = branches + .iter() + .flat_map(|branch| { + existing_paths + .clone() + .into_iter() + .map(|path| path.join(branch)) + .collect::>() + }) + .collect::>(); + } + // The folder should just be a simple folder name without any glob magic. We should + // be able to safely add it to the existing paths. + else { + temp_paths = temp_paths + .into_iter() + .map(|path| path.join(folder)) + .collect(); + } + } + + // As long as we didn't bail, we can now add the current expanded patterns to the + // optimized patterns. + if !bail { + for path in &temp_paths { + optimized_patterns.push((path.to_path_buf(), vec![file_pattern.to_string()])); + } + } + } + + // Ensure that we re-add all the `!` signs to the patterns. + if is_negated { + for (_, patterns) in &mut optimized_patterns { + for pattern in patterns { + pattern.insert(0, '!'); + } + } + } + } + + optimized_patterns +} + +pub fn path_matches_globs(path: &Path, globs: &[GlobEntry]) -> bool { + let path = path.to_string_lossy(); + + globs + .iter() + .any(|g| glob_match(&format!("{}/{}", g.base, g.pattern), &path)) +} + +/// Given this input: a-{b,c}-d-{e,f} +/// We will get: +/// [ +/// a-b-d-e +/// a-b-d-f +/// a-c-d-e +/// a-c-d-f +/// ] +/// TODO: There is probably a way nicer way of doing this, but this works for now. +fn expand_braces(input: &str) -> Vec { + let mut result: Vec = vec![]; + + let mut in_braces = false; + let mut last_char: char = '\0'; + + let mut current = String::new(); + + // Given the input: a-{b,c}-d-{e,f}-g + // The template will look like this: ["a-", "-d-", "g"]. + let mut template: Vec = vec![]; + + // The branches will look like this: [["b", "c"], ["e", "f"]]. + let mut branches: Vec> = vec![]; + + for (i, c) in input.char_indices() { + let is_escaped = i > 0 && last_char == '\\'; + last_char = c; + + match c { + '{' if !is_escaped => { + // Ensure that when a new set of braces is opened, that we at least have 1 + // template. + if template.is_empty() { + template.push(String::new()); + } + + in_braces = true; + branches.push(vec![]); + template.push(String::new()); + } + '}' if !is_escaped => { + in_braces = false; + if let Some(last) = branches.last_mut() { + last.push(current.clone()); + } + current.clear(); + } + ',' if !is_escaped && in_braces => { + if let Some(last) = branches.last_mut() { + last.push(current.clone()); + } + current.clear(); + } + _ if in_braces => current.push(c), + _ => { + if template.is_empty() { + template.push(String::new()); + } + + if let Some(last) = template.last_mut() { + last.push(c); + } + } + }; + } + + // Ensure we have a string that we can start adding information too. + if !template.is_empty() && !branches.is_empty() { + result.push("".to_string()); + } + + // Let's try to generate everything! + for (i, template) in template.into_iter().enumerate() { + // Append current template string to all existing results. + result = result.into_iter().map(|x| x + &template).collect(); + + // Get the results, and copy it for every single branch. + if let Some(branches) = branches.get(i) { + result = branches + .iter() + .flat_map(|branch| { + result + .clone() + .into_iter() + .map(|x| x + branch) + .collect::>() + }) + .collect::>(); + } + } + + result +} + +#[cfg(test)] +mod tests { + use super::get_fast_patterns; + use crate::GlobEntry; + use std::path::PathBuf; + + #[test] + fn it_should_keep_globs_that_start_with_file_wildcards_as_is() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "*.html".to_string(), + }]); + let expected = vec![(PathBuf::from("/projects"), vec!["*.html".to_string()])]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_keep_globs_that_start_with_folder_wildcards_as_is() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "**/*.html".to_string(), + }]); + + let expected = vec![(PathBuf::from("/projects"), vec!["**/*.html".to_string()])]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_move_the_starting_folder_to_the_path() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "example/*.html".to_string(), + }]); + let expected = vec![( + PathBuf::from("/projects/example"), + vec!["*.html".to_string()], + )]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_move_the_starting_folders_to_the_path() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "example/other/*.html".to_string(), + }]); + let expected = vec![( + PathBuf::from("/projects/example/other"), + vec!["*.html".to_string()], + )]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_branch_expandable_folders() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "{foo,bar}/*.html".to_string(), + }]); + + let expected = vec![ + (PathBuf::from("/projects/foo"), vec!["*.html".to_string()]), + (PathBuf::from("/projects/bar"), vec!["*.html".to_string()]), + ]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_expand_multiple_expansions_in_the_same_folder() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "a-{b,c}-d-{e,f}-g/*.html".to_string(), + }]); + let expected = vec![ + ( + PathBuf::from("/projects/a-b-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-b-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-d-f-g"), + vec!["*.html".to_string()], + ), + ]; + + assert_eq!(actual, expected,); + } + + #[test] + fn multiple_expansions_per_folder_starting_at_the_root() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "{a,b}-c-{d,e}-f/{b,c}-d-{e,f}-g/*.html".to_string(), + }]); + let expected = vec![ + ( + PathBuf::from("/projects/a-c-d-f/b-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-d-f/b-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-e-f/b-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-e-f/b-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-d-f/c-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-d-f/c-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-e-f/c-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-e-f/c-d-e-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-d-f/b-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-d-f/b-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-e-f/b-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-e-f/b-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-d-f/c-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-d-f/c-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a-c-e-f/c-d-f-g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/b-c-e-f/c-d-f-g"), + vec!["*.html".to_string()], + ), + ]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_stop_expanding_once_we_hit_a_wildcard() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "{foo,bar}/example/**/{baz,qux}/*.html".to_string(), + }]); + + let expected = vec![ + ( + PathBuf::from("/projects/foo/example"), + vec!["**/{baz,qux}/*.html".to_string()], + ), + ( + PathBuf::from("/projects/bar/example"), + vec!["**/{baz,qux}/*.html".to_string()], + ), + ]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_keep_the_negation_symbol_for_all_new_patterns() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "!{foo,bar}/*.html".to_string(), + }]); + let expected = vec![ + (PathBuf::from("/projects/foo"), vec!["!*.html".to_string()]), + (PathBuf::from("/projects/bar"), vec!["!*.html".to_string()]), + ]; + + assert_eq!(actual, expected,); + } + + #[test] + fn it_should_expand_a_complex_example() { + let actual = get_fast_patterns(&vec![GlobEntry { + base: "/projects".to_string(), + pattern: "a/{b,c}/d/{e,f}/g/*.html".to_string(), + }]); + let expected = vec![ + ( + PathBuf::from("/projects/a/b/d/e/g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a/c/d/e/g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a/b/d/f/g"), + vec!["*.html".to_string()], + ), + ( + PathBuf::from("/projects/a/c/d/f/g"), + vec!["*.html".to_string()], + ), + ]; + + assert_eq!(actual, expected,); + } +} diff --git a/packages/tailwindcss/crates/oxide/src/lib.rs b/packages/tailwindcss/crates/oxide/src/lib.rs new file mode 100644 index 0000000..ac6ab90 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/lib.rs @@ -0,0 +1,318 @@ +#![feature(lazy_cell)] +use crate::parser::Extractor; +use crate::scanner::detect_sources::DetectSources; +use bstr::ByteSlice; +use fxhash::{FxHashMap, FxHashSet}; +use glob::fast_glob; +use glob::get_fast_patterns; +use rayon::prelude::*; +use std::fs; +use std::path::PathBuf; +use std::sync; +use std::time::SystemTime; +use tracing::event; + +pub mod cursor; +pub mod fast_skip; +pub mod glob; +pub mod parser; +pub mod scanner; + +static SHOULD_TRACE: sync::LazyLock = sync::LazyLock::new( + || matches!(std::env::var("DEBUG"), Ok(value) if value.eq("*") || value.eq("1") || value.eq("true") || value.contains("tailwind")), +); + +fn init_tracing() { + if !*SHOULD_TRACE { + return; + } + + _ = tracing_subscriber::fmt() + .with_max_level(tracing::Level::INFO) + .with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE) + .compact() + .try_init(); +} + +#[derive(Debug, Clone)] +pub struct ChangedContent { + pub file: Option, + pub content: Option, +} + +#[derive(Debug, Clone)] +pub struct ScanOptions { + /// Base path to start scanning from + pub base: Option, + /// Glob sources + pub sources: Vec, +} + +#[derive(Debug, Clone)] +pub struct ScanResult { + pub candidates: Vec, + pub files: Vec, + pub globs: Vec, +} + +#[derive(Debug, Clone)] +pub struct GlobEntry { + pub base: String, + pub pattern: String, +} + +#[derive(Debug, Clone, Default)] +pub struct Scanner { + /// Auto content configuration + detect_sources: Option, + + /// Glob sources + sources: Option>, + + /// Scanner is ready to scan. We delay the file system traversal for detecting all files until + /// we actually need them. + ready: bool, + + /// All files that we have to scan + files: Vec, + + /// All generated globs + globs: Vec, + + /// Track file modification times + mtimes: FxHashMap, + + /// Track unique set of candidates + candidates: FxHashSet, +} + +impl Scanner { + pub fn new(detect_sources: Option, sources: Option>) -> Self { + Self { + detect_sources, + sources, + ..Default::default() + } + } + + pub fn scan(&mut self) -> Vec { + init_tracing(); + self.prepare(); + + self.compute_candidates(); + + let mut candidates: Vec = self.candidates.clone().into_iter().collect(); + + candidates.sort(); + + candidates + } + + #[tracing::instrument(skip_all)] + pub fn scan_content(&mut self, changed_content: Vec) -> Vec { + self.prepare(); + let candidates = parse_all_blobs(read_all_files(changed_content)); + + let mut new_candidates = vec![]; + for candidate in candidates { + if self.candidates.contains(&candidate) { + continue; + } + self.candidates.insert(candidate.clone()); + new_candidates.push(candidate); + } + + new_candidates + } + + #[tracing::instrument(skip_all)] + pub fn get_files(&mut self) -> Vec { + self.prepare(); + + self.files + .iter() + .map(|x| x.to_string_lossy().into()) + .collect() + } + + #[tracing::instrument(skip_all)] + pub fn get_globs(&mut self) -> Vec { + self.prepare(); + + self.globs.clone() + } + + #[tracing::instrument(skip_all)] + fn compute_candidates(&mut self) { + let mut changed_content = vec![]; + + for path in &self.files { + let current_time = fs::metadata(path) + .and_then(|m| m.modified()) + .unwrap_or(SystemTime::now()); + + let previous_time = self.mtimes.insert(path.clone(), current_time); + + let should_scan_file = match previous_time { + // Time has changed, so we need to re-scan the file + Some(prev) if prev != current_time => true, + + // File was in the cache, no need to re-scan + Some(_) => false, + + // File didn't exist before, so we need to scan it + None => true, + }; + + if should_scan_file { + changed_content.push(ChangedContent { + file: Some(path.clone()), + content: None, + }); + } + } + + if !changed_content.is_empty() { + let candidates = parse_all_blobs(read_all_files(changed_content)); + self.candidates.extend(candidates); + } + } + + // Ensures that all files/globs are resolved and the scanner is ready to scan + // content for candidates. + fn prepare(&mut self) { + if self.ready { + return; + } + + self.detect_sources(); + self.scan_sources(); + + self.ready = true; + } + + #[tracing::instrument(skip_all)] + fn detect_sources(&mut self) { + if let Some(detect_sources) = &self.detect_sources { + let (files, globs) = detect_sources.detect(); + self.files.extend(files); + self.globs.extend(globs); + } + } + + #[tracing::instrument(skip_all)] + fn scan_sources(&mut self) { + let Some(sources) = &self.sources else { + return; + }; + + if sources.is_empty() { + return; + } + + let resolved_files: Vec<_> = match fast_glob(sources) { + Ok(matches) => matches + .filter_map(|x| dunce::canonicalize(&x).ok()) + .collect(), + Err(err) => { + event!(tracing::Level::ERROR, "Failed to resolve glob: {:?}", err); + vec![] + } + }; + + self.files.extend(resolved_files); + self.globs.extend(sources.clone()); + + // Re-optimize the globs to reduce the number of patterns we have to scan. + self.globs = get_fast_patterns(&self.globs) + .into_iter() + .filter_map(|(root, globs)| { + let root = match dunce::canonicalize(root) { + Ok(root) => root, + Err(error) => { + event!( + tracing::Level::ERROR, + "Failed to canonicalize base path {:?}", + error + ); + return None; + } + }; + + Some((root, globs)) + }) + .flat_map(|(root, globs)| { + let base = root.display().to_string(); + + globs.into_iter().map(move |glob| GlobEntry { + base: base.clone(), + pattern: glob, + }) + }) + .collect::>(); + } +} + +fn read_changed_content(c: ChangedContent) -> Option> { + if let Some(content) = c.content { + return Some(content.into_bytes()); + } + + let Some(file) = c.file else { + return Default::default(); + }; + + let Ok(content) = std::fs::read(&file).map_err(|e| { + event!(tracing::Level::ERROR, "Failed to read file: {:?}", e); + e + }) else { + return Default::default(); + }; + + let Some(extension) = file.extension().map(|x| x.to_str()) else { + return Some(content); + }; + + match extension { + Some("svelte") => Some(content.replace(" class:", " ")), + _ => Some(content), + } +} + +#[tracing::instrument(skip_all)] +fn read_all_files(changed_content: Vec) -> Vec> { + event!( + tracing::Level::INFO, + "Reading {:?} file(s)", + changed_content.len() + ); + + changed_content + .into_par_iter() + .filter_map(read_changed_content) + .collect() +} + +#[tracing::instrument(skip_all)] +fn parse_all_blobs(blobs: Vec>) -> Vec { + let input: Vec<_> = blobs.iter().map(|blob| &blob[..]).collect(); + let input = &input[..]; + + let mut result: Vec = input + .par_iter() + .map(|input| Extractor::unique(input, Default::default())) + .reduce(Default::default, |mut a, b| { + a.extend(b); + a + }) + .into_iter() + .map(|s| { + // SAFETY: When we parsed the candidates, we already guaranteed that the byte slices + // are valid, therefore we don't have to re-check here when we want to convert it back + // to a string. + unsafe { String::from_utf8_unchecked(s.to_vec()) } + }) + .collect(); + result.sort(); + result +} diff --git a/packages/tailwindcss/crates/oxide/src/parser.rs b/packages/tailwindcss/crates/oxide/src/parser.rs new file mode 100644 index 0000000..ad612f9 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/parser.rs @@ -0,0 +1,1427 @@ +use crate::{cursor::Cursor, fast_skip::fast_skip}; +use bstr::ByteSlice; +use fxhash::FxHashSet; +use tracing::trace; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ParseAction<'a> { + Consume, + Skip, + RestartAt(usize), + + SingleCandidate(&'a [u8]), + MultipleCandidates(Vec<&'a [u8]>), + Done, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Bracketing<'a> { + Included(&'a [u8]), + Wrapped(&'a [u8]), + None, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct SplitCandidate<'a> { + variant: &'a [u8], + utility: &'a [u8], +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ValidationResult { + Invalid, + Valid, + Restart, +} + +#[derive(Default)] +pub struct ExtractorOptions { + pub preserve_spaces_in_arbitrary: bool, +} + +pub struct Extractor<'a> { + opts: ExtractorOptions, + + input: &'a [u8], + cursor: Cursor<'a>, + + idx_start: usize, + idx_end: usize, + idx_last: usize, + idx_arbitrary_start: usize, + + in_arbitrary: bool, + in_candidate: bool, + in_escape: bool, + + discard_next: bool, + + quote_stack: Vec, + bracket_stack: Vec, +} + +impl<'a> Extractor<'a> { + pub fn all(input: &'a [u8], opts: ExtractorOptions) -> Vec<&'a [u8]> { + Self::new(input, opts).flatten().collect() + } + + pub fn unique(input: &'a [u8], opts: ExtractorOptions) -> FxHashSet<&'a [u8]> { + let mut candidates: FxHashSet<&[u8]> = Default::default(); + candidates.reserve(100); + candidates.extend(Self::new(input, opts).flatten()); + candidates + } + + pub fn unique_ord(input: &'a [u8], opts: ExtractorOptions) -> Vec<&'a [u8]> { + // This is an inefficient way to get an ordered, unique + // list as a Vec but it is only meant for testing. + let mut candidates = Self::all(input, opts); + let mut unique_list = FxHashSet::default(); + unique_list.reserve(candidates.len()); + candidates.retain(|c| unique_list.insert(*c)); + + candidates + } +} + +impl<'a> Extractor<'a> { + pub fn new(input: &'a [u8], opts: ExtractorOptions) -> Self { + Self { + opts, + input, + cursor: Cursor::new(input), + + idx_start: 0, + idx_end: 0, + idx_arbitrary_start: 0, + + in_arbitrary: false, + in_candidate: false, + in_escape: false, + + discard_next: false, + + idx_last: input.len(), + quote_stack: Vec::with_capacity(8), + bracket_stack: Vec::with_capacity(8), + } + } +} + +/// Helpers +impl<'a> Extractor<'a> { + #[inline(always)] + fn in_quotes(&self) -> bool { + !self.quote_stack.is_empty() + } + + #[inline(always)] + fn get_current_candidate(&mut self) -> ParseAction<'a> { + if self.discard_next { + return ParseAction::Skip; + } + + let mut candidate = &self.input[self.idx_start..=self.idx_end]; + + // The bracket stack is not empty, which means that we are dealing with unbalanced + // brackets. + if !self.bracket_stack.is_empty() { + return ParseAction::Skip; + } + + while !candidate.is_empty() { + match Extractor::is_valid_candidate_string(candidate) { + ValidationResult::Valid => return ParseAction::SingleCandidate(candidate), + ValidationResult::Restart => return ParseAction::RestartAt(self.idx_start + 1), + _ => {} + } + + match candidate.split_last() { + // At this point the candidate is technically invalid, however it can be that it + // has a few dangling characters attached to it. For example, think about a + // JavaScript object: + // + // ```js + // { underline: true } + // ``` + // + // The candidate at this point will be `underline:`, which is invalid. However, we + // can assume in this case that the `:` should not be there, and therefore we can + // try to slice it off and retry the validation. + Some((b':' | b'/' | b'.', head)) => { + candidate = head; + } + + // It could also be that we have the candidate is nested inside of bracket or quote + // pairs. In this case we want to retrieve the inner part and try to validate that + // inner part instead. For example, in a JavaScript array: + // + // ```js + // let myClasses = ["underline"] + // ``` + // + // The `underline` is nested inside of quotes and in square brackets. Let's try to + // get the inner part and validate that instead. + _ => match Self::slice_surrounding(candidate) { + Some(shorter) if shorter != candidate => { + candidate = shorter; + } + _ => break, + }, + } + } + + ParseAction::Consume + } + + #[inline(always)] + fn split_candidate(candidate: &'a [u8]) -> SplitCandidate { + let mut brackets = 0; + let mut idx_end = 0; + + for (n, c) in candidate.iter().enumerate() { + match c { + b'[' => brackets += 1, + b']' if brackets > 0 => brackets -= 1, + b':' if brackets == 0 => idx_end = n + 1, + _ => {} + } + } + + SplitCandidate { + variant: &candidate[0..idx_end], + utility: &candidate[idx_end..], + } + } + + #[inline(always)] + fn contains_in_constrained(candidate: &'a [u8], bytes: Vec) -> bool { + let mut brackets = 0; + + for c in candidate { + match c { + b'[' => brackets += 1, + b']' if brackets > 0 => brackets -= 1, + _ if brackets == 0 && bytes.contains(c) => return true, + _ => {} + } + } + + false + } + + #[inline(always)] + fn is_valid_candidate_string(candidate: &'a [u8]) -> ValidationResult { + // Reject candidates that start with a capital letter + if candidate[0].is_ascii_uppercase() { + return ValidationResult::Invalid; + } + + // Rejects candidates that end with "-" or "_" + if candidate.ends_with(b"-") || candidate.ends_with(b"_") { + return ValidationResult::Invalid; + } + + // Reject candidates that are single camelCase words, e.g.: `useEffect` + if candidate.iter().all(|c| c.is_ascii_alphanumeric()) + && candidate + .iter() + .any(|c| c.is_ascii_uppercase() || c.is_ascii_digit()) + { + return ValidationResult::Invalid; + } + + // Reject candidates that look like SVG path data, e.g.: `m32.368 m7.5` + if !candidate.contains(&b'-') + && !candidate.contains(&b':') + && candidate.iter().any(|c| c == &b'.' || c.is_ascii_digit()) + { + return ValidationResult::Invalid; + } + + // Reject candidates that look like version constraints or email addresses, e.g.: `next@latest`, `bob@example.com` + if candidate + .iter() + .all(|c| c.is_ascii_alphanumeric() || c == &b'.' || c == &b'-' || c == &b'@') + && candidate[1..].contains(&b'@') + { + return ValidationResult::Invalid; + } + + // Reject candidates that look like URLs + if candidate.starts_with(b"http://") || candidate.starts_with(b"https://") { + return ValidationResult::Invalid; + } + + // Reject candidates that look short markdown links, e.g.: `[https://example.com]` + if candidate.starts_with(b"[http://") || candidate.starts_with(b"[https://") { + return ValidationResult::Invalid; + } + + // Reject candidates that look like imports with path aliases, e.g.: `@/components/button` + if candidate.len() > 1 && candidate[1] == b'/' { + return ValidationResult::Invalid; + } + + // Reject candidates that look like paths, e.g.: `app/assets/stylesheets` + if !candidate.contains(&b':') && !candidate.contains(&b'[') { + let mut count = 0; + for c in candidate { + if c == &b'/' { + count += 1; + } + if count > 1 { + return ValidationResult::Invalid; + } + } + } + + let split_candidate = Extractor::split_candidate(candidate); + + let mut offset = 0; + let mut offset_end = 0; + let utility = &split_candidate.utility; + let original_utility = &utility; + + // Some special cases that we can ignore while validating + if utility.starts_with(b"!-") { + offset += 2; + } else if utility.starts_with(b"!") || utility.starts_with(b"-") { + offset += 1; + } else if utility.ends_with(b"!") { + offset_end += 1; + } + + // These are allowed in arbitrary values and in variants but nowhere else + if Extractor::contains_in_constrained(utility, vec![b'<', b'>']) { + return ValidationResult::Restart; + } + + // It's an arbitrary property + if utility.starts_with(b"[") + && utility.ends_with(b"]") + && (utility.starts_with(b"['") + || utility.starts_with(b"[\"") + || utility.starts_with(b"[`")) + { + return ValidationResult::Restart; + } + + // Pluck out the part that we are interested in. + let utility = &utility[offset..(utility.len() - offset_end)]; + + // Validations + // We should have _something_ + if utility.is_empty() { + return ValidationResult::Invalid; + } + + // = b'0' && utility[0] <= b'9' && !utility.contains(&b':') { + return ValidationResult::Invalid; + } + + // In case of an arbitrary property, we should have at least this structure: [a:b] + if utility.starts_with(b"[") && utility.ends_with(b"]") { + // [a:b] is at least 5 characters long + if utility.len() < 5 { + return ValidationResult::Invalid; + } + + // Now that we validated that the candidate is technically fine, let's ensure that it + // doesn't start with a `-` because that would make it invalid for arbitrary properties. + if original_utility.starts_with(b"-") || original_utility.starts_with(b"!-") { + return ValidationResult::Invalid; + } + + // Make sure an arbitrary property/value pair is valid, otherwise + // we may generate invalid CSS that will cause tools like PostCSS + // to crash when trying to parse the generated CSS. + if !Self::validate_arbitrary_property(utility) { + return ValidationResult::Invalid; + } + + // The ':` must be preceded by a-Z0-9 because it represents a property name. + let colon = utility.find(":").unwrap(); + + if !utility + .chars() + .nth(colon - 1) + .map_or_else(|| false, |c| c.is_ascii_alphanumeric()) + { + return ValidationResult::Invalid; + } + + let property = &utility[1..colon]; + + // The property must match /^[a-zA-Z-][a-zA-Z0-9-_]+$/ + if !property[0].is_ascii_alphabetic() && property[0] != b'-' { + return ValidationResult::Invalid; + } + + if !property + .iter() + .all(|c| c.is_ascii_alphanumeric() || c == &b'-' || c == &b'_') + { + return ValidationResult::Invalid; + } + } + + ValidationResult::Valid + } + + /** + * Make sure an arbitrary property/value pair is valid, otherwise + * PostCSS may crash when trying to parse the generated CSS. + * + * `input` - the full candidate string, including the brackets + */ + fn validate_arbitrary_property(candidate: &[u8]) -> bool { + if !candidate.starts_with(b"[") || !candidate.ends_with(b"]") { + return false; + } + let property = &candidate[1..candidate.len() - 1]; + let is_custom_property = property.starts_with(b"--"); + let Some(colon_pos) = property.find(b":") else { + return false; + }; + if is_custom_property { + return true; + } + + let mut stack = vec![]; + let mut iter = property[colon_pos + 1..].iter(); + while let Some(c) = iter.next() { + match c { + // The value portion cannot contain unquoted colons. + // E.g. `[foo::bar]` leads to "foo::bar; which errors because of the `:`. + b':' | b'{' | b'}' if stack.is_empty() => { + return false; + } + + b'\'' => { + if let Some(b'\'') = stack.last() { + _ = stack.pop() + } else { + stack.push(b'\'') + } + } + b'"' => { + if let Some(b'"') = stack.last() { + _ = stack.pop() + } else { + stack.push(b'"') + } + } + + // Skip escaped characters. + b'\\' => { + iter.next(); + } + + _ => {} + } + } + + true + } + + #[inline(always)] + fn parse_escaped(&mut self) -> ParseAction<'a> { + // If this character is escaped, we don't care about it. + // It gets consumed. + trace!("Escape::Consume"); + + self.in_escape = false; + + ParseAction::Consume + } + + #[inline(always)] + fn parse_arbitrary(&mut self) -> ParseAction<'a> { + // In this we could technically use memchr 6 times (then looped) to find the indexes / bounds of arbitrary valuesq + if self.in_escape { + return self.parse_escaped(); + } + + match self.cursor.curr { + b'\\' => { + // The `\` character is used to escape characters in arbitrary content _and_ to prevent the starting of arbitrary content + trace!("Arbitrary::Escape"); + self.in_escape = true; + } + + b'(' => self.bracket_stack.push(self.cursor.curr), + b')' => match self.bracket_stack.last() { + Some(&b'(') => { + self.bracket_stack.pop(); + } + + // Last bracket is different compared to what we expect, therefore we are not in a + // valid arbitrary value. + _ if !self.in_quotes() => return ParseAction::Skip, + + // We're probably in quotes or nested brackets, so we keep going + _ => {} + }, + + // Make sure the brackets are balanced + b'[' => self.bracket_stack.push(self.cursor.curr), + b']' => match self.bracket_stack.last() { + // We've ended a nested bracket + Some(&b'[') => { + self.bracket_stack.pop(); + } + + // This is the last bracket meaning the end of arbitrary content + _ if !self.in_quotes() => { + if matches!(self.cursor.next, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') { + return ParseAction::Consume; + } + + trace!("Arbitrary::End\t"); + self.in_arbitrary = false; + + if self.cursor.pos - self.idx_arbitrary_start == 1 { + // We have an empty arbitrary value, which is not allowed + return ParseAction::Skip; + } + } + + // We're probably in quotes or nested brackets, so we keep going + _ => {} + }, + + // Arbitrary values sometimes contain quotes + // These can "escape" the arbitrary value mode + // switching of `[` and `]` characters + b'"' | b'\'' | b'`' => match self.quote_stack.last() { + Some(&last_quote) if last_quote == self.cursor.curr => { + trace!("Quote::End\t"); + self.quote_stack.pop(); + } + _ => { + trace!("Quote::Start\t"); + self.quote_stack.push(self.cursor.curr); + } + }, + + b' ' if !self.opts.preserve_spaces_in_arbitrary => { + trace!("Arbitrary::SkipAndEndEarly\t"); + + // Restart the parser ahead of the arbitrary value + // It may pick up more candidates + return ParseAction::RestartAt(self.idx_arbitrary_start + 1); + } + + // Arbitrary values allow any character inside them + // Except spaces unless you are in loose mode + _ => { + trace!("Arbitrary::Consume\t"); + // No need to move the end index because either the arbitrary value will end properly OR we'll hit invalid characters + } + } + + ParseAction::Consume + } + + #[inline(always)] + fn parse_start(&mut self) -> ParseAction<'a> { + match self.cursor.curr { + // Enter arbitrary value mode + b'[' => { + trace!("Arbitrary::Start\t"); + self.in_arbitrary = true; + self.idx_arbitrary_start = self.cursor.pos; + + ParseAction::Consume + } + + // Allowed first characters. + b'@' | b'!' | b'-' | b'<' | b'>' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'*' => { + // TODO: A bunch of characters that we currently support but maybe we only want it behind + // a flag. E.g.: ` ParseAction::Skip, + } + } + + #[inline(always)] + fn parse_continue(&mut self) -> ParseAction<'a> { + match self.cursor.curr { + // Enter arbitrary value mode + b'[' if matches!( + self.cursor.prev, + b'@' | b'-' | b' ' | b':' | b'/' | b'!' | b'\0' + ) => + { + trace!("Arbitrary::Start\t"); + self.in_arbitrary = true; + self.idx_arbitrary_start = self.cursor.pos; + } + + // Can't enter arbitrary value mode + // This can't be a candidate + b'[' => { + trace!("Arbitrary::Skip_Start\t"); + + return ParseAction::Skip; + } + + // A % can only appear at the end of the candidate itself. It can also only be after a + // digit 0-9. This covers the following cases: + // - from-15% + b'%' if self.cursor.prev.is_ascii_digit() => { + return match (self.cursor.at_end, self.cursor.next) { + // End of string == end of candidate == okay + (true, _) => ParseAction::Consume, + + // Looks like the end of a candidate == okay + (_, b' ' | b'\'' | b'"' | b'`') => ParseAction::Consume, + + // Otherwise, not a valid character in a candidate + _ => ParseAction::Skip, + }; + } + b'%' => return ParseAction::Skip, + + // < and > can only be part of a variant and only be the first or last character + b'<' | b'>' | b'*' => { + // Can only be the first or last character + // + // E.g.: + // + // - :underline + // ^ + if self.cursor.pos == self.idx_start || self.cursor.pos == self.idx_last { + trace!("Candidate::Consume\t"); + } + // If it is in the middle, it can only be part of a stacked variant + // - dark::underline + // ^ + else if self.cursor.prev == b':' || self.cursor.next == b':' { + trace!("Candidate::Consume\t"); + } else { + return ParseAction::Skip; + } + } + + // Allowed characters in the candidate itself + // None of these can come after a closing bracket `]` + b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_' | b'@' + if self.cursor.prev != b']' => + { + /* TODO: The `b'@'` is necessary for custom separators like _@, maybe we can handle this in a better way... */ + trace!("Candidate::Consume\t"); + } + + // A dot (.) can only appear in the candidate itself (not the arbitrary part), if the previous + // and next characters are both digits. This covers the following cases: + // - p-1.5 + b'.' if self.cursor.prev.is_ascii_digit() => match self.cursor.next { + next if next.is_ascii_digit() => { + trace!("Candidate::Consume\t"); + } + _ => return ParseAction::Skip, + }, + + // Allowed characters in the candidate itself + // These MUST NOT appear at the end of the candidate + b'/' | b':' if !self.cursor.at_end => { + trace!("Candidate::Consume\t"); + } + + // The important character `!`, is allowed at the end of the candidate + b'!' => { + trace!("Candidate::Consume\t"); + } + + _ => return ParseAction::Skip, + } + + ParseAction::Consume + } + + #[inline(always)] + fn can_be_candidate(&mut self) -> bool { + self.in_candidate + && !self.in_arbitrary + && (0..=127).contains(&self.cursor.curr) + && (self.idx_start == 0 || self.input[self.idx_start - 1] <= 127) + } + + #[inline(always)] + fn handle_skip(&mut self) { + // In all other cases, we skip characters and reset everything so we can make new candidates + trace!("Characters::Skip\t"); + self.idx_start = self.cursor.pos; + self.idx_end = self.cursor.pos; + self.in_candidate = false; + self.in_arbitrary = false; + self.in_escape = false; + } + + #[inline(always)] + fn parse_char(&mut self) -> ParseAction<'a> { + if self.in_arbitrary { + self.parse_arbitrary() + } else if self.in_candidate { + self.parse_continue() + } else if self.parse_start() == ParseAction::Consume { + self.in_candidate = true; + self.idx_start = self.cursor.pos; + self.idx_end = self.cursor.pos; + + ParseAction::Consume + } else { + ParseAction::Skip + } + } + + #[inline(always)] + fn yield_candidate(&mut self) -> ParseAction<'a> { + if self.can_be_candidate() { + self.get_current_candidate() + } else { + ParseAction::Consume + } + } + + #[inline(always)] + fn restart(&mut self, pos: usize) { + trace!("Parser::Restart\t{}", pos); + + self.idx_start = pos; + self.idx_end = pos; + self.idx_arbitrary_start = 0; + + self.in_arbitrary = false; + self.in_candidate = false; + self.in_escape = false; + + self.discard_next = false; + + self.quote_stack.clear(); + self.bracket_stack.clear(); + self.cursor.move_to(pos); + } + + #[inline(always)] + fn without_surrounding(&self) -> Bracketing<'a> { + let range = self.idx_start..=self.idx_end; + let clipped = &self.input[range]; + + Self::slice_surrounding(clipped) + .map(Bracketing::Included) + .or_else(|| { + if self.idx_start == 0 || self.idx_end + 1 == self.idx_last { + return None; + } + + let range = self.idx_start - 1..=self.idx_end + 1; + let clipped = &self.input[range]; + Self::slice_surrounding(clipped).map(Bracketing::Wrapped) + }) + .unwrap_or(Bracketing::None) + } + + #[inline(always)] + fn is_balanced(input: &[u8]) -> bool { + let mut depth = 0isize; + + for n in input { + match n { + b'[' | b'{' | b'(' => depth += 1, + b']' | b'}' | b')' => depth -= 1, + _ => continue, + } + + if depth < 0 { + return false; + } + } + + depth == 0 + } + + #[inline(always)] + fn slice_surrounding(input: &[u8]) -> Option<&[u8]> { + let mut prev = None; + let mut input = input; + + loop { + let leading = input.first().unwrap_or(&0x00); + let trailing = input.last().unwrap_or(&0x00); + + let needed = matches!( + (leading, trailing), + (b'(', b')') + | (b'{', b'}') + | (b'[', b']') + | (b'"', b'"') + | (b'`', b'`') + | (b'\'', b'\'') + ); + + if needed { + prev = Some(input); + input = &input[1..input.len() - 1]; + continue; + } + if Self::is_balanced(input) && prev.is_some() { + return Some(input); + } + return prev; + } + } + + #[inline(always)] + fn parse_and_yield(&mut self) -> ParseAction<'a> { + trace!("Cursor {}", self.cursor); + + // Fast skipping of invalid characters + let can_skip_whitespace = false; // if self.opts.preserve_spaces_in_arbitrary { !self.in_arbitrary } else { true }; + if can_skip_whitespace { + if let Some(pos) = fast_skip(&self.cursor) { + trace!("FastSkip::Restart\t{}", pos); + return ParseAction::RestartAt(pos); + } + } + + let action = self.parse_char(); + + match action { + ParseAction::RestartAt(_) => return action, + ParseAction::Consume => { + self.idx_end = self.cursor.pos; + + // If we're still consuming characters, we keep going + // Only exception is if we've hit the end of the input + if !self.cursor.at_end { + return action; + } + } + _ => {} + } + + let action = self.yield_candidate(); + + match (&action, self.cursor.curr) { + (ParseAction::RestartAt(_), _) => action, + (_, 0x00) => ParseAction::Done, + (ParseAction::SingleCandidate(candidate), _) => self.generate_slices(candidate), + _ => ParseAction::RestartAt(self.cursor.pos + 1), + } + } + + /// Peek inside `[]`, `{}`, and `()` pairs + /// to look for an additional candidate + #[inline(always)] + fn generate_slices(&mut self, candidate: &'a [u8]) -> ParseAction<'a> { + match self.without_surrounding() { + Bracketing::None => ParseAction::SingleCandidate(candidate), + Bracketing::Included(sliceable) if sliceable == candidate => { + ParseAction::SingleCandidate(candidate) + } + Bracketing::Included(sliceable) | Bracketing::Wrapped(sliceable) => { + let parts = vec![candidate, sliceable]; + let parts = parts + .into_iter() + .filter(|v| !v.is_empty()) + .collect::>(); + + ParseAction::MultipleCandidates(parts) + } + } + } +} + +impl<'a> Iterator for Extractor<'a> { + type Item = Vec<&'a [u8]>; + + fn next(&mut self) -> Option { + if self.cursor.at_end { + return None; + } + + loop { + let result = self.parse_and_yield(); + + // Cursor control + match result { + ParseAction::RestartAt(pos) => self.restart(pos), + _ => self.cursor.advance_by(1), + } + + // Candidate state control + match result { + ParseAction::SingleCandidate(_) => self.handle_skip(), + ParseAction::MultipleCandidates(_) => self.handle_skip(), + _ => {} + } + + // Iterator results + return match result { + ParseAction::SingleCandidate(candidate) => Some(vec![candidate]), + ParseAction::MultipleCandidates(candidates) => Some(candidates), + ParseAction::Done => None, + _ => continue, + }; + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn _please_trace() { + tracing_subscriber::fmt() + .with_max_level(tracing::Level::TRACE) + .with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE) + .compact() + .init(); + } + + fn run(input: &str, loose: bool) -> Vec<&str> { + Extractor::unique_ord( + input.as_bytes(), + ExtractorOptions { + preserve_spaces_in_arbitrary: loose, + }, + ) + .into_iter() + .map(|s| unsafe { std::str::from_utf8_unchecked(s) }) + .collect() + } + + #[test] + fn it_can_parse_simple_candidates() { + let candidates = run("underline", false); + assert_eq!(candidates, vec!["underline"]); + } + + #[test] + fn it_can_parse_multiple_simple_utilities() { + let candidates = run("font-bold underline", false); + assert_eq!(candidates, vec!["font-bold", "underline"]); + } + + #[test] + fn it_can_parse_simple_candidates_with_variants() { + let candidates = run("hover:underline", false); + assert_eq!(candidates, vec!["hover:underline"]); + } + + #[test] + fn it_can_parse_start_variants() { + let candidates = run("*:underline", false); + assert_eq!(candidates, vec!["*:underline"]); + + let candidates = run("hover:*:underline", false); + assert_eq!(candidates, vec!["hover:*:underline"]); + } + + #[test] + fn it_can_parse_simple_candidates_with_stacked_variants() { + let candidates = run("focus:hover:underline", false); + assert_eq!(candidates, vec!["focus:hover:underline"]); + } + + #[test] + fn it_can_parse_utilities_with_arbitrary_values() { + let candidates = run("m-[2px]", false); + assert_eq!(candidates, vec!["m-[2px]"]); + } + + #[test] + fn it_throws_away_arbitrary_values_that_are_unbalanced() { + let candidates = run("m-[calc(100px*2]", false); + assert!(candidates.is_empty()); + } + + #[test] + fn it_can_parse_utilities_with_arbitrary_values_and_variants() { + let candidates = run("hover:m-[2px]", false); + assert_eq!(candidates, vec!["hover:m-[2px]"]); + } + + #[test] + fn it_can_parse_arbitrary_variants() { + let candidates = run("[@media(min-width:200px)]:underline", false); + assert_eq!(candidates, vec!["[@media(min-width:200px)]:underline"]); + } + + #[test] + fn it_can_parse_matched_variants() { + let candidates = run("group-[&:hover]:underline", false); + assert_eq!(candidates, vec!["group-[&:hover]:underline"]); + } + + #[test] + fn it_should_not_keep_spaces() { + let candidates = run("bg-[rgba(0, 0, 0)]", false); + + assert_eq!(candidates, vec!["rgba"]); + } + + #[test] + fn it_should_keep_spaces_in_loose_mode() { + let candidates = run("bg-[rgba(0, 0, 0)]", true); + assert_eq!(candidates, vec!["bg-[rgba(0, 0, 0)]"]); + } + + #[test] + fn it_should_keep_important_arbitrary_properties_legacy() { + let candidates = run("![foo:bar]", false); + assert_eq!(candidates, vec!["![foo:bar]"]); + } + + #[test] + fn it_should_keep_important_arbitrary_properties() { + let candidates = run("[foo:bar]!", false); + assert_eq!(candidates, vec!["[foo:bar]!"]); + } + + #[test] + fn it_should_keep_important_arbitrary_values() { + let candidates = run("w-[calc(var(--size)/2)]!", false); + assert_eq!(candidates, vec!["w-[calc(var(--size)/2)]!"]); + } + + #[test] + fn it_should_keep_important_candidates_legacy() { + let candidates = run("!w-4", false); + assert_eq!(candidates, vec!["!w-4"]); + } + + #[test] + fn it_should_keep_important_candidates() { + let candidates = run("w-4!", false); + assert_eq!(candidates, vec!["w-4!"]); + } + + #[test] + fn it_should_not_allow_for_bogus_candidates() { + let candidates = run("[0]", false); + assert!(candidates.is_empty()); + + let candidates = run("[something]", false); + assert_eq!(candidates, vec!["something"]); + + let candidates = run(" [feature(slice_as_chunks)]", false); + assert_eq!(candidates, vec!["feature(slice_as_chunks)"]); + + let candidates = run("![feature(slice_as_chunks)]", false); + assert!(candidates.is_empty()); + + let candidates = run("-[feature(slice_as_chunks)]", false); + assert!(candidates.is_empty()); + + let candidates = run("!-[feature(slice_as_chunks)]", false); + assert!(candidates.is_empty()); + + let candidates = run("-[foo:bar]", false); + assert!(candidates.is_empty()); + + let candidates = run("!-[foo:bar]", false); + assert!(candidates.is_empty()); + } + + #[test] + fn it_should_keep_candidates_with_brackets_in_arbitrary_values_inside_quotes() { + let candidates = run("content-['hello_[_]_world']", false); + assert_eq!(candidates, vec!["content-['hello_[_]_world']"]); + } + + #[test] + fn it_should_ignore_leading_spaces() { + let candidates = run(" backdrop-filter-none", false); + assert_eq!(candidates, vec!["backdrop-filter-none"]); + } + + #[test] + fn it_should_ignore_trailing_spaces() { + let candidates = run("backdrop-filter-none ", false); + assert_eq!(candidates, vec!["backdrop-filter-none"]); + } + + #[test] + fn it_should_keep_classes_before_an_ending_newline() { + let candidates = run("backdrop-filter-none\n", false); + assert_eq!(candidates, vec!["backdrop-filter-none"]); + } + + #[test] + fn it_should_parse_out_the_correct_classes_from_tailwind_tests() { + // From: tests/arbitrary-variants.test.js + let candidates = run( + r#" +
+ +
+
+ "#, + false, + ); + // TODO: it should not include additional (separate) classes: class, hover:, foo: bar, underline + // TODO: Double check the expectations based on above information + assert_eq!( + candidates, + vec![ + "div", + "class", + r#"dark:lg:hover:[&>*]:underline"#, + r#"[&_.foo\_\_bar]:hover:underline"#, + r#"hover:[&_.foo\_\_bar]:underline"# + ] + ); + } + + #[test] + fn potential_candidates_are_skipped_when_hitting_impossible_characters() { + let candidates = run("

A new software update is available. See what’s new in version 2.0.4.

", false); + assert_eq!( + candidates, + vec![ + "p", + "class", + "text-sm", + "text-blue-700", + // "A", // Uppercase first letter is not allowed + "new", + "software", + "update", + "is", + "available", + // "See", // Uppercase first letter is not allowed + // "what", // what is dropped because it is followed by the fancy: ’ + // "s", // s is dropped because it is preceded by the fancy: ’ + // "new", // Already seen + "in", + "version", + ] + ); + } + + #[test] + fn ignores_arbitrary_property_ish_things() { + // FIXME: () are only valid in an arbitrary + let candidates = run(" [feature(slice_as_chunks)]", false); + assert_eq!(candidates, vec!["feature(slice_as_chunks)",]); + } + + #[test] + fn foo_bar() { + // w[…] is not a valid pattern for part of candidate + // but @[] is (specifically in the context of a variant) + + let candidates = run("%w[text-[#bada55]]", false); + assert_eq!(candidates, vec!["w", "text-[#bada55]"]); + } + + #[test] + fn crash_001() { + let candidates = run("Aҿɿ[~5", false); + assert!(candidates.is_empty()); + } + + #[test] + fn crash_002() { + let candidates = run("", false); + assert!(candidates.is_empty()); + } + + #[test] + fn bad_001() { + let candidates = run("[杛杛]/", false); + assert_eq!(candidates, vec!["杛杛"]); + } + + #[test] + fn bad_002() { + let candidates = run(r"[\]\\\:[]", false); + assert!(candidates.is_empty()); + } + + #[test] + fn bad_003() { + // TODO: This seems… wrong + let candidates = run(r"[𕤵:]", false); + assert_eq!(candidates, vec!["𕤵", "𕤵:"]); + } + + #[test] + fn classes_in_js_arrays() { + let candidates = run( + r#"let classes = ['bg-black', 'hover:px-0.5', 'text-[13px]', '[--my-var:1_/_2]', '[.foo_&]:px-[0]', '[.foo_&]:[color:red]']">"#, + false, + ); + assert_eq!( + candidates, + vec![ + "let", + "classes", + "bg-black", + "hover:px-0.5", + "text-[13px]", + "[--my-var:1_/_2]", + "--my-var:1_/_2", + "[.foo_&]:px-[0]", + "[.foo_&]:[color:red]", + ] + ); + } + + #[test] + fn classes_in_js_arrays_without_spaces() { + let candidates = run( + r#"let classes = ['bg-black','hover:px-0.5','text-[13px]','[--my-var:1_/_2]','[.foo_&]:px-[0]','[.foo_&]:[color:red]']">"#, + false, + ); + assert_eq!( + candidates, + vec![ + "let", + "classes", + "bg-black", + "hover:px-0.5", + "text-[13px]", + "[--my-var:1_/_2]", + "--my-var:1_/_2", + "[.foo_&]:px-[0]", + "[.foo_&]:[color:red]", + ] + ); + } + + #[test] + fn classes_as_object_keys() { + let candidates = run( + r#"
"#, + false, + ); + assert_eq!( + candidates, + vec!["div", "underline", "active", "px-1.5", "online"] + ); + } + + #[test] + fn multiple_nested_candidates() { + let candidates = run(r#"{color:red}"#, false); + assert_eq!(candidates, vec!["color:red"]); + } + + #[test] + fn percent_ended_candidates() { + let candidates = run( + r#""#, + false, + ); + assert_eq!( + candidates, + vec!["should", "work", "underline", "from-50%", "flex",] + ); + } + + #[test] + fn candidate_cannot_start_with_uppercase_character() { + let candidates = run(r#"
"#, false); + assert_eq!(candidates, vec!["div", "class", "foo", "baz"]); + } + + #[test] + fn candidate_cannot_end_with_a_dash() { + let candidates = run(r#"
"#, false); + assert_eq!(candidates, vec!["div", "class", "foo", "baz"]); + } + + #[test] + fn candidate_cannot_end_with_an_underscore() { + let candidates = run(r#"
"#, false); + assert_eq!(candidates, vec!["div", "class", "foo", "baz"]); + } + + #[test] + fn candidate_cannot_be_a_single_camelcase_word() { + let candidates = run(r#"
"#, false); + assert_eq!(candidates, vec!["div", "class", "foo", "baz"]); + } + + #[test] + fn candidate_cannot_be_svg_path_data() { + let candidates = run(r#""#, false); + assert_eq!(candidates, vec!["path", "d"]); + } + + #[test] + fn candidate_cannot_be_email_or_version_constraint() { + let candidates = run(r#"
next@latest"#, false); + assert_eq!(candidates, vec!["div", "class", "@container/dialog"]); + } + + #[test] + fn candidate_cannot_be_a_url() { + let candidates = run( + r#"Our website is https://example.com or http://example.com if you want a virus"#, + false, + ); + assert_eq!( + candidates, + vec!["website", "is", "com", "or", "if", "you", "want", "a", "virus"] + ); + } + + #[test] + fn candidate_cannot_be_a_paths_with_aliases() { + let candidates = run(r#"import potato from '@/potato';"#, false); + assert_eq!(candidates, vec!["import", "potato", "from"]); + } + + #[test] + fn candidate_cannot_be_a_path() { + let candidates = run( + r#"import potato from 'some/path/to/something'; + import banana from '@/banana';"#, + false, + ); + assert_eq!(candidates, vec!["import", "potato", "from", "banana"]); + } + + #[test] + fn ruby_percent_formatted_strings() { + let candidates = run(r#"%w[hover:flex]"#, false); + assert_eq!(candidates, vec!["w", "hover:flex"]); + } + + #[test] + fn urls_in_arbitrary_values_are_ok() { + let candidates = run(r#"
"#, false); + assert_eq!( + candidates, + vec!["div", "class", "bg-[url('/img/hero-pattern.svg')]"] + ); + } + + #[test] + fn colon_in_arbitrary_property_value() { + let candidates = run("[color::] #[test::foo]", false); + assert!(candidates + .iter() + .all(|candidate| !candidate.starts_with('['))); + } + + #[test] + fn braces_in_arbitrary_property_value() { + let candidates = run("[color:${foo}] #[test:{foo}]", false); + assert!(candidates + .iter() + .all(|candidate| !candidate.starts_with('['))); + } + + #[test] + fn quoted_colon_in_arbitrary_property_value() { + let candidates = run("[content:'bar:bar'] [content:\"bar:bar\"]", false); + assert!(candidates + .iter() + .any(|candidate| candidate == &"[content:'bar:bar']")); + assert!(candidates + .iter() + .any(|candidate| candidate == &"[content:\"bar:bar\"]")); + } + + #[test] + fn quoted_braces_in_arbitrary_property_value() { + let candidates = run("[content:'{bar}'] [content:\"{bar}\"]", false); + assert!(candidates + .iter() + .any(|candidate| candidate == &"[content:'{bar}']")); + assert!(candidates + .iter() + .any(|candidate| candidate == &"[content:\"{bar}\"]")); + } + + #[test] + fn colon_in_custom_property_value() { + let candidates = run("[--foo:bar:bar]", false); + assert!(candidates + .iter() + .any(|candidate| candidate == &"[--foo:bar:bar]")); + } + + #[test] + fn braces_in_custom_property_value() { + let candidates = run("[--foo:{bar}]", false); + assert!(candidates + .iter() + .any(|candidate| candidate == &"[--foo:{bar}]")); + } + + #[test] + fn candidate_slicing() { + let result = Extractor::slice_surrounding(&b".foo_&]:px-[0"[..]) + .map(std::str::from_utf8) + .transpose() + .unwrap(); + assert_eq!(result, None); + + let result = Extractor::slice_surrounding(&b"[.foo_&]:px-[0]"[..]) + .map(std::str::from_utf8) + .transpose() + .unwrap(); + assert_eq!(result, Some("[.foo_&]:px-[0]")); + + let result = Extractor::slice_surrounding(&b"{[.foo_&]:px-[0]}"[..]) + .map(std::str::from_utf8) + .transpose() + .unwrap(); + assert_eq!(result, Some("[.foo_&]:px-[0]")); + + let result = Extractor::slice_surrounding(&b"![foo:bar]"[..]) + .map(std::str::from_utf8) + .transpose() + .unwrap(); + assert_eq!(result, None); + + let result = Extractor::slice_surrounding(&b"[\"pt-1.5\"]"[..]) + .map(std::str::from_utf8) + .transpose() + .unwrap(); + assert_eq!(result, Some("pt-1.5")); + + let count = 1_000; + let crazy = format!("{}[.foo_&]:px-[0]{}", "[".repeat(count), "]".repeat(count)); + + let result = Extractor::slice_surrounding(crazy.as_bytes()) + .map(std::str::from_utf8) + .transpose() + .unwrap(); + assert_eq!(result, Some("[.foo_&]:px-[0]")); + } +} diff --git a/packages/tailwindcss/crates/oxide/src/scanner/allowed_paths.rs b/packages/tailwindcss/crates/oxide/src/scanner/allowed_paths.rs new file mode 100644 index 0000000..3015e9d --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/scanner/allowed_paths.rs @@ -0,0 +1,64 @@ +use ignore::{DirEntry, WalkBuilder}; +use std::{path::Path, sync}; + +static BINARY_EXTENSIONS: sync::LazyLock> = sync::LazyLock::new(|| { + include_str!("fixtures/binary-extensions.txt") + .trim() + .lines() + .collect() +}); + +static IGNORED_EXTENSIONS: sync::LazyLock> = sync::LazyLock::new(|| { + include_str!("fixtures/ignored-extensions.txt") + .trim() + .lines() + .collect() +}); + +static IGNORED_FILES: sync::LazyLock> = sync::LazyLock::new(|| { + include_str!("fixtures/ignored-files.txt") + .trim() + .lines() + .collect() +}); + +static IGNORED_CONTENT_DIRS: sync::LazyLock> = + sync::LazyLock::new(|| vec![".git"]); + +#[tracing::instrument(skip(root))] +pub fn resolve_allowed_paths(root: &Path) -> impl Iterator { + WalkBuilder::new(root) + .hidden(false) + .require_git(false) + .filter_entry(|entry| match entry.file_type() { + Some(file_type) if file_type.is_dir() => match entry.file_name().to_str() { + Some(dir) => !IGNORED_CONTENT_DIRS.contains(&dir), + None => false, + }, + Some(file_type) if file_type.is_file() || file_type.is_symlink() => { + is_allowed_content_path(entry.path()) + } + _ => false, + }) + .build() + .filter_map(Result::ok) +} + +pub fn is_allowed_content_path(path: &Path) -> bool { + // Skip known ignored files + if path + .file_name() + .unwrap() + .to_str() + .map(|s| IGNORED_FILES.contains(&s)) + .unwrap_or(false) + { + return false; + } + + // Skip known ignored extensions + path.extension() + .map(|s| s.to_str().unwrap_or_default()) + .map(|ext| !IGNORED_EXTENSIONS.contains(&ext) && !BINARY_EXTENSIONS.contains(&ext)) + .unwrap_or(false) +} diff --git a/packages/tailwindcss/crates/oxide/src/scanner/detect_sources.rs b/packages/tailwindcss/crates/oxide/src/scanner/detect_sources.rs new file mode 100644 index 0000000..deb42c6 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/scanner/detect_sources.rs @@ -0,0 +1,223 @@ +use crate::scanner::allowed_paths::{is_allowed_content_path, resolve_allowed_paths}; +use crate::GlobEntry; +use fxhash::FxHashSet; +use std::cmp::Ordering; +use std::path::PathBuf; +use std::sync; +use walkdir::WalkDir; + +#[derive(Debug, Clone)] +pub struct DetectSources { + base: PathBuf, +} + +static KNOWN_EXTENSIONS: sync::LazyLock> = sync::LazyLock::new(|| { + include_str!("fixtures/template-extensions.txt") + .trim() + .lines() + // Drop commented lines + .filter(|x| !x.starts_with('#')) + // Drop empty lines + .filter(|x| !x.is_empty()) + .collect() +}); + +impl DetectSources { + pub fn new(base: PathBuf) -> Self { + Self { base } + } + + pub fn detect(&self) -> (Vec, Vec) { + let (files, dirs) = self.resolve_files(); + let globs = self.resolve_globs(&dirs); + + (files, globs) + } + + fn resolve_files(&self) -> (Vec, Vec) { + let mut files: Vec = vec![]; + let mut dirs: Vec = vec![]; + + for entry in resolve_allowed_paths(&self.base) { + let Some(file_type) = entry.file_type() else { + continue; + }; + + if file_type.is_file() { + files.push(entry.into_path()); + } else if file_type.is_dir() { + dirs.push(entry.into_path()); + } + } + + (files, dirs) + } + + fn resolve_globs(&self, dirs: &Vec) -> Vec { + let allowed_paths = FxHashSet::from_iter(dirs); + + // A list of directory names where we can't use globs, but we should track each file + // individually instead. This is because these directories are often used for both source and + // destination files. + let mut forced_static_directories = vec![self.base.join("public")]; + + // A list of known extensions + a list of extensions we found in the project. + let mut found_extensions = + FxHashSet::from_iter(KNOWN_EXTENSIONS.iter().map(|x| x.to_string())); + + // All root directories. + let mut root_directories = FxHashSet::from_iter(vec![self.base.clone()]); + + // All directories where we can safely use deeply nested globs to watch all files. + // In other comments we refer to these as "deep glob directories" or similar. + // + // E.g.: `./src/**/*.{html,js}` + let mut deep_globable_directories: FxHashSet = FxHashSet::default(); + + // All directories where we can only use shallow globs to watch all direct files but not + // folders. + // In other comments we refer to these as "shallow glob directories" or similar. + // + // E.g.: `./src/*/*.{html,js}` + let mut shallow_globable_directories: FxHashSet = FxHashSet::default(); + + // Collect all valid paths from the root. This will already filter out ignored files, unknown + // extensions and binary files. + let mut it = WalkDir::new(&self.base) + // Sorting to make sure that we always see the directories before the files. Also sorting + // alphabetically by default. + .sort_by( + |a, z| match (a.file_type().is_dir(), z.file_type().is_dir()) { + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + _ => a.file_name().cmp(z.file_name()), + }, + ) + .into_iter(); + + loop { + // We are only interested in valid entries + let entry = match it.next() { + Some(Ok(entry)) => entry, + _ => break, + }; + + // Ignore known directories that we don't want to traverse into. + if entry.file_type().is_dir() && entry.file_name() == ".git" { + it.skip_current_dir(); + continue; + } + + if entry.file_type().is_dir() { + // If we are in a directory where we know that we can't use any globs, then we have to + // track each file individually. + if forced_static_directories.contains(&entry.path().to_path_buf()) { + forced_static_directories.push(entry.path().to_path_buf()); + root_directories.insert(entry.path().to_path_buf()); + continue; + } + + // If we are in a directory where the parent is a forced static directory, then this + // will become a forced static directory as well. + if forced_static_directories.contains(&entry.path().parent().unwrap().to_path_buf()) + { + forced_static_directories.push(entry.path().to_path_buf()); + root_directories.insert(entry.path().to_path_buf()); + continue; + } + + // If we are in a directory, and the directory is git ignored, then we don't have to + // descent into the directory. However, we have to make sure that we mark the _parent_ + // directory as a shallow glob directory because using deep globs from any of the + // parent directories will include this ignored directory which should not be the case. + // + // Another important part is that if one of the ignored directories is a deep glob + // directory, then all of its parents (until the root) should be marked as shallow glob + // directories as well. + if !allowed_paths.contains(&entry.path().to_path_buf()) { + let mut parent = entry.path().parent(); + while let Some(parent_path) = parent { + // If the parent is already marked as a valid deep glob directory, then we have + // to mark it as a shallow glob directory instead, because we won't be able to + // use deep globs for this directory anymore. + if deep_globable_directories.contains(parent_path) { + deep_globable_directories.remove(parent_path); + shallow_globable_directories.insert(parent_path.to_path_buf()); + } + + // If we reached the root, then we can stop. + if parent_path == self.base { + break; + } + + // Mark the parent directory as a shallow glob directory and continue with its + // parent. + shallow_globable_directories.insert(parent_path.to_path_buf()); + parent = parent_path.parent(); + } + + it.skip_current_dir(); + continue; + } + + // If we are in a directory that is not git ignored, then we can mark this directory as + // a valid deep glob directory. This is only necessary if any of its parents aren't + // marked as deep glob directories already. + let mut found_deep_glob_parent = false; + let mut parent = entry.path().parent(); + while let Some(parent_path) = parent { + // If we reached the root, then we can stop. + if parent_path == self.base { + break; + } + + // If the parent is already marked as a deep glob directory, then we can stop + // because this glob will match the current directory already. + if deep_globable_directories.contains(parent_path) { + found_deep_glob_parent = true; + break; + } + + parent = parent_path.parent(); + } + + // If we didn't find a deep glob directory parent, then we can mark this directory as a + // deep glob directory (unless it is the root). + if !found_deep_glob_parent && entry.path() != self.base { + deep_globable_directories.insert(entry.path().to_path_buf()); + } + } + + // Handle allowed content paths + if is_allowed_content_path(entry.path()) + && allowed_paths.contains(&entry.path().to_path_buf()) + { + let path = entry.path(); + + // Collect the extension for future use when building globs. + if let Some(extension) = path.extension().and_then(|x| x.to_str()) { + found_extensions.insert(extension.to_string()); + } + } + } + + let mut extension_list = found_extensions.into_iter().collect::>(); + + extension_list.sort(); + + let extension_list = extension_list.join(","); + + // Build the globs for all globable directories. + let shallow_globs = shallow_globable_directories.iter().map(|path| GlobEntry { + base: path.display().to_string(), + pattern: format!("*/*.{{{}}}", extension_list), + }); + + let deep_globs = deep_globable_directories.iter().map(|path| GlobEntry { + base: path.display().to_string(), + pattern: format!("**/*.{{{}}}", extension_list), + }); + + shallow_globs.chain(deep_globs).collect::>() + } +} diff --git a/packages/tailwindcss/crates/oxide/src/scanner/fixtures/binary-extensions.txt b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/binary-extensions.txt new file mode 100644 index 0000000..e4e7af7 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/binary-extensions.txt @@ -0,0 +1,264 @@ +3dm +3ds +3g2 +3gp +7z +DS_Store +a +aac +adp +ai +aif +aiff +alz +ape +apk +appimage +ar +arj +asf +au +avi +avif +bak +baml +bh +bin +bk +bmp +btif +bz2 +bzip2 +cab +caf +cgm +class +cmx +cpio +cr2 +cur +dat +dcm +deb +dex +djvu +dll +dmg +dng +doc +docm +docx +dot +dotm +dra +dsk +dts +dtshd +dvb +dwg +dxf +ecelp4800 +ecelp7470 +ecelp9600 +egg +eol +eot +epub +exe +f4v +fbs +fh +fla +flac +flatpak +fli +flv +fpx +fst +fvt +g3 +gh +gif +graffle +gz +gzip +h261 +h263 +h264 +icns +ico +ief +img +ipa +iso +jar +jpeg +jpg +jpgv +jpm +jxr +key +ktx +lha +lib +lockb +lvp +lz +lzh +lzma +lzo +m3u +m4a +m4v +mar +mdi +mht +mid +midi +mj2 +mka +mkv +mmr +mng +mobi +mov +movie +mp3 +mp4 +mp4a +mpeg +mpg +mpga +mxu +nef +npx +numbers +nupkg +o +odp +ods +odt +oga +ogg +ogv +otf +ott +pages +pbm +pcx +pdb +pdf +pea +pgm +pic +png +pnm +pot +potm +potx +ppa +ppam +ppm +pps +ppsm +ppsx +ppt +pptm +pptx +psd +pya +pyc +pyo +pyv +qt +rar +ras +raw +resources +rgb +rip +rlc +rmf +rmvb +rpm +rtf +rz +s3m +s7z +scpt +sgi +shar +sil +sketch +slk +smv +snap +snk +so +sqlite +sqlite +sqlite3 +sqlite3 +stl +sub +suo +swf +tar +tbz +tbz2 +tga +tgz +thmx +tif +tiff +tlz +ttc +ttf +txz +udf +uvh +uvi +uvm +uvp +uvs +uvu +viv +vob +war +wav +wax +wbmp +wdp +weba +webm +webp +whl +wim +wm +wma +wmv +wmx +woff +woff2 +wrm +wvx +xbm +xif +xla +xlam +xls +xlsb +xlsm +xlsx +xlt +xltm +xltx +xm +xmind +xpi +xpm +xwd +xz +z +zip +zipx diff --git a/packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-extensions.txt b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-extensions.txt new file mode 100644 index 0000000..f147c24 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-extensions.txt @@ -0,0 +1,6 @@ +css +less +lock +sass +scss +styl diff --git a/packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-files.txt b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-files.txt new file mode 100644 index 0000000..45d4ced --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/ignored-files.txt @@ -0,0 +1,3 @@ +package-lock.json +pnpm-lock.yaml +bun.lockb diff --git a/packages/tailwindcss/crates/oxide/src/scanner/fixtures/template-extensions.txt b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/template-extensions.txt new file mode 100644 index 0000000..096932b --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/scanner/fixtures/template-extensions.txt @@ -0,0 +1,61 @@ +# HTML +html +pug + +# Glimmer +gjs +gts + +# JS +astro +cjs +cts +jade +js +jsx +mjs +mts +svelte +ts +tsx +vue + +# Markdown +md +mdx + +# ASP +aspx +razor + +# Handlebars +handlebars +hbs +mustache + +# PHP +php +twig + +# Ruby +erb +haml +liquid +rb +rhtml +slim + +# Elixir / Phoenix +eex +heex + +# Nunjucks +njk +nunjucks + +# Python +py +tpl + +# Rust +rs diff --git a/packages/tailwindcss/crates/oxide/src/scanner/mod.rs b/packages/tailwindcss/crates/oxide/src/scanner/mod.rs new file mode 100644 index 0000000..8ddf60f --- /dev/null +++ b/packages/tailwindcss/crates/oxide/src/scanner/mod.rs @@ -0,0 +1,2 @@ +pub mod allowed_paths; +pub mod detect_sources; diff --git a/packages/tailwindcss/crates/oxide/tests/scanner.rs b/packages/tailwindcss/crates/oxide/tests/scanner.rs new file mode 100644 index 0000000..2eb1281 --- /dev/null +++ b/packages/tailwindcss/crates/oxide/tests/scanner.rs @@ -0,0 +1,367 @@ +#[cfg(test)] +mod scanner { + use scanner::detect_sources::DetectSources; + use std::process::Command; + use std::{fs, path}; + + use tailwindcss_oxide::*; + use tempfile::tempdir; + + fn scan_with_globs( + paths_with_content: &[(&str, Option<&str>)], + globs: Vec<&str>, + ) -> (Vec, Vec) { + // Create a temporary working directory + let dir = tempdir().unwrap().into_path(); + + // Initialize this directory as a git repository + let _ = Command::new("git").arg("init").current_dir(&dir).output(); + + // Create the necessary files + for (path, contents) in paths_with_content { + // Ensure we use the right path separator for the current platform + let path = dir.join(path.replace('/', path::MAIN_SEPARATOR.to_string().as_str())); + let parent = path.parent().unwrap(); + if !parent.exists() { + fs::create_dir_all(parent).unwrap(); + } + + match contents { + Some(contents) => fs::write(path, contents).unwrap(), + None => fs::write(path, "").unwrap(), + } + } + + let base = format!("{}", dir.display()); + + // Resolve all content paths for the (temporary) current working directory + let mut scanner = Scanner::new( + Some(DetectSources::new(base.clone().into())), + Some( + globs + .iter() + .map(|x| GlobEntry { + base: base.clone(), + pattern: x.to_string(), + }) + .collect(), + ), + ); + + let candidates = scanner.scan(); + + let mut paths: Vec<_> = scanner + .get_files() + .into_iter() + .map(|x| x.replace(&format!("{}{}", &base, path::MAIN_SEPARATOR), "")) + .collect(); + + for glob in scanner.get_globs() { + paths.push(format!( + "{}{}{}", + glob.base, + path::MAIN_SEPARATOR, + glob.pattern + )); + } + + paths = paths + .into_iter() + .map(|x| { + let parent_dir = format!("{}{}", &base.to_string(), path::MAIN_SEPARATOR); + x.replace(&parent_dir, "") + // Normalize paths to use unix style separators + .replace('\\', "/") + }) + .collect(); + + // Sort the output for easier comparison (depending on internal datastructure the order + // _could_ be random) + paths.sort(); + + (paths, candidates) + } + + fn scan(paths_with_content: &[(&str, Option<&str>)]) -> (Vec, Vec) { + scan_with_globs(paths_with_content, vec![]) + } + + fn test(paths_with_content: &[(&str, Option<&str>)]) -> Vec { + scan(paths_with_content).0 + } + + #[test] + fn it_should_work_with_a_set_of_root_files() { + let globs = test(&[ + ("index.html", None), + ("a.html", None), + ("b.html", None), + ("c.html", None), + ]); + assert_eq!(globs, vec!["a.html", "b.html", "c.html", "index.html"]); + } + + #[test] + fn it_should_work_with_a_set_of_root_files_and_ignore_ignored_files() { + let globs = test(&[ + (".gitignore", Some("b.html")), + ("index.html", None), + ("a.html", None), + ("b.html", None), + ("c.html", None), + ]); + assert_eq!(globs, vec!["a.html", "c.html", "index.html"]); + } + + #[test] + fn it_should_list_all_files_in_the_public_folder_explicitly() { + let globs = test(&[ + ("index.html", None), + ("public/a.html", None), + ("public/b.html", None), + ("public/c.html", None), + ]); + assert_eq!( + globs, + vec![ + "index.html", + "public/a.html", + "public/b.html", + "public/c.html", + ] + ); + } + + #[test] + fn it_should_list_nested_folders_explicitly_in_the_public_folder() { + let globs = test(&[ + ("index.html", None), + ("public/a.html", None), + ("public/b.html", None), + ("public/c.html", None), + ("public/nested/a.html", None), + ("public/nested/b.html", None), + ("public/nested/c.html", None), + ("public/nested/again/a.html", None), + ("public/very/deeply/nested/a.html", None), + ]); + assert_eq!( + globs, + vec![ + "index.html", + "public/a.html", + "public/b.html", + "public/c.html", + "public/nested/a.html", + "public/nested/again/a.html", + "public/nested/b.html", + "public/nested/c.html", + "public/very/deeply/nested/a.html", + ] + ); + } + + #[test] + fn it_should_list_all_files_in_the_public_folder_explicitly_except_ignored_files() { + let globs = test(&[ + (".gitignore", Some("public/b.html\na.html")), + ("index.html", None), + ("public/a.html", None), + ("public/b.html", None), + ("public/c.html", None), + ]); + assert_eq!(globs, vec!["index.html", "public/c.html",]); + } + + #[test] + fn it_should_use_a_glob_for_top_level_folders() { + let globs = test(&[ + ("index.html", None), + ("src/a.html", None), + ("src/b.html", None), + ("src/c.html", None), + ]); + assert_eq!(globs, vec![ + "index.html", + "src/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "src/a.html", + "src/b.html", + "src/c.html" + ]); + } + + #[test] + fn it_should_ignore_binary_files() { + let globs = test(&[ + ("index.html", None), + ("a.mp4", None), + ("b.png", None), + ("c.lock", None), + ]); + assert_eq!(globs, vec!["index.html"]); + } + + #[test] + fn it_should_ignore_known_extensions() { + let globs = test(&[ + ("index.html", None), + ("a.css", None), + ("b.sass", None), + ("c.less", None), + ]); + assert_eq!(globs, vec!["index.html"]); + } + + #[test] + fn it_should_ignore_known_files() { + let globs = test(&[ + ("index.html", None), + ("package-lock.json", None), + ("yarn.lock", None), + ]); + assert_eq!(globs, vec!["index.html"]); + } + + #[test] + fn it_should_ignore_and_expand_nested_ignored_folders() { + let globs = test(&[ + // Explicitly listed root files + ("foo.html", None), + ("bar.html", None), + ("baz.html", None), + // Nested folder A, using glob + ("nested-a/foo.html", None), + ("nested-a/bar.html", None), + ("nested-a/baz.html", None), + // Nested folder B, with deeply nested files, using glob + ("nested-b/deeply-nested/foo.html", None), + ("nested-b/deeply-nested/bar.html", None), + ("nested-b/deeply-nested/baz.html", None), + // Nested folder C, with ignored sub-folder + ("nested-c/foo.html", None), + ("nested-c/bar.html", None), + ("nested-c/baz.html", None), + // Ignored folder + ("nested-c/.gitignore", Some("ignored-folder/")), + ("nested-c/ignored-folder/foo.html", None), + ("nested-c/ignored-folder/bar.html", None), + ("nested-c/ignored-folder/baz.html", None), + // Deeply nested, without issues + ("nested-c/sibling-folder/foo.html", None), + ("nested-c/sibling-folder/bar.html", None), + ("nested-c/sibling-folder/baz.html", None), + // Nested folder D, with deeply nested ignored folder + ("nested-d/foo.html", None), + ("nested-d/bar.html", None), + ("nested-d/baz.html", None), + ("nested-d/.gitignore", Some("deep/")), + ("nested-d/very/deeply/nested/deep/foo.html", None), + ("nested-d/very/deeply/nested/deep/bar.html", None), + ("nested-d/very/deeply/nested/deep/baz.html", None), + ("nested-d/very/deeply/nested/foo.html", None), + ("nested-d/very/deeply/nested/bar.html", None), + ("nested-d/very/deeply/nested/baz.html", None), + ("nested-d/very/deeply/nested/directory/foo.html", None), + ("nested-d/very/deeply/nested/directory/bar.html", None), + ("nested-d/very/deeply/nested/directory/baz.html", None), + ("nested-d/very/deeply/nested/directory/again/foo.html", None), + ]); + + assert_eq!( + globs, + vec![ + "bar.html", + "baz.html", + "foo.html", + "nested-a/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-a/bar.html", + "nested-a/baz.html", + "nested-a/foo.html", + "nested-b/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-b/deeply-nested/bar.html", + "nested-b/deeply-nested/baz.html", + "nested-b/deeply-nested/foo.html", + "nested-c/*/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-c/bar.html", + "nested-c/baz.html", + "nested-c/foo.html", + "nested-c/sibling-folder/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-c/sibling-folder/bar.html", + "nested-c/sibling-folder/baz.html", + "nested-c/sibling-folder/foo.html", + "nested-d/*/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-d/bar.html", + "nested-d/baz.html", + "nested-d/foo.html", + "nested-d/very/*/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-d/very/deeply/*/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-d/very/deeply/nested/*/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-d/very/deeply/nested/bar.html", + "nested-d/very/deeply/nested/baz.html", + "nested-d/very/deeply/nested/directory/**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}", + "nested-d/very/deeply/nested/directory/again/foo.html", + "nested-d/very/deeply/nested/directory/bar.html", + "nested-d/very/deeply/nested/directory/baz.html", + "nested-d/very/deeply/nested/directory/foo.html", + "nested-d/very/deeply/nested/foo.html", + ] + ); + } + + #[test] + fn it_should_scan_for_utilities() { + let mut ignores = String::new(); + ignores.push_str("# md:font-bold\n"); + ignores.push_str("foo.html\n"); + + let candidates = scan(&[ + // The gitignore file is used to filter out files but not scanned for candidates + (".gitignore", Some(&ignores)), + // A file that should definitely be scanned + ("index.html", Some("font-bold md:flex")), + // A file that should definitely not be scanned + ("foo.jpg", Some("xl:font-bold")), + // A file that is ignored + ("foo.html", Some("lg:font-bold")), + // A svelte file with `class:foo="bar"` syntax + ("index.svelte", Some("
")), + ]) + .1; + + assert_eq!( + candidates, + vec!["condition", "div", "font-bold", "md:flex", "px-4"] + ); + } + + #[test] + fn it_should_scan_content_paths() { + let candidates = scan_with_globs( + &[ + // We know that `.styl` extensions are ignored, so they are not covered by auto content + // detection. + ("foo.styl", Some("content-['foo.styl']")), + ], + vec!["*.styl"], + ) + .1; + + assert_eq!(candidates, vec!["content-['foo.styl']"]); + } + + #[test] + fn it_should_scan_content_paths_even_when_they_are_git_ignored() { + let candidates = scan_with_globs( + &[ + (".gitignore", Some("foo.styl")), + // We know that `.styl` extensions are ignored, so they are not covered by auto content + // detection. + ("foo.styl", Some("content-['foo.styl']")), + ], + vec!["*.styl"], + ) + .1; + + assert_eq!(candidates, vec!["content-['foo.styl']"]); + } +} diff --git a/packages/tailwindcss/npm/darwin-arm64/README.md b/packages/tailwindcss/npm/darwin-arm64/README.md new file mode 100644 index 0000000..b159fea --- /dev/null +++ b/packages/tailwindcss/npm/darwin-arm64/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-darwin-arm64` + +This is the **aarch64-apple-darwin** binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/darwin-arm64/package.json b/packages/tailwindcss/npm/darwin-arm64/package.json new file mode 100644 index 0000000..3231734 --- /dev/null +++ b/packages/tailwindcss/npm/darwin-arm64/package.json @@ -0,0 +1,18 @@ +{ + "name": "farmfe-plugin-tailwindcss-darwin-arm64", + "version": "0.1.2", + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/tailwindcss/npm/darwin-x64/README.md b/packages/tailwindcss/npm/darwin-x64/README.md new file mode 100644 index 0000000..be1b184 --- /dev/null +++ b/packages/tailwindcss/npm/darwin-x64/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-darwin-x64` + +This is the **x86_64-apple-darwin** binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/darwin-x64/package.json b/packages/tailwindcss/npm/darwin-x64/package.json new file mode 100644 index 0000000..4f3a2f1 --- /dev/null +++ b/packages/tailwindcss/npm/darwin-x64/package.json @@ -0,0 +1,18 @@ +{ + "name": "farmfe-plugin-tailwindcss-darwin-x64", + "version": "0.0.0", + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/tailwindcss/npm/linux-arm64-gnu/README.md b/packages/tailwindcss/npm/linux-arm64-gnu/README.md new file mode 100644 index 0000000..bd661ac --- /dev/null +++ b/packages/tailwindcss/npm/linux-arm64-gnu/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-linux-arm64-gnu` + +This is the **aarch64-unknown-linux-gnu** binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/linux-arm64-gnu/package.json b/packages/tailwindcss/npm/linux-arm64-gnu/package.json new file mode 100644 index 0000000..e2409be --- /dev/null +++ b/packages/tailwindcss/npm/linux-arm64-gnu/package.json @@ -0,0 +1,21 @@ +{ + "name": "farmfe-plugin-tailwindcss-linux-arm64-gnu", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} diff --git a/packages/tailwindcss/npm/linux-arm64-musl/README.md b/packages/tailwindcss/npm/linux-arm64-musl/README.md new file mode 100644 index 0000000..ca20ac9 --- /dev/null +++ b/packages/tailwindcss/npm/linux-arm64-musl/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-linux-arm64-musl` + +This is the **aarch64-unknown-linux-musl** binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/linux-arm64-musl/package.json b/packages/tailwindcss/npm/linux-arm64-musl/package.json new file mode 100644 index 0000000..3dd3d7f --- /dev/null +++ b/packages/tailwindcss/npm/linux-arm64-musl/package.json @@ -0,0 +1,21 @@ +{ + "name": "farmfe-plugin-tailwindcss-linux-arm64-musl", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} diff --git a/packages/tailwindcss/npm/linux-x64-gnu/README.md b/packages/tailwindcss/npm/linux-x64-gnu/README.md new file mode 100644 index 0000000..a2aeb63 --- /dev/null +++ b/packages/tailwindcss/npm/linux-x64-gnu/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-linux-x64-gnu` + +This is the **x86_64-unknown-linux-gnu** binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/linux-x64-gnu/package.json b/packages/tailwindcss/npm/linux-x64-gnu/package.json new file mode 100644 index 0000000..e7149a7 --- /dev/null +++ b/packages/tailwindcss/npm/linux-x64-gnu/package.json @@ -0,0 +1,21 @@ +{ + "name": "farmfe-plugin-tailwindcss-linux-x64-gnu", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} diff --git a/packages/tailwindcss/npm/linux-x64-musl/README.md b/packages/tailwindcss/npm/linux-x64-musl/README.md new file mode 100644 index 0000000..f96c5f6 --- /dev/null +++ b/packages/tailwindcss/npm/linux-x64-musl/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-linux-x64-musl` + +This is the **x86_64-unknown-linux-musl** binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/linux-x64-musl/package.json b/packages/tailwindcss/npm/linux-x64-musl/package.json new file mode 100644 index 0000000..b2a0233 --- /dev/null +++ b/packages/tailwindcss/npm/linux-x64-musl/package.json @@ -0,0 +1,18 @@ +{ + "name": "farmfe-plugin-tailwindcss-linux-x64-musl", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/tailwindcss/npm/win32-arm64-msvc/README.md b/packages/tailwindcss/npm/win32-arm64-msvc/README.md new file mode 100644 index 0000000..d6450f3 --- /dev/null +++ b/packages/tailwindcss/npm/win32-arm64-msvc/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-win32-arm64-msvc` + +This is the aarch64-pc-windows-msvc binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/win32-arm64-msvc/package.json b/packages/tailwindcss/npm/win32-arm64-msvc/package.json new file mode 100644 index 0000000..f3a728e --- /dev/null +++ b/packages/tailwindcss/npm/win32-arm64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "farmfe-plugin-tailwindcss-win32-arm64-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "arm64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/tailwindcss/npm/win32-ia32-msvc/README.md b/packages/tailwindcss/npm/win32-ia32-msvc/README.md new file mode 100644 index 0000000..d4b9bdc --- /dev/null +++ b/packages/tailwindcss/npm/win32-ia32-msvc/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-win32-ia32-msvc` + +This is the i686-pc-windows-msvc binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/win32-ia32-msvc/package.json b/packages/tailwindcss/npm/win32-ia32-msvc/package.json new file mode 100644 index 0000000..3c2a1ea --- /dev/null +++ b/packages/tailwindcss/npm/win32-ia32-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "farmfe-plugin-tailwindcss-win32-ia32-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "ia32" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/tailwindcss/npm/win32-x64-msvc/README.md b/packages/tailwindcss/npm/win32-x64-msvc/README.md new file mode 100644 index 0000000..a950e52 --- /dev/null +++ b/packages/tailwindcss/npm/win32-x64-msvc/README.md @@ -0,0 +1,3 @@ +# `farmfe-plugin-tailwindcss-win32-x64-msvc` + +This is the **x86_64-pc-windows-msvc** binary for `farmfe-plugin-tailwindcss` diff --git a/packages/tailwindcss/npm/win32-x64-msvc/package.json b/packages/tailwindcss/npm/win32-x64-msvc/package.json new file mode 100644 index 0000000..31d90c8 --- /dev/null +++ b/packages/tailwindcss/npm/win32-x64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "farmfe-plugin-tailwindcss-win32-x64-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "main": "index.farm", + "files": [ + "index.farm" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/tailwindcss/options.d.ts b/packages/tailwindcss/options.d.ts new file mode 100644 index 0000000..876eb23 --- /dev/null +++ b/packages/tailwindcss/options.d.ts @@ -0,0 +1,3 @@ +export interface IPluginOptions { + +} diff --git a/packages/tailwindcss/package.json b/packages/tailwindcss/package.json new file mode 100644 index 0000000..723228a --- /dev/null +++ b/packages/tailwindcss/package.json @@ -0,0 +1,43 @@ +{ + "name": "@farmfe/plugin-tailwindcss", + "version": "0.0.0", + "main": "scripts/index.js", + "types": "scripts/index.d.ts", + "type": "module", + "license": "MIT", + "devDependencies": { + "@farmfe/plugin-tools": "latest" + }, + "napi": { + "name": "farmfe_plugin_tailwindcss", + "triples": { + "additional": [ + "aarch64-apple-darwin", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "x86_64-unknown-linux-musl", + "i686-pc-windows-msvc", + "aarch64-pc-windows-msvc" + ] + } + }, + "exports": { + ".": { + "import": "./scripts/func.js", + "types": "./scripts/index.d.ts", + "default": "./scripts/index.js" + }, + "./package.json": "./package.json" + }, + "scripts": { + "dev": "cargo watch -w src -s 'scripts/watch.sh'", + "bump": "npx changeset version", + "play": "pnpm --filter playground dev", + "build": "farm-plugin-tools build --platform --cargo-name farmfe_plugin_tailwindcss -p farmfe_plugin_tailwindcss --release", + "prepublishOnly": "farm-plugin-tools prepublish" + }, + "files": [ + "scripts", + "options.d.ts" + ] +} diff --git a/packages/tailwindcss/playground/README.md b/packages/tailwindcss/playground/README.md new file mode 100644 index 0000000..5802e85 --- /dev/null +++ b/packages/tailwindcss/playground/README.md @@ -0,0 +1,37 @@ +# Farm + React + +This template should help you start developing using React and TypeScript in Farm. + +## Setup + +Install the dependencies: + +```bash +pnpm install +``` + +## Get Started + +Start the dev server: + +```bash +pnpm start +``` + +Build the app for production: + +```bash +pnpm build +``` + +Preview the Production build product: + +```bash +pnpm preview +``` + +Clear persistent cache local files + +```bash +pnpm clean +``` diff --git a/packages/tailwindcss/playground/farm.config.ts b/packages/tailwindcss/playground/farm.config.ts new file mode 100644 index 0000000..820d98b --- /dev/null +++ b/packages/tailwindcss/playground/farm.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "@farmfe/core"; +import react from '@farmfe/plugin-react'; +import farmPlugin from '@farmfe/plugin-tailwindcss'; + +export default defineConfig({ + compilation: { + input: { + index: "./index.html", + }, + persistentCache: false, + progress: false, + }, + plugins: [ + react({ runtime: "automatic" }), + farmPlugin() + ], +}); diff --git a/packages/tailwindcss/playground/index.css b/packages/tailwindcss/playground/index.css new file mode 100644 index 0000000..7844639 --- /dev/null +++ b/packages/tailwindcss/playground/index.css @@ -0,0 +1,65 @@ +p, +blockquote, +hr, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +figure, +pre { + margin: 0; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle; +} + +*, +::before, +::after { + border-width: 0; + border-style: solid; + border-color: theme('borderColor.DEFAULT', currentColor); +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +. { + content: ; +} + +.flex-1 { + flex: 1; +} diff --git a/packages/tailwindcss/playground/index.html b/packages/tailwindcss/playground/index.html new file mode 100644 index 0000000..8d229f7 --- /dev/null +++ b/packages/tailwindcss/playground/index.html @@ -0,0 +1,16 @@ + + + + + + + + Farm + React + TS + + +
+ + +
+ + diff --git a/packages/tailwindcss/playground/index.js b/packages/tailwindcss/playground/index.js new file mode 100644 index 0000000..065718f --- /dev/null +++ b/packages/tailwindcss/playground/index.js @@ -0,0 +1 @@ +console.log("lib") diff --git a/packages/tailwindcss/playground/package.json b/packages/tailwindcss/playground/package.json new file mode 100644 index 0000000..76a5817 --- /dev/null +++ b/packages/tailwindcss/playground/package.json @@ -0,0 +1,30 @@ +{ + "name": "playground", + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "farm start", + "start": "farm start", + "build": "farm build", + "preview": "farm preview", + "clean": "farm clean" + }, + "dependencies": { + "@farmfe/plugin-tailwindcss": "workspace:*", + "clsx": "^1.2.1", + "react": "18", + "react-dom": "18" + }, + "devDependencies": { + "@farmfe/cli": "^1.0.2", + "@farmfe/core": "^1.3.0", + "@farmfe/plugin-react": "^1.2.0", + "@types/react": "18", + "@types/react-dom": "18", + "autoprefixer": "^10.4.20", + "core-js": "^3.36.1", + "postcss": "^8.4.41", + "react-refresh": "^0.14.0", + "tailwindcss": "^3.4.10" + } +} diff --git a/packages/tailwindcss/playground/postcss.config.js b/packages/tailwindcss/playground/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/packages/tailwindcss/playground/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/packages/tailwindcss/playground/public/favicon.ico b/packages/tailwindcss/playground/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..80465dedc03cfd08b0e3b118db6e765f65e3adc3 GIT binary patch literal 4154 zcmb`~duWw)9LMqBoO)WB8>rdNSy794a8L80HKCYy;X53Ll~aB<(pZM`qE!25qV^T{4`B6-myS?o2hN82+<+U< zgU>Js#Y@ls0rgpHaWfVd>OhcuLiH?%JvX{-jp-L?TuqIfpde{Z+6RpMT(1M2a zNgW#BR8$vQhXMP8dvl>UUXQDxF|NSvPbf6_&zLFD zH5>=EtG%cFqj(pZ5A8>dbr{yJ+S~!fc|+tT()+LzipxT%okH!;)YStw?b>8VB6{p_in}7AeAdaJ^{r}^?eMB-Gk({ zrayu9w#~ow!{$co^?m3pP+TWG|G2Mpr`Uyk4031DEWT^Zs#|q!fzAf4HC z@HD383zV1%YP(h6O6-MVF$0><`LHpo%n?h&yyCS6;aV%P*?jSIU3mWM_tJK}3hkK- z(TTZGyGg9VBE;t=>{Gt7qs&mJ>d|=ip#xfr=c5gZ$yw07U$FsIX?|Ok>qC96J=cd; z@;YC2-m6XRg(lYaG*Z2nG~YT0)YowAdafLws6MUp<@g2%pfgBwk;0cy``Y{OLgf^v zvdn?TV0Do;U>(}g2+jRrsC}dJR{Q2sg!{9Maj?GBP`Bpc6{z<{_vLJy;6Olit;eS4G)6KtfV<)|&@?~GFW7k{s0_}^bcdli`x%y$}s)w9QNY*W`%sMACqBL=U`#(}{kZI}9O!ob|625;;!v7E?e72>_ YXKTD4qPpQwz4tCb{gqHVI7FV$f0MB}F8}}l literal 0 HcmV?d00001 diff --git a/packages/tailwindcss/playground/src/assets/logo.png b/packages/tailwindcss/playground/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0caeb4381267cff0c3adf3020077c55bac612a20 GIT binary patch literal 16859 zcmXwBWmFtZ(`Avx3GS}JT>`-^IKkcB-DL^BXmE!RoZuGRLvVKu?(X_6&wIWf91d(x zb@gmbch$YOCQ?~Z8Wo8U2?`1dRpyI?3KSH81oDHA0EGO4z1=x3Tj;_n3fpCwAyh=i!LHY@WbqFI%s5*P^3b=QJt<)ms|=?8^nE#<9_ z{_B3hx1ZiV-F0#Y&byp_g7vooa=~N-ad3#3M;D4)zyATSnw+EyvJl}^)&SP;CnYMeT#R?)RCP`5%cVP^^GMLm=Gj+Np}*cL#Lbu0KKM zLrF@8d~RN-3(uGm)EI9!#^GUU( zxA)Ajy!&ZSPKus1VOGLXUu9!VF}I*d_h7ysBmyPf zCi?3A$}ov%ZRMHy)%mU>*w1SykV=u{?epe6geT08o6)M zOP#r`nSPG~z4$FrH*Rycd!rLG>UaA0HwGZDu|%hIyO^sidq6W zhG+NC-bcbRq>EN07|DfUG2&HDS+!TgZ%zdL4J)D5Lp&Ryh!b$E?83LsKQ&N9lU)UW zd2;`poQ6w6HYz<7zsFgQ5aGe#sB?{uoJDM%I?NlL$&pXT;Uea$=6yx)%C%WM>gM;x zAziWT1X&-)4ZZl7**Oi4d@=k;G2^Bt;-)-wHsHJ(X;*b@f;Us+INAmHYflR@l63Y&;W#@#V@Tcu7{k9%z|ivV zs&7{yOtt&LNA-h6w221BCXq}(bq`c7=;oLeyDQ*l#SQ<@MD){fBhhWkoPMa!pCAvf z+D1Y3y0UqHODCXS7p_N>jk*eZBwXpQyno{awGOIxHIy)lk<||&$%O;UPm=LFDW$k1 zO=(QfbayF8e*PFN+{Utb$UP~~<~6}G_{{TIP60Im-p41Xx#8&~E;Yly30Xcs9#;k@ zFm+7II)JYo`UfL^f&h%odE=HqrIv;r<}D90TXQQfcps&>yf%s?yz%V)@rN4=X~8Dh zZ5oyL+fzAEB%HikEo&CN|F5EFPL4g^0 zW2oG%Www4>sXY0Q&R^Xq#ZU`&f`sDW#g5UkRMQ&keh()1YL>_`muaxQx((XWI0?ko z?_N`xj}@ld?0}#%&U^Tq^TUC)!-#dhYHT!8B0SUj!HS-VCMM+$iYs*! zdBb}e?AMVRLJSJlzW;a~S~<1ozxpbHmIo~IYN_1s$z_UcmQ8M7h@cA-zY zyPqs*0~{s;mbz6T%kz@0^4y5Da78E`o%h1)=G-38^qA&rmak-?7UQ7qgwwbJS2W2> zsPV#Z{$p^bKIh&Z>c5sp+$b;+mIq0Oeq@U}buO5cN z5S>LbetGNz0VFocuI;{X4f;pkA22Aaztkg^CR16dbYvf$!p}wYzn>3UfBZ}wJ1xf1 zc9Vrpn}-cdUPCPGW}7ABgyl zpnJJi+dmVe2Z_bla<>#RCIav)mi$w)u!}bp$G$N1r<#Y{OR2fZmG`r3IU4$};I_S* zA$(*N=fxN3IJ1c_lSH;~_>3Z2fC0XpU$CR^H`Ja~5}6kmijIJZc#e8~AlnmIyiIBu8{9sp+t; zW+?TDGjLfx&)$oqi@X`1$LQybMC_kHRhu23V20XmL#uZJh%?v9keHKhB^7l5IG|DQ&3>Lzd2y)|*6O$?28PJ1tuUW#b?c*}NrioPfPXjN_dr z&xMio5^k;FKb85_dPe6x+wdoAxGC%Y#q;=BLx^!L@UI(a(wL{J} z91}G|`SBrYI}ydEYQiw?%={HU_Km+CNI|u>a3{n1#1inPTn!aftt3j-!;v4%{eB$y zN2kCT5OL@17NTRE=O9UE{5KbUrV2o~`^Su9LUMyZaFLsnMVtT0l$R~rBx#Q)%7LBP zyJcFhA@GGwIW<4g2AtC`Q7LF@TqKMg2_7*Z-KCm zhoFRU()sFB_{&PsS2u+YHviG^9X@WApOu88L1RBfxN!68tp}}sI9IJp#U9vn=|ctn zRL&JU(So~;c9SrqpD|dr1|CYe9W%n93m^3)RaxZAQCeFIgKn?WKG|F!Qtr>y);)2pwr$YYTxw%>~an$O!EctrtV0xc(Ku$Uh_ zS(UQ;(&*QzDUQSIa)t8DjSTw0B)WDhNfdFW=Y~-?j3YS~X(?^L*mUg+3HHq)W+1m# z8o}>(qD+%xvBu2=jZ3=T39Kc#p)NW0%mM6Ux24B55Wj9T`q{n4Iq^?Y70f0nlrG+p zZiFDByU}m|Lt&(vS)Pm_CHxZaN$1y%wAS=KFILB56@|-U@~p8;1ghXbPP_Ao$h|gK z?a7niH#%z16AO1%kydZF7GYDJnhZz(Eeub0RNd+PM5Mtpjw}Ddakj!AGunl2)*q=Y zYUzC#BL2WEcw#-N%YPP1h+S7f7%Spw#^n=tVomGR1_v4oF)1*TGLC5IS_650dsL}& zsQlSp#qY+0B30YO&;9U`zdvd;T}GS~K#p?$dwlOt6-Jb6FTsOXq<8OC!zcMStxuTY zLz?EArJrm%AI8WmwzP}Xn@FDLTPbWw>`|E5Q_`?n^4eF-lSV)PO1 zLWtr^Rqd95dl%u4yzpTx!t*k`AxRk7eR&6kmfE1={N53?=4vQ8`+S1^#GnkUY_l&p zXuIpl9P6;Jk_+IsBJA}bzl5+h{Pu6td)?92-{tMViN7P2uenTG77?X{452$P8cme8 z>!x#Ufk2bIB8lQA5DqI;wfN+;;*pTE#R=~R2Hd)7kX1+(}?9Bmc)+0n7mW#4By0gr$5>ys^z$1IOlqIhPR z0onmsw?=j4Gfl#eg;JxNrvP?DR#nd}jDL4kdWTXg8m2=+(3^%1M*d-ADv@eaFMNeOh3}E=r z7&S}LSiL6FX1hhyqZCV<)MY1sN0M9unuFoKWt+WQ_6--b8Kp~`SI~a zr~GVzwjoZ$9{@KkP2?abVO%`NNk!1z;D-6hAC9-1k+eGYfdMuvyK%9 z9wlM4hlp}M)fr7xQKo!euJ9t1=2S*TQLEb@Ir8l_Tc7TUjwPS|=U~5KhdOu$26$Fa zpA^w3RfZ-?#EEil(88$G^B>8HUBBtHdreP4u=WWX#8=_?AH}yPNU&puSksX07&)$op1IjMQga`9o?ct<4EBNUe#RXv9+>c#<|+p6h*cBZF%u4h{gs_-%O z6b35qU!}NZTMzbxPQ-+8g<0ec5tJZJ%J2+YlXuiAS4KVz{F8qk4_*3TmTG6y3>Px) zI796-AwO)o67jVP-`=!xO)9c-i{QCo?NzUh2%nL_3%~CTTTt*r$a?2eGA8-WPz~9@ ziSMLLOp6H@JkhZaJ6!UnS&^_b$K`-Sd^TJDI+e0v^2?fusI>Ibj;=#rDR1=6O;#WR z`8xDaKY5FT)l$nT@yd+88ZSTDt4EAK=n=*=0kv5&P^q zYnHY*E{bqE$71kr!oG9pI9P7b6~<&5Ab!ls3oYilecs-&os=QC^aC0iA{fIyBJ6+q zXs6)&6aC4LXRs&*jy!sGA=ZJtLT{DOAA3+_-47QL+6PXXc&~uKxCW!4{R!n>#|=`k zy+Ikj^@N?QiFK)cd5uozJ)jypqhS1Vh}BWOxG=$>ExYEm(l|hK}&z%NtF(22lHCa@K;s@9l5_9%i zmaTSnXRXZ)!HUac_QAEbLiJHacypzR2htW&YbQx4%fiMIWHb}Txkl_06!9cSb9I!w zF28`$N$lRd7`Ws|>LSKo0`CSQSei^79nt&x z2>zhmup9B={8ELmeAO;&)}bna4S`8(?#dO7yno!F@ExlD z)5RI8T3>@Dp_BCoyDNX8fq3zGs4D2T7oX)1k|}=_wHOS?_R59dqJuQVNtr;QP`pW@ zc(l_ae_w5glWE{c3iyD2bo_|o246P5;jXj)i~H_&JhK_L(sWbgo_ce7F{Pz|&-@`_ zzDb>^Kq{oT_dqLXm_e2(@zy03APgQ`g?$yJ=rucc#$XIEq-cDwOOU!I1$9_1v$L_9 z^v90w{S;nL3sU>Y|2^FzH5(7lkUB~5jvr;8aq@e7H%8bYRLR+)ACb}oXA#cwc+4j` zE~Uk&B(DoBCSahjNxz`??2%MQK;K^+ZPjOdgv?Z7;s2n3VKPl=rci)kq#~r+#<>3> z1{B+ngWy9N?;h|hhVZS|o8+!t(te^rxQawXTisMVF7#t#=E2UBS z=Q(iyd=Rolmu7wQWVfodj3`h@iHwIVtj z0V)a{-F+73%@a*p$vd6r`yCkBM@`=|-MP;Lk!7+$2gZyZ-tW$wXPQER9fDdLO z2_6RggdVTP@vW92Alsr{SI1CkS6x<&h1j}@`e5V%(ImY^E*d8Z$>2hh#8{kC&K~;t zT{X^Ai)-Jb*q5;FStE}fg7rn0@LDvu{YhCFt^~?D~-$8&kvk3nnk| zLE?bNX6wQAl;CTf$nRDi91>;!v_aBOrt*+0$*$O(a3Ss%P`sfzt?hBau0XOMx@J*_ zvnyf)#Phl$ob`Fs5uctfVP>!+6+(npmz9-21mqu$R79H&goauxRW82o*E>;+aNgr# zFurDr*uLQ4Q@^Vdr)bKP^`-uji+V27H z(Ypr{5=NchibRPX*xLL0nh-Y{t8sKyKIY(gWS;)Lqm+_Kixy5#U$~%ouqm!(dv}lU zk_B{?^AXktQFp2#0a4~>VP>RaWWmY(D<4vMnw4-kW)tGrtA&`wVcpKyXHT3)k73R3 zd$DHIy*TN!j1;C{_qqXW_WuAdLKxZan9?2z+4THKbp3n?pOBB{!ka#Vz~^ zI8X<2&mK%sX%WrOhhHntpUowd%qB=2Oj^K&R?-mI*#k#4xcQGrzoca&MH3n*6^D&- zxZcG78jH27?gLh95*)_Kzd6d@soMLI^1Ei-)ejSYO==?O3C8{^MaAJ98UFI0iuZ)_ zGpPyKskO||wW*CY?{yb-%PaYn9WwbjzBY?^}*_B6=PFvTvj zi*P&(XWbCH8-}4!)U@2TON>CNySWse>v}tJd)bmxR^Iqs7;BOr(bH?<;l@oPo@k49 zGDE!zqf;bNh_xc@`|ZbY0d0ILM zszGoThxQdG0VUxrbv3t266QNKKma|Ns6$8d5Z-Y4IPU@9KXv?6Cum;|P%Sn@7JLmgHL$Eruh4^CZ%$XDPenh1IQ@6ZLW_SB{3?Ou!k4;6 zubn}v9(SYa&ewcR9X!|qdNn?MpAw`#W&rSzeP~d9BjEyn<`OUAO#TZMB4YF*=H6BQ zI!XTv-}k1YSvDrUmJHdrvvf)t4xhYd_Mh9aZ1E3d#$lcIy;9Wx@J$tDl9+uNs8t@P zso96!Lw@noHJE^k1;oi)77mf;`t;bdGuTOkFGFUAr7Ge=#I!eoKk zpdsj96Gj30f622=M#+Cn+cNjJ>#xSZkUVFsr5%{U0`~Vjf}D=en+SNlIqhFW6URuS zA^4!C=7y;-i71go81IBB%sI^*Sdt#%OVk-9uI z6=~PowUo#=G0YC;KHtPeQ`s=vO2NMpKi8+OqI&-?W5j(Kpvo;G_C|a(Q%o_s)ya?C z{`j8_juGH+YROK^SYKf1QC{-`rw*+r(rx)81Ti zz^XYKWDBGfbc(Q+%)NfHemjw5p@xPJTmJdB|6zGtlOMKisEgF#T!o)@RDUssbBE)hS>ex-t@|>K;uUVv zFkY@`XQb98-ox?X%@r7|$UxmWJaUIB@roP6wH@8>l1)ZeGMiQ#2XZPDkR;pEwbQ8~ zfhY7dmO~pFTfqd;LOrL}O0$rY!+1O$8p6+Rc)t@gbIWmp=l)Q5I4bj{AoN>ZCQZ2- zY}`7ZUkr@=&D`jpm2Wyor@=e=WM2_meCHie(psnMFFV|2Lh`X9tsAFB93GYfC!o7I zacUD0^e$AYy$AZW5PBBcJZSLMdQF2c!*;-OkQ=~^{U)1IH-0CK`B-H=II2%j8bvN6 zZh&&mghwF^FPS%2Z9Z`DhQD!phylH3RuqUV%F2CvF87Z5(q-(V6#T~NIw0K+m>+U@ zd_XuQjQ#WHO>NS_?L$d5#RHWEyRY(x0N-wogU2hOxC9ntJ4s2)x&1)_AWRTIR`o>i(s8JvM*_8ff?}ijZYqz-fs64m?K6tyx{rQrXz91oBQ7e;! zy7_7CN>u@4U(tly^GngznyZtlC%5^jWF-zx_RV@585&zP4J1chiweSv`eb|k%NR9i zHqc~4p#L$&?0@uK^0oj-6_QkD1MV0OF%-C_FQg!hhF-EIxc*-Y@K$8qe~D{<_ZVWwx%p&PYKfM1d&NIzd4IaDQ-tD8 z5nSbJi;~$vsK`CcTDOU}(o>~RY#=A!RIS{}JFSX0d&>7jsx2u==lRK@z5sy#QgHXp zdJsJ8G-z+VuZ9==_d;&V_>8!z3XJ6sFM>=sbatlncH}LB`^QBReMJNuRJ^E*gU8kj| zc8ceI7@zB6{q z3Zy{rJ1QxI+qBkg;%rvH*`XY&A$5IgjZf4Jecoxm$Qt%`^9qKw{Ze*M?IxSP~4Ynq(-T9I!< zpbd_&SZiDV1ci9GWu0Iz4tzMWiU9lHgF28UblFkb1<5?qaOzi=`e$h9XAdEPmu5K> zbQSUGKZSc6S!Mc*$HTfpom4qTFyA2 zFnPuYhkKf~LNWU44tSu{2&TEd0W0uu@@g}6c^AahKQhbw?5|AGn&AM6)yUPVy5S@E z2H!ItWx{CKIa|v-;GNckBWWe4F8}oCKO2`y4)lAc+5cUIn{gPa_Xk-CyvnOJ!h6+6 z{m=_%C_MVp@MHfUc+fHi_5i+!=4>%Ok4S3xtG?)x9Q_({pKp-2F#5?3eaJv=1Pq#%Yyf&4yr)wx{;h=7sF7=hr?)5mWi%#6kFH`Y% z(0+C`0Xa)p*~UdXqYowp(J3cgeq30KnW`tbMnYs{fv+eLwCUClC2_9LT-?n3-WG!_ zridl}5|wehJFsDXnqhVIsxcyD?EiEvbkLNiO9JhlDom}v3tpRWj7Agxu9&3#w-=oy zWHLJ8E)C5G4UU8ThfHd*kHKXgIaA=o?=UZSdGkZkGV!3f(fg7G+>g<>`31P#I+W=z z7|h?SfbX=1!DB2DM>FBvuIapew7jWMoSUBTJv#dSp&r6$J?wbQN9(1Yd{$wLHLBL{ z+^u6q2*~-Att*T&;a)^D&?-8f#VSu$W3if%i`gdu{Ge5}6ytSjJi%N<(_VshAPaV< z=O2uJ#>F=k<-;CXcMFw4-NXX!YfLAMH3itQWo*xBaygrBNkH$FQKbY-sIYbJs_XBf zUQrquEPx-5yq6zgkHp-LdDtn-(cmY4pghuc{g_fBJ~^-jMv!95$`1nh1t?E67aKD4 z1dhYUk=zgf;UMQPrwUrR@a=LN^Ig|ExQE=dJ_-mvH;MKr_PRr(t?;E(a8A@Bq(b8P zl0`HhJYt|yK{gt0K2Kjue~NBeJu!#M`B|qOnr!%kj&rO@pe!Sd=qG#uJ(zB$gG-eS zXE*bL4OzWyjoPG%>YrnM*7Hv`TpO}Ms_GP&>j3g{3NsaQMy5`X1IT=XUw4z zh5wwSn@lM8qnpLI_RrsF(~UF(fXfN^SsWsX(3e#-xS#1uj(zzPslFi(9D`*WLeA&1 z5M&pLO1nrxxA_jz;u2zB9v1ZRm6I2D+GiiR<)eaE>UXM7*^yL zs+A?vZc?XR%D1G%86O_BR!*@?=M%AOXg4@NJea0muIu&>pY}OEJy6ZZ&cH_Jg1zvm zbxS)rf4a4f00T4*L%#Au?57nF$)c1 z2N6DNs}zgWfnS=-b4?uDttzbtu`c5V{G-ayvmu;r-BzgfX<{)3H_QV;HQX0L`M#_6 z%0SiU3QhBnf%$w>&3;=u;?MATiI3@el6ju#5Blg8u*=M!t!UV*wpSb*R1j-aJ~pTz z)NqvhXaIw|laE@$D%#8rXl!>kjyDEId%vw75q^uwL`5}gMJJw$F>1u_6ZYuWc!?r8GxiUL>(rmcXG8vm!U8|j8 zPPS@aw+T$P9Jae8>~LwgJe6li%<~6J=0)P5#Oo8pqp)$adll;}wx1>y^IrsFbqN}^ zK)}O5Z*GgHIb^fW)ds`XJ70N@iUs*9mj&$k)O^lX8B$rw)>`Ag;Q4`0cYVv|za?(O zK-Jn$Ep+rtSWF##1-H}_sn@hKvg6Tv7iY#3*^mP={U~y?dy-M8xv5?H8gcxZij(T> zmi&5;+@GF*2j7dkzgjLk_88l62u!LQ-+_|<0@WZa5?p3Y%s=mkGQo(lLNnoNxsY)y zC3K`Bq(u1j>1)3?l)tHIpkgBWz&pQNxPiO%Q?eMp(a$2&CdDvJ<%xarwXZ%`zkR}z z{?JX<1k@6FuLwFtDc)&IAiw0J5x;c%DCGEaPBc1{Sj%0$K(Ki(DlON@X7VComBUo- z?}ii16Bc7D@ccFDiD%1xI4cZoj}|66P;4SwzFUCmBvF5r+p##EWBXx*0Xjc>uK463 z*qoW)A%|S3PnJjFm6F1V8jCEM@h245Bb!3DN&rv;w66$o-wSc`6AifPVLrwqURmYN z>9ROu7Kr1m`2B)8kSHD%OIQDhH50&7?@jjyY17f=45r`zCB1aaMg)kDOmWY`6|uZ} z4E>#J(-3|J4l1|rn@42*a8p|vA~U~1wGK^MbIPZXns2U@ZrC8^a_SofrNmUgHK_<0 zv{vs%L()?W_pKvn9*Qd|=m+etHwAH*m;N;A1=~)M1#ple<;oxJ7Qrcsw*y@ILFHq$ zj~!hje_>X8R?wxRV)1@yP*~(^JIE~FNRG!d`V_&HTzmVGb!Ec(hzG4>%Abr_ec*y! z>&?4cUey6|z+3WO+nL(UQdKul+9?z?a&Y*rxk4-cP08`8vRqCZZW;uKT|r1^S8zjV zpaOV|SRC!e@l^MRuND-S8Ys*n=m&K74;0cOm$xzZ!s8cO3&%LT}vJ zZ8aLdyss{4rUlo}wZDWpEEHrE6K~w!#+0Fx=uQedtT|wt`$`4RTEX^NvBg1~a{YC{ zNLbz!F7w>;mRWw$Pa}Jx?mJu~t09b@B{x9qf>vE(Ngf3CBWbWf*?JSEgs8E=-eQX( z$1AWdA6e#LqK`9fDD-#pvW&?G%&TtN;;+m@814K(*lA6XW*ZQ<7DhY=Y^y_+4s=8l zY8mSC=Afn6c$1_*QGT4_vi#CtRrE zdfAxhEcxbN|D%BEz|GeFX^DyqtI;Vb(l5v4!w26lw%p{@?D+3jUf{y|5T5R3u!-nO zZIiFqkD3c?XvNAfoJm+8w2g4BNpVK_E&67yO4lgl7*%|TEfQY@MDaC=jar9x)@Xi1 z?RL0{M3kGRJj|#+o_{qNzi0cKBTWpV5Nk%>`~RVCg9)XBy7&^e8P1~3aKbD1SV9h? z4nf(@F!pnhT-4lu5bTq0ID>LD3UW>k^zP(8<;v;V{td3%Seg9*Vjhy;2!|2`Y#>@N zMVoDNn#Wf4?ihF7<(r<-5n`A;&2Hme+ogC z3@j#k0YD<*+rgCUJOqb0ql{>WB%q_uRpm5ekliTDNp|X%5$g!f9_&F((tQ=FZoMCiL^H%GN zJaS8&ATo=8;L@@IYMy=c3Kio$CRA}MZZMft<%;F)zsQ49)}*i>?F{b>=h$O;_5;*% zYr)Mw8W@SVpxJNaec5>4GiodZ3BE9&3#%K1u9>u6K7H9_HQ3pr$%Zj*vZv?W`yu!< z8AA8yUNg*Cu}NX{Ink1$?fwHTFx?4gI%bArRTKQYTYtFPbpQ!9-g(}U{h zS^9R|n}Re!=dmg)K_cXwC0HbQOo_M4Dw=UNm?W)ZM~-?V?LZP{N&$ zsJcT%oCxv8_?;2u3U!yK!g)2Q8PD_)cA5oGRDZR#T7OITJQi%twTP^jE**TVer1B) zbxW&AMt1EwozUqn&<){x^&I!QELhpSq?=?Wyu9(rKaXw*oRpKXwrDTW;NaUZP|Qni z6;+;6G$}nAC7fvj#(B2q07Iu@2*=R;`5zdlH=eA>w`M2SH{Dm4R$D0#3xPX|692S*A=tI>Ai?L{<|6{1 zxvRD^iGd>S#TgE~5VBUq8X!{)pcPDV-(*4i8a@TZu*dDcU4RiBj`jeo>inkN8Dkng zWWHVg_g!y{!pc!5G>IrV5Hy&G^k9Il$D7s;*XPJKeQD7dyI5IQA2ws$x5)-118$TD zj5bVpW0;EZVcA|$#+6}_WQK%LoY9_tXd{;}9F=2;2zc! zWdcyoQ@G1tD=TjtnIWSriUp6#fy!L*e0uA*PYu2+C+8>nHk$qwfD)QyG~seE04|AM zTLRn-Y38T4Z5~v5JwlR$Zd!}{BG zf!fla>0wJ<>m~s%M{i|nGSmCAmqB`PKnTDH-rhvlb9Ym0W**B{%pFfZ` z%UsUnEJtG_&_t-|fQ;rxN8Z$W@0=EEn$ zsh{o8R1Kk&8T_|gX4Q1I=Aqwr`YrPJR6#nU^-RMWiR&li@PRXhij*7FpusT{e|VaNShh&v;dPpZqfeVg`ahjq%J zvKw(wK#x4#>{cBi@D**d6|UUd*mjA?VbrAI-%RxWLf?_jkYrXsv;^Ci3JAASLD>f8 zdC8ra)xB|=mqD^ymm<;Q61fprI4L-@*layI0T*?ameWtBbL+&~Ae@_`cr}K!KE46Z z`CuE;PF#MZnx?OE?bre$5=EQKGiX^&A22e5yrI@t8yc@phlMhX@9L}WjROI0S4*pP z4Cj;mIEOJ7iOQ<^rXNZgWM0+KHTBa&U9!H9q%|^#GaIm++M@P%iZS@IU}VEMjb`iKl7$)8x$vakTpE!10$BK1y`xTQSP%vv1mD#R3)>Ff64pC~?IS_CNp%s7SdWKk)f>?H z(}8|y3+10oAWRZL1ti0(*}PLv7|6gTX&p~c=^i+|s12A-j07E+?7JMOJC-kotq6+v zp}8vB0W~kpSX_^r@PEfdI^5*PIt7}(3rP_V$$#vN&}Qjwr>%2Tr0I7y=SwSqV@Ivt zn4ESfDO2Xaz;m~Z>@=IlJ&hiT+2mj&bW1YMpW$(t+w zp&s+_waDdiH(2rry+e6$|El7R(-xD%PhCaF4kE!Z@E8#i;8XoC)3C(OCU(g^# zT~s1Nk8{CV?$mllLYRl21M3d3rk1x2ahmtVnw4NdJ@=I3xBtElmQ_RCca-O5<-hIv zpSbpn%Tc%h~0Nd7sdhRjuumvM~iIWKV)T&`b^o{FOI z34yq}fA@ass*FeJkr7+PBK(Q5e)&&dXK4t3On1(8+|W1|iM7t<_h*lth7zL5!BvaU z?WQgpQV}6XJ>heiE(uji>rbN9+Zc#9}u0 z=|=o`A%d%YEWoI=N)*fO+5Y>a;l@e{%NP5(LD-H4J=j)a%Niq$^myxsDBa)hr9?Ft zrX+PNxl6few^$;JPM+~L2Gd{Avs_@ZhCm@Se~)cC zCa3Pm1j^zw`9~t>a%!>n>UCn(JSt|uQBjY&n*tlq3CjjiI;8N0JkAISOofXrNEbQCe`zqi zKLro0{VMHiwvc)qMs>iFl8XkF5NWA{E>I#Y(ySlonp_mP1|dWnL(}=nz`t;LMh8#+ z3h>Xy{?oM;exC2c+XzHuhMW}d1|7E3DApTl6;w*?*ELn`8gZ0k+Hoxe^NdC4r zI#m2wSAfyb#7+&c`N$l1ctQCmhEZw?_Mz<9CBZ!tW4#!D5gq}53$}Qr10^7p{=Pl( zU>|uW_C)1)+1Hvh4X_CVb}?t-;;iWfUmo=G5hQ2^Ke!b0LH75)?+}35aP$-Z zT%dLVagbVHy5U#qfPG2sUNDNKzcMpFO|@fc?g(1X$Tv^?R%$-PpF8}QJ5s%vUx>Rx z@f|JULg|WjKGXeuS8f)C^E=Y^uq!$-{FCcyM_qLrf*amGlFlMBn_?ppF|`P>*dD z&PZ+^>l!~ko$7n^6|Wm<@c}~{E{W!W8(Ip|U@rR=^Vs+2xUT_zx@IDn(c99}6^@ z!Een%<^yeDWrUVXKq2RaoF6kkPFCc9?O7td*^*~lWkzF#VIDqmvyCS+*|m$Lt-ZZ# zg}cg%!oU{Tt9vI}q2es(a*7n(Y^3oi{ojo7JO(v#4s&+ChQ*MU)+dSM-?ByzMHxHF z9f_t?vRJI| z4u{8FBF+p~)po~Y7y|15PZ{UOCFx&eaz=pFeSCazrr^7L*8E5z{NFc+>XOF&xM_@P zQdcT(_sG{_i3lHTq)MFTAhB?s28g7uiq&f$!nC-zErr;vaB z#CUHKF5I{O+%4xr(S2z^~yXR z5}>sMdKAFHu5>zy>sQDF4GG~8yV-+g)7z}yz7=EKb5;6<2OC1_<6kPJZ4h0Jd`)dt zyhJHJ%e`RthA)iRD(4UXrB3~>Y4jI1NH5@!Qp`4*3-YO2IAVUyh#P*y9m7@pDSO4j z2iX{FUQV@NxNKf#rTB$Ulm7BvpysDnNcr>#E^?!cx}3>Z#nyl2ZL9(z1W~2me-v5x z@FUzSnH!+=*}9$IgJm~mhVQ(^zHl@tq(WBERoh}0=p}4Qm9^(Wy=zc zo1Kw46$76{Of`_83=g#lB~_`J`fn`$U_B(qw0EwaSGL=ftuRB!0P1_VrbQOsdiFoa zI|BUQ-wOaZ|ER@uJa)_U``W+Vt17A4Wf=mpE=NBSSIwq1R^;Wtq|FAw@3eIjAWJ#!;F|Jaf!Mdc zFB%I8k`OEr{-HgFX=%0O5DnmFj-DPNK{JAU$`7MhQ3Q+%N&}{dBn&;(Pf7CGUNgGu_lj z6gI%;KFGTVgh#{1BsrDgA)T+K!7_8=Gbs0w5Q703sM%2#d1&uZ2%q>XyoBH!3u5q@ ji-;d@RD^`>{4E>P*d_6jJR9=11}GUxMTtr=qi_EQr81c) literal 0 HcmV?d00001 diff --git a/packages/tailwindcss/playground/src/assets/react.svg b/packages/tailwindcss/playground/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/packages/tailwindcss/playground/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/tailwindcss/playground/src/index.css b/packages/tailwindcss/playground/src/index.css new file mode 100644 index 0000000..6cc4daf --- /dev/null +++ b/packages/tailwindcss/playground/src/index.css @@ -0,0 +1,69 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #9f1a8f; + text-decoration: inherit; +} +a:hover { + color: #9f1a8f; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #9f1a8f; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #9F1A8F; + } + button { + background-color: #f9f9f9; + } +} diff --git a/packages/tailwindcss/playground/src/index.tsx b/packages/tailwindcss/playground/src/index.tsx new file mode 100644 index 0000000..06a4dd2 --- /dev/null +++ b/packages/tailwindcss/playground/src/index.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { Main } from './main'; +import './index.css' + + +const container = document.querySelector('#root'); +const root = createRoot(container); + +root.render(
); diff --git a/packages/tailwindcss/playground/src/main.css b/packages/tailwindcss/playground/src/main.css new file mode 100644 index 0000000..ff29d41 --- /dev/null +++ b/packages/tailwindcss/playground/src/main.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #9F1A8Faa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/packages/tailwindcss/playground/src/main.tsx b/packages/tailwindcss/playground/src/main.tsx new file mode 100644 index 0000000..67c183c --- /dev/null +++ b/packages/tailwindcss/playground/src/main.tsx @@ -0,0 +1,32 @@ +import React, { useState } from "react"; +import "./main.css"; +import reactLogo from "./assets/react.svg"; +import FarmLogo from "./assets/logo.png"; +export function Main() { + const [count, setCount] = useState(0); + console.log("rendering Main component") + return ( + <> + +

Farm + React

+
+ +

+ Edit src/main.tsx and save to test HMR +

+
+

+ Click on the Farm and React logos to learn more +

+ + ); +} diff --git a/packages/tailwindcss/playground/src/typings.d.ts b/packages/tailwindcss/playground/src/typings.d.ts new file mode 100644 index 0000000..fa0a2da --- /dev/null +++ b/packages/tailwindcss/playground/src/typings.d.ts @@ -0,0 +1,3 @@ +declare module '*.svg'; +declare module '*.png'; +declare module '*.css'; diff --git a/packages/tailwindcss/playground/tailwind.config.js b/packages/tailwindcss/playground/tailwind.config.js new file mode 100644 index 0000000..57b6e16 --- /dev/null +++ b/packages/tailwindcss/playground/tailwind.config.js @@ -0,0 +1,13 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + }, + }, + plugins: [], +} + diff --git a/packages/tailwindcss/playground/tsconfig.json b/packages/tailwindcss/playground/tsconfig.json new file mode 100644 index 0000000..7a7611e --- /dev/null +++ b/packages/tailwindcss/playground/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} \ No newline at end of file diff --git a/packages/tailwindcss/playground/tsconfig.node.json b/packages/tailwindcss/playground/tsconfig.node.json new file mode 100644 index 0000000..8d42325 --- /dev/null +++ b/packages/tailwindcss/playground/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["farm.config.ts"] +} diff --git a/packages/tailwindcss/pnpm-lock.yaml b/packages/tailwindcss/pnpm-lock.yaml new file mode 100644 index 0000000..cfb83ee --- /dev/null +++ b/packages/tailwindcss/pnpm-lock.yaml @@ -0,0 +1,3470 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@farmfe/plugin-tools': + specifier: latest + version: 0.1.1 + + playground: + dependencies: + '@farmfe/plugin-tailwindcss': + specifier: workspace:* + version: link:.. + clsx: + specifier: ^1.2.1 + version: 1.2.1 + react: + specifier: '18' + version: 18.3.1 + react-dom: + specifier: '18' + version: 18.3.1(react@18.3.1) + devDependencies: + '@farmfe/cli': + specifier: ^1.0.2 + version: 1.0.4 + '@farmfe/core': + specifier: ^1.3.0 + version: 1.3.16 + '@farmfe/plugin-react': + specifier: ^1.2.0 + version: 1.2.1 + '@types/react': + specifier: '18' + version: 18.3.5 + '@types/react-dom': + specifier: '18' + version: 18.3.0 + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.41) + core-js: + specifier: ^3.36.1 + version: 3.38.1 + postcss: + specifier: ^8.4.41 + version: 8.4.41 + react-refresh: + specifier: ^0.14.0 + version: 0.14.2 + tailwindcss: + specifier: ^3.4.10 + version: 3.4.10 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/runtime@7.25.6': + resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} + engines: {node: '>=6.9.0'} + + '@changesets/apply-release-plan@7.0.4': + resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} + + '@changesets/assemble-release-plan@6.0.3': + resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} + + '@changesets/changelog-git@0.2.0': + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + + '@changesets/cli@2.27.7': + resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} + hasBin: true + + '@changesets/config@3.0.2': + resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.1': + resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} + + '@changesets/get-release-plan@4.0.3': + resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.0': + resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + + '@changesets/logger@0.1.0': + resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + + '@changesets/parse@0.4.0': + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + + '@changesets/pre@2.0.0': + resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + + '@changesets/read@0.6.0': + resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + + '@changesets/should-skip-package@0.1.0': + resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.0.0': + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + + '@changesets/write@0.3.1': + resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + + '@farmfe/cli@1.0.4': + resolution: {integrity: sha512-bau/2P7DylHUDksUvbvJod/Ew4dl5ZICYVgTNn0EfrJM/ZuwLaV8LjmPo0pM4y1QOZmg/N3B9DwkXO89DrutYQ==} + engines: {node: '>= 16'} + hasBin: true + + '@farmfe/core-darwin-arm64@1.3.16': + resolution: {integrity: sha512-Q0xpDBphj5/lmKfe/9uQaUloCvCemY8HLaAjs/y2y9GSO+4xin1+hW8snHlWyFADbpOxH492G6jSc2SJNeVe2Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@farmfe/core-darwin-x64@1.3.16': + resolution: {integrity: sha512-VdPbDCjdTXIkNbi1xHNYBALFddqqO3qZQM5WXc775Zw6h2h/mSovsdsWrnyIxnA/Kh/uSJiade78Blng2Dr9og==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@farmfe/core-linux-arm64-gnu@1.3.16': + resolution: {integrity: sha512-9Cs7oE3YJcnpe0fbFX/PKXHtgTW7W04tkj1D0j3e2aM5JNOQkHeBVXy81ccA6I2EDD0+66TmViIDJIHQscEbMQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@farmfe/core-linux-arm64-musl@1.3.16': + resolution: {integrity: sha512-6jzKIMo1x3ZdVMw2nWlnkoKBAdxwrL+tqPgCJc1vAhlWSxY0q6NZHCKetXbxMWm9DXjPxuocAPVYjm8UZlX00Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@farmfe/core-linux-x64-gnu@1.3.16': + resolution: {integrity: sha512-gtQz7wlCE5HFi3BfF7EiJeXRHAD8BDx27sHt+Txg/27esDhaAjsPKUltI+bsAqwPwYFIYtyT9kr33p1OYemDfg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@farmfe/core-linux-x64-musl@1.3.16': + resolution: {integrity: sha512-EUKUdeslQ759dOGS7nJflzfUIoKZCWb1vuOpwRrGHvrVCI0wjwVsi8FKnqEFqoiEZpX6CV86Kxccr3Up8cnElw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@farmfe/core-win32-arm64-msvc@1.3.16': + resolution: {integrity: sha512-P6QTB/fCbmpP8QuOJRVKwmNKdopEg1dCcU6BRJD1X9qBoDeyOU1hLUv6a7WVj++qy79PeO4dm1J0qMIEJjcOUw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@farmfe/core-win32-ia32-msvc@1.3.16': + resolution: {integrity: sha512-KAGuklsZYoKStwB7AuPPM707TNmewLtYwBRvPslEzTMVqI7Z12m3BU59+a+T/hzBmOvqMysBYEoSAbH/r3/8PA==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@farmfe/core-win32-x64-msvc@1.3.16': + resolution: {integrity: sha512-qggAzgOKx/zU8/KtHrJy4kFyRAftLIMwu/c3xnKsnn/PoEylm3VHWDgl8Elc89Q7blJ2l49xUmQ22CucaMqj5w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@farmfe/core@1.3.16': + resolution: {integrity: sha512-F0sf8AzqZLQU06q6msgR9Aa49g3YxTm5m8NZlYSm5gOQ6IfVtrQE7L748gTnnDtk6SCKvzk38OyIVxldrUnZ3A==} + engines: {node: '>=16.15.1'} + + '@farmfe/plugin-react-darwin-arm64@1.2.1': + resolution: {integrity: sha512-LPVY6qXwc/V7DahmsEHxWAwEL4YH9jaOtV+T/HwCV50LNATPol7T8xWIUDKuaE5ERccfu2dycYVRtx4imCrVKg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@farmfe/plugin-react-darwin-x64@1.2.1': + resolution: {integrity: sha512-Zi6M7LqfTIZkmHdkFX1ouARRqjneC1AYFcoJDXvKpSzTMtmKkfowql0MAMKc+LCuLoeBigifoNYoVOeeuFE/ew==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@farmfe/plugin-react-linux-arm64-gnu@1.2.1': + resolution: {integrity: sha512-NbVx21i0oiMQbfJ1TkEtcKVNR2APZf+DsHocwpcZO6/2StGtXUIC8y/KZH0+T62UtxKFr883LxUGvavlLNm6sw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@farmfe/plugin-react-linux-arm64-musl@1.2.1': + resolution: {integrity: sha512-uXVSeV6SiSG/LPosplOnpdmKoN7Y0bIFIec2WRT0xtxeUBNPLIIYLhTbxkqym+dIsVoJlSQlwJt0yWFTpPObXQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@farmfe/plugin-react-linux-x64-gnu@1.2.1': + resolution: {integrity: sha512-XKyF0tQQWnOqKb9Lt/nap35WUc+BwTuOf4nVSPUgP1IsuG1oJLqS6qGzS8p4tb3eGGTygHTYkkpbfQHVrGiqtw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@farmfe/plugin-react-linux-x64-musl@1.2.1': + resolution: {integrity: sha512-xgbAiZMFmYP+6xbljNBA7y89fsZmCxmbuYw1zoOCUoSBOEpHbcA3/QzGj20rQhGF+qgxAoVmLwQnm9SUOttmiw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@farmfe/plugin-react-win32-arm64-msvc@1.2.1': + resolution: {integrity: sha512-YuJ8VnCXsRTn/7AnBxFoPfCuTecr8fJRe+WktLT6y1U4/u2bUUVYTnFM46/0JvBxVN48O+LQGg+3FUrAtZe31A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@farmfe/plugin-react-win32-ia32-msvc@1.2.1': + resolution: {integrity: sha512-vaa2tK8N1GExPC+MLlVd6LIvBZgPDugnMzueQt6t3H+TvKpbSIhIdzD959ORwCQUbl01L33/xZJBOLliT1+Acw==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@farmfe/plugin-react-win32-x64-msvc@1.2.1': + resolution: {integrity: sha512-PSrbsXdhQ64awgNWvig3UHUyt6LRFJQ/WUESmw/OUpPlnfqfpw42wNiG8xcMWGJ/CompksFJT4v7jt5eCrrkbg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@farmfe/plugin-react@1.2.1': + resolution: {integrity: sha512-Uv/WspjlJLf1Rmjqf39bXfq0NDf8m771jnvpMtT95xCdcL3Yqcmua5M4rkVTn607UGZHwBVJB+VM/TORKeOUpg==} + + '@farmfe/plugin-tools@0.1.1': + resolution: {integrity: sha512-7ox1VgCI5AWFrIUQVLEYGdqCSGNBJYRC0Yhu5sK0a/sR/OSaZa97MdiKA80LkM6UgK+dDFy49rPre10U/M7yww==} + hasBin: true + + '@farmfe/runtime-plugin-hmr@3.5.6': + resolution: {integrity: sha512-VNvxRKEgPdDmScmxWhTV90Grg3GrMwVZHOr6svoqmRYxsgx83GJ391RXjmPrU0inCW1iTdQL5kkDzNqSFqLrSw==} + + '@farmfe/runtime-plugin-import-meta@0.2.2': + resolution: {integrity: sha512-xrWJOHbmhVJX+a6LtRpv5wvj/uqpPQFZIKeHfT+mqybKRSC9+JxDgOySLzYUbT8beSTtXgcxGXf55EN3Byd0ng==} + + '@farmfe/runtime@0.12.3': + resolution: {integrity: sha512-4gpTOWbQ6IpDODCUbm3N7Ulqv6awcE5aQ+kAlxhErz/q5iRFVCT/HyOoUQH3ZhZgPUQ8UYxgABGmjhYFY/ILSA==} + + '@farmfe/utils@0.0.1': + resolution: {integrity: sha512-QLbgNrojcvxfumXA/H329XAXhoCahmeSH3JmaiwwJEGS2QAmWfgAJMegjwlt6OmArGVO4gSbJ7Xbmm1idZZs+g==} + + '@farmfe/utils@0.1.0': + resolution: {integrity: sha512-neNJQGqV7XL4XifG1uHOBFSFLy2yx1/DVZNRA7nfeEAXEksVZTwWA+fZrYEaI0w7Sw6K/9NYn9Jgpn+NAT0mcg==} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@koa/cors@5.0.0': + resolution: {integrity: sha512-x/iUDjcS90W69PryLDIMgFyV21YLTnG9zOpPXS7Bkt2b8AsY3zZsIpOLBkYr9fBcF3HbkKaER5hOBZLfpLgYNw==} + engines: {node: '>= 14.0.0'} + + '@ljharb/through@2.3.13': + resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} + engines: {node: '>= 0.4'} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@mdn/browser-compat-data@5.5.49': + resolution: {integrity: sha512-FNYbYIA8WEff/+A8iMGstZhArpgy5ZxZ9uQRsBQ+qXsiKTYn3WjxpCmJRw3CFUOqFlQSZDkC3v1y3BijRnE1Pg==} + + '@napi-rs/cli@2.18.4': + resolution: {integrity: sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==} + engines: {node: '>= 10'} + hasBin: true + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@swc/helpers@0.5.12': + resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} + + '@types/http-proxy@1.17.15': + resolution: {integrity: sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@22.5.1': + resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==} + + '@types/object-path@0.11.4': + resolution: {integrity: sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw==} + + '@types/prop-types@15.7.12': + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + + '@types/react@18.3.5': + resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/ua-parser-js@0.7.39': + resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bplist-parser@0.2.0: + resolution: {integrity: sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==} + engines: {node: '>= 5.10.0'} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bundle-name@3.0.0: + resolution: {integrity: sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==} + engines: {node: '>=12'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cache-content-type@1.0.1: + resolution: {integrity: sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==} + engines: {node: '>= 6.0.0'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + caniuse-lite@1.0.30001655: + resolution: {integrity: sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookies@0.9.1: + resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==} + engines: {node: '>= 0.8'} + + core-js@3.38.1: + resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==} + + cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-equal@1.0.1: + resolution: {integrity: sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@3.0.0: + resolution: {integrity: sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==} + engines: {node: '>=12'} + + default-browser@4.0.0: + resolution: {integrity: sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==} + engines: {node: '>=14.16'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dotenv-expand@11.0.6: + resolution: {integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==} + engines: {node: '>=12'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + electron-to-chromium@1.5.13: + resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@7.2.0: + resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} + engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + farm-browserslist-generator@1.0.0: + resolution: {integrity: sha512-UGd1eWtSLOqxl0tlexRwpsy1JbHdO39iCDZKzdr7khBKYFXL5PDjjcyj1xelt5YUnLNB2vPAb8P+uD8IqRnBJQ==} + engines: {node: '>=16.15.1', npm: '>=7.0.0', pnpm: '>=3.2.0', yarn: '>=1.13'} + + farm-plugin-replace-dirname-darwin-arm64@0.2.1: + resolution: {integrity: sha512-9FThv/qoFuj3cJjv9P6YnXbBwPQ5TwGjnr50ejXdZn13Ehz0+7w7EscbRsZHNvT7p24p6i0Y9NUSallcWc2syw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + farm-plugin-replace-dirname-darwin-x64@0.2.1: + resolution: {integrity: sha512-Msqrh8mAPBbEpANpa0z9uQBr1/MO+PaHgBxym/aNs1vpxB4KAs6JQWYKtO+Ob7JzFyV6d9lIRhpnpxzxTqSIfA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + farm-plugin-replace-dirname-linux-arm64-gnu@0.2.1: + resolution: {integrity: sha512-ZKuxGu9G01CW521uTQHh+IP8pcT/NolGQfcQuEmBpD8epJ8per8Ps52fS05u5TGniaOg+ySZpt7HxbX+++k1YQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + farm-plugin-replace-dirname-linux-arm64-musl@0.2.1: + resolution: {integrity: sha512-m3gH8ggczbRYTHZSNp3LjIQIcqhvDO4O78bxXc8O1ozKD8M47/YfQLyQV06M7H4rZ8s6XV3Bb1kAcRAASp3M5A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + farm-plugin-replace-dirname-linux-x64-gnu@0.2.1: + resolution: {integrity: sha512-MehKkoM2RFw3sCnEu9nCbXKjxtC3hfTad0h/dC+Z8iEBcLEReVLoNzHWWUa6BxkxqDtB82/BWO/ObSUj/VUnwQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + farm-plugin-replace-dirname-linux-x64-musl@0.2.1: + resolution: {integrity: sha512-o1qPZi16N/sHOteZYJVv6UmZFK3QKpVQrywk/4spJI0mPH9A9Y+G6iBE2Tqjb3d+1Hb6phr++EBJHZ2x1ajtGQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + farm-plugin-replace-dirname-win32-arm64-msvc@0.2.1: + resolution: {integrity: sha512-Xn/wYFkgb7SsTlSaefFtvxNbXEVdvZB854b/rBZu47+MRQpSnBIPwnTGcqo8eNTMjtnY4beGGtcd78iqMVAHFQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + farm-plugin-replace-dirname-win32-ia32-msvc@0.2.1: + resolution: {integrity: sha512-YtIu5CS/BSgbQZb1qjaBg0cEKvB4vCIbBxNn64H468zwliPbE93SAIyiwu6cL3la59cjBP4sEbz4ZAWyY9GoMQ==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + farm-plugin-replace-dirname-win32-x64-msvc@0.2.1: + resolution: {integrity: sha512-KUAf4rcv3Nz+CpGs4zr+ZRu4hWRp7SHQBgpX+mb0hhMjRvn+LoWm2qCL2q9Gp3jsTDVmzjPbyZxp/9UJKx13lQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + farm-plugin-replace-dirname@0.2.1: + resolution: {integrity: sha512-aJ4euQzxoq0sVu4AwXrNQflHJrSZdrdApGEyVRtN6KiCop3CHXnTg9ydlyCNXN2unQB283aNjojvCd5E/32KgA==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + figures@5.0.0: + resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} + engines: {node: '>=14'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-assert@1.5.0: + resolution: {integrity: sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==} + engines: {node: '>= 0.8'} + + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + + http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + + http-proxy-middleware@3.0.0: + resolution: {integrity: sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@4.3.1: + resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} + engines: {node: '>=14.18.0'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inquirer@9.2.12: + resolution: {integrity: sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==} + engines: {node: '>=14.18.0'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@3.0.0: + resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} + engines: {node: '>=10'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isbot@3.8.0: + resolution: {integrity: sha512-vne1mzQUTR+qsMLeCBL9+/tgnDXRyc2pygLGl/WsgA+EZKIiB5Ehu0CiVTHIIk30zhJ24uGz4M5Ppse37aR0Hg==} + engines: {node: '>=12'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + keygrip@1.1.0: + resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} + engines: {node: '>= 0.6'} + + koa-compose@4.1.0: + resolution: {integrity: sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==} + + koa-compress@5.1.1: + resolution: {integrity: sha512-UgMIN7ZoEP2DuoSQmD6CYvFSLt0NReGlc2qSY4bO4Oq0L56OiD9pDG41Kj/zFmVY/A3Wvmn4BqKcfq5H30LGIg==} + engines: {node: '>= 12'} + + koa-connect@2.1.0: + resolution: {integrity: sha512-O9pcFafHk0oQsBevlbTBlB9co+2RUQJ4zCzu3qJPmGlGoeEZkne+7gWDkecqDPSbCtED6LmhlQladxs6NjOnMQ==} + + koa-convert@2.0.0: + resolution: {integrity: sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==} + engines: {node: '>= 10'} + + koa-is-json@1.0.0: + resolution: {integrity: sha512-+97CtHAlWDx0ndt0J8y3P12EWLwTLMXIfMnYDev3wOTwH/RpBGMlfn4bDXlMEg1u73K6XRE9BbUp+5ZAYoRYWw==} + + koa-send@5.0.1: + resolution: {integrity: sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ==} + engines: {node: '>= 8'} + + koa-static@5.0.0: + resolution: {integrity: sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==} + engines: {node: '>= 7.6.0'} + + koa@2.15.3: + resolution: {integrity: sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==} + engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + loglevel@1.9.1: + resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + engines: {node: '>= 0.6.0'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.53.0: + resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-path@0.11.8: + resolution: {integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==} + engines: {node: '>= 10.12.0'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + only@0.0.2: + resolution: {integrity: sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==} + + open@9.1.0: + resolution: {integrity: sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==} + engines: {node: '>=14.16'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.41: + resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} + engines: {node: ^10 || ^12 || >=14} + + preferred-pm@3.1.4: + resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} + engines: {node: '>=10'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-path@1.4.0: + resolution: {integrity: sha512-i1xevIst/Qa+nA9olDxLWnLk8YZbi8R/7JPbCMcgyWaFR6bKWaexgJgEB5oc2PKMjYdrHynyz0NY+if+H98t1w==} + engines: {node: '>= 0.8'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-applescript@5.0.0: + resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==} + engines: {node: '>=12'} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@3.4.10: + resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==} + engines: {node: '>=14.0.0'} + hasBin: true + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + titleize@3.0.0: + resolution: {integrity: sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==} + engines: {node: '>=12'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tsscmp@1.0.6: + resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==} + engines: {node: '>=0.6.x'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + + ua-parser-js@1.0.38: + resolution: {integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + walkdir@0.4.1: + resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==} + engines: {node: '>=6.0.0'} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yaml@2.5.0: + resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==} + engines: {node: '>= 14'} + hasBin: true + + ylru@1.4.0: + resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==} + engines: {node: '>= 4.0.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@1.5.0: + resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} + engines: {node: '>=16.0.0'} + peerDependencies: + zod: ^3.18.0 + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@babel/runtime@7.25.6': + dependencies: + regenerator-runtime: 0.14.1 + + '@changesets/apply-release-plan@7.0.4': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/config': 3.0.2 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.6.3 + + '@changesets/assemble-release-plan@6.0.3': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.6.3 + + '@changesets/changelog-git@0.2.0': + dependencies: + '@changesets/types': 6.0.0 + + '@changesets/cli@2.27.7': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/apply-release-plan': 7.0.4 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.2 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/get-release-plan': 4.0.3 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.1 + '@manypkg/get-packages': 1.1.3 + '@types/semver': 7.5.8 + ansi-colors: 4.1.3 + chalk: 2.4.2 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + mri: 1.2.0 + outdent: 0.5.0 + p-limit: 2.3.0 + preferred-pm: 3.1.4 + resolve-from: 5.0.0 + semver: 7.6.3 + spawndamnit: 2.0.0 + term-size: 2.2.1 + + '@changesets/config@3.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/logger': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.1': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + chalk: 2.4.2 + fs-extra: 7.0.1 + semver: 7.6.3 + + '@changesets/get-release-plan@4.0.3': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/config': 3.0.2 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.0': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 2.0.0 + + '@changesets/logger@0.1.0': + dependencies: + chalk: 2.4.2 + + '@changesets/parse@0.4.0': + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + + '@changesets/pre@2.0.0': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.0': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + chalk: 2.4.2 + fs-extra: 7.0.1 + p-filter: 2.1.0 + + '@changesets/should-skip-package@0.1.0': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.0.0': {} + + '@changesets/write@0.3.1': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + + '@farmfe/cli@1.0.4': + dependencies: + cac: 6.7.14 + cross-spawn: 7.0.3 + inquirer: 9.2.12 + walkdir: 0.4.1 + + '@farmfe/core-darwin-arm64@1.3.16': + optional: true + + '@farmfe/core-darwin-x64@1.3.16': + optional: true + + '@farmfe/core-linux-arm64-gnu@1.3.16': + optional: true + + '@farmfe/core-linux-arm64-musl@1.3.16': + optional: true + + '@farmfe/core-linux-x64-gnu@1.3.16': + optional: true + + '@farmfe/core-linux-x64-musl@1.3.16': + optional: true + + '@farmfe/core-win32-arm64-msvc@1.3.16': + optional: true + + '@farmfe/core-win32-ia32-msvc@1.3.16': + optional: true + + '@farmfe/core-win32-x64-msvc@1.3.16': + optional: true + + '@farmfe/core@1.3.16': + dependencies: + '@farmfe/runtime': 0.12.3 + '@farmfe/runtime-plugin-hmr': 3.5.6 + '@farmfe/runtime-plugin-import-meta': 0.2.2 + '@farmfe/utils': 0.1.0 + '@koa/cors': 5.0.0 + '@swc/helpers': 0.5.12 + chokidar: 3.6.0 + deepmerge: 4.3.1 + dotenv: 16.4.5 + dotenv-expand: 11.0.6 + execa: 7.2.0 + farm-browserslist-generator: 1.0.0 + farm-plugin-replace-dirname: 0.2.1 + fast-glob: 3.3.2 + fs-extra: 11.2.0 + http-proxy-middleware: 3.0.0 + is-plain-object: 5.0.0 + koa: 2.15.3 + koa-compress: 5.1.1 + koa-connect: 2.1.0 + koa-static: 5.0.0 + lodash.debounce: 4.0.8 + loglevel: 1.9.1 + open: 9.1.0 + ws: 8.18.0 + zod: 3.23.8 + zod-validation-error: 1.5.0(zod@3.23.8) + optionalDependencies: + '@farmfe/core-darwin-arm64': 1.3.16 + '@farmfe/core-darwin-x64': 1.3.16 + '@farmfe/core-linux-arm64-gnu': 1.3.16 + '@farmfe/core-linux-arm64-musl': 1.3.16 + '@farmfe/core-linux-x64-gnu': 1.3.16 + '@farmfe/core-linux-x64-musl': 1.3.16 + '@farmfe/core-win32-arm64-msvc': 1.3.16 + '@farmfe/core-win32-ia32-msvc': 1.3.16 + '@farmfe/core-win32-x64-msvc': 1.3.16 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@farmfe/plugin-react-darwin-arm64@1.2.1': + optional: true + + '@farmfe/plugin-react-darwin-x64@1.2.1': + optional: true + + '@farmfe/plugin-react-linux-arm64-gnu@1.2.1': + optional: true + + '@farmfe/plugin-react-linux-arm64-musl@1.2.1': + optional: true + + '@farmfe/plugin-react-linux-x64-gnu@1.2.1': + optional: true + + '@farmfe/plugin-react-linux-x64-musl@1.2.1': + optional: true + + '@farmfe/plugin-react-win32-arm64-msvc@1.2.1': + optional: true + + '@farmfe/plugin-react-win32-ia32-msvc@1.2.1': + optional: true + + '@farmfe/plugin-react-win32-x64-msvc@1.2.1': + optional: true + + '@farmfe/plugin-react@1.2.1': + optionalDependencies: + '@farmfe/plugin-react-darwin-arm64': 1.2.1 + '@farmfe/plugin-react-darwin-x64': 1.2.1 + '@farmfe/plugin-react-linux-arm64-gnu': 1.2.1 + '@farmfe/plugin-react-linux-arm64-musl': 1.2.1 + '@farmfe/plugin-react-linux-x64-gnu': 1.2.1 + '@farmfe/plugin-react-linux-x64-musl': 1.2.1 + '@farmfe/plugin-react-win32-arm64-msvc': 1.2.1 + '@farmfe/plugin-react-win32-ia32-msvc': 1.2.1 + '@farmfe/plugin-react-win32-x64-msvc': 1.2.1 + + '@farmfe/plugin-tools@0.1.1': + dependencies: + '@farmfe/utils': 0.1.0 + '@napi-rs/cli': 2.18.4 + cac: 6.7.14 + + '@farmfe/runtime-plugin-hmr@3.5.6': + dependencies: + core-js: 3.38.1 + + '@farmfe/runtime-plugin-import-meta@0.2.2': + dependencies: + core-js: 3.38.1 + + '@farmfe/runtime@0.12.3': + dependencies: + core-js: 3.38.1 + + '@farmfe/utils@0.0.1': {} + + '@farmfe/utils@0.1.0': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@koa/cors@5.0.0': + dependencies: + vary: 1.1.2 + + '@ljharb/through@2.3.13': + dependencies: + call-bind: 1.0.7 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.25.6 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@mdn/browser-compat-data@5.5.49': {} + + '@napi-rs/cli@2.18.4': {} + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@swc/helpers@0.5.12': + dependencies: + tslib: 2.7.0 + + '@types/http-proxy@1.17.15': + dependencies: + '@types/node': 22.5.1 + + '@types/node@12.20.55': {} + + '@types/node@22.5.1': + dependencies: + undici-types: 6.19.8 + + '@types/object-path@0.11.4': {} + + '@types/prop-types@15.7.12': {} + + '@types/react-dom@18.3.0': + dependencies: + '@types/react': 18.3.5 + + '@types/react@18.3.5': + dependencies: + '@types/prop-types': 15.7.12 + csstype: 3.1.3 + + '@types/semver@7.5.8': {} + + '@types/ua-parser-js@0.7.39': {} + + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + array-union@2.1.0: {} + + autoprefixer@10.4.20(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-lite: 1.0.30001655 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.1 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + big-integer@1.6.52: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bplist-parser@0.2.0: + dependencies: + big-integer: 1.6.52 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.23.3: + dependencies: + caniuse-lite: 1.0.30001655 + electron-to-chromium: 1.5.13 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.23.3) + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bundle-name@3.0.0: + dependencies: + run-applescript: 5.0.0 + + bytes@3.1.2: {} + + cac@6.7.14: {} + + cache-content-type@1.0.1: + dependencies: + mime-types: 2.1.35 + ylru: 1.4.0 + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + camelcase-css@2.0.1: {} + + caniuse-lite@1.0.30001655: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.3.0: {} + + chardet@0.7.0: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + ci-info@3.9.0: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-width@4.1.0: {} + + clone@1.0.4: {} + + clsx@1.2.1: {} + + co@4.6.0: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + commander@4.1.1: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.53.0 + + content-disposition@0.5.4: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookies@0.9.1: + dependencies: + depd: 2.0.0 + keygrip: 1.1.0 + + core-js@3.38.1: {} + + cross-spawn@5.1.0: + dependencies: + lru-cache: 4.1.5 + shebang-command: 1.2.0 + which: 1.3.1 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssesc@3.0.0: {} + + csstype@3.1.3: {} + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + deep-equal@1.0.1: {} + + deepmerge@4.3.1: {} + + default-browser-id@3.0.0: + dependencies: + bplist-parser: 0.2.0 + untildify: 4.0.0 + + default-browser@4.0.0: + dependencies: + bundle-name: 3.0.0 + default-browser-id: 3.0.0 + execa: 7.2.0 + titleize: 3.0.0 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + define-lazy-prop@3.0.0: {} + + delegates@1.0.0: {} + + depd@1.1.2: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + detect-indent@6.1.0: {} + + didyoumean@1.2.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dotenv-expand@11.0.6: + dependencies: + dotenv: 16.4.5 + + dotenv@16.4.5: {} + + eastasianwidth@0.2.0: {} + + ee-first@1.1.1: {} + + electron-to-chromium@1.5.13: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@1.0.2: {} + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@5.0.0: {} + + esprima@4.0.1: {} + + eventemitter3@4.0.7: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + execa@7.2.0: + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 4.3.1 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 3.0.7 + strip-final-newline: 3.0.0 + + extendable-error@0.1.7: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + farm-browserslist-generator@1.0.0: + dependencies: + '@mdn/browser-compat-data': 5.5.49 + '@types/object-path': 0.11.4 + '@types/semver': 7.5.8 + '@types/ua-parser-js': 0.7.39 + browserslist: 4.23.3 + caniuse-lite: 1.0.30001655 + isbot: 3.8.0 + object-path: 0.11.8 + semver: 7.6.3 + ua-parser-js: 1.0.38 + + farm-plugin-replace-dirname-darwin-arm64@0.2.1: + optional: true + + farm-plugin-replace-dirname-darwin-x64@0.2.1: + optional: true + + farm-plugin-replace-dirname-linux-arm64-gnu@0.2.1: + optional: true + + farm-plugin-replace-dirname-linux-arm64-musl@0.2.1: + optional: true + + farm-plugin-replace-dirname-linux-x64-gnu@0.2.1: + optional: true + + farm-plugin-replace-dirname-linux-x64-musl@0.2.1: + optional: true + + farm-plugin-replace-dirname-win32-arm64-msvc@0.2.1: + optional: true + + farm-plugin-replace-dirname-win32-ia32-msvc@0.2.1: + optional: true + + farm-plugin-replace-dirname-win32-x64-msvc@0.2.1: + optional: true + + farm-plugin-replace-dirname@0.2.1: + dependencies: + '@changesets/cli': 2.27.7 + '@farmfe/utils': 0.0.1 + cac: 6.7.14 + optionalDependencies: + farm-plugin-replace-dirname-darwin-arm64: 0.2.1 + farm-plugin-replace-dirname-darwin-x64: 0.2.1 + farm-plugin-replace-dirname-linux-arm64-gnu: 0.2.1 + farm-plugin-replace-dirname-linux-arm64-musl: 0.2.1 + farm-plugin-replace-dirname-linux-x64-gnu: 0.2.1 + farm-plugin-replace-dirname-linux-x64-musl: 0.2.1 + farm-plugin-replace-dirname-win32-arm64-msvc: 0.2.1 + farm-plugin-replace-dirname-win32-ia32-msvc: 0.2.1 + farm-plugin-replace-dirname-win32-x64-msvc: 0.2.1 + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + figures@5.0.0: + dependencies: + escape-string-regexp: 5.0.0 + is-unicode-supported: 1.3.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-yarn-workspace-root2@1.2.16: + dependencies: + micromatch: 4.0.8 + pkg-dir: 4.2.0 + + follow-redirects@1.15.6(debug@4.3.6): + optionalDependencies: + debug: 4.3.6 + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + fraction.js@4.3.7: {} + + fresh@0.5.2: {} + + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-stream@6.0.1: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + graceful-fs@4.2.11: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.0.3 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-assert@1.5.0: + dependencies: + deep-equal: 1.0.1 + http-errors: 1.8.1 + + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + + http-errors@1.8.1: + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + + http-proxy-middleware@3.0.0: + dependencies: + '@types/http-proxy': 1.17.15 + debug: 4.3.6 + http-proxy: 1.18.1(debug@4.3.6) + is-glob: 4.0.3 + is-plain-obj: 3.0.0 + micromatch: 4.0.8 + transitivePeerDependencies: + - supports-color + + http-proxy@1.18.1(debug@4.3.6): + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.6(debug@4.3.6) + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + + human-id@1.0.2: {} + + human-signals@2.1.0: {} + + human-signals@4.3.1: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + inherits@2.0.3: {} + + inherits@2.0.4: {} + + inquirer@9.2.12: + dependencies: + '@ljharb/through': 2.3.13 + ansi-escapes: 4.3.2 + chalk: 5.3.0 + cli-cursor: 3.1.0 + cli-width: 4.1.0 + external-editor: 3.1.0 + figures: 5.0.0 + lodash: 4.17.21 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.0.10: + dependencies: + has-tostringtag: 1.0.2 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-interactive@1.0.0: {} + + is-number@7.0.0: {} + + is-plain-obj@3.0.0: {} + + is-plain-object@5.0.0: {} + + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-windows@1.0.2: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isbot@3.8.0: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.6: {} + + js-tokens@4.0.0: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keygrip@1.1.0: + dependencies: + tsscmp: 1.0.6 + + koa-compose@4.1.0: {} + + koa-compress@5.1.1: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + http-errors: 1.8.1 + koa-is-json: 1.0.0 + + koa-connect@2.1.0: {} + + koa-convert@2.0.0: + dependencies: + co: 4.6.0 + koa-compose: 4.1.0 + + koa-is-json@1.0.0: {} + + koa-send@5.0.1: + dependencies: + debug: 4.3.6 + http-errors: 1.8.1 + resolve-path: 1.4.0 + transitivePeerDependencies: + - supports-color + + koa-static@5.0.0: + dependencies: + debug: 3.2.7 + koa-send: 5.0.1 + transitivePeerDependencies: + - supports-color + + koa@2.15.3: + dependencies: + accepts: 1.3.8 + cache-content-type: 1.0.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookies: 0.9.1 + debug: 4.3.6 + delegates: 1.0.0 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 1.0.2 + escape-html: 1.0.3 + fresh: 0.5.2 + http-assert: 1.5.0 + http-errors: 1.8.1 + is-generator-function: 1.0.10 + koa-compose: 4.1.0 + koa-convert: 2.0.0 + on-finished: 2.4.1 + only: 0.0.2 + parseurl: 1.3.3 + statuses: 1.5.0 + type-is: 1.6.18 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} + + lines-and-columns@1.2.4: {} + + load-yaml-file@0.2.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.debounce@4.0.8: {} + + lodash.startcase@4.4.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + loglevel@1.9.1: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@10.4.3: {} + + lru-cache@4.1.5: + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + + media-typer@0.3.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-db@1.53.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minipass@7.1.2: {} + + mri@1.2.0: {} + + ms@2.1.2: {} + + ms@2.1.3: {} + + mute-stream@1.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.7: {} + + negotiator@0.6.3: {} + + node-releases@2.0.18: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + object-path@0.11.8: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + only@0.0.2: {} + + open@9.1.0: + dependencies: + default-browser: 4.0.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 2.2.0 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + os-tmpdir@1.0.2: {} + + outdent@0.5.0: {} + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-map@2.1.0: {} + + p-try@2.2.0: {} + + package-json-from-dist@1.0.0: {} + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-type@4.0.0: {} + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pify@4.0.1: {} + + pirates@4.0.6: {} + + pkg-dir@4.2.0: + dependencies: + find-up: 4.1.0 + + postcss-import@15.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.41): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.41 + + postcss-load-config@4.0.2(postcss@8.4.41): + dependencies: + lilconfig: 3.1.2 + yaml: 2.5.0 + optionalDependencies: + postcss: 8.4.41 + + postcss-nested@6.2.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.41: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + preferred-pm@3.1.4: + dependencies: + find-up: 5.0.0 + find-yarn-workspace-root2: 1.2.16 + path-exists: 4.0.0 + which-pm: 2.2.0 + + prettier@2.8.8: {} + + pseudomap@1.0.2: {} + + queue-microtask@1.2.3: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-refresh@0.14.2: {} + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.1 + pify: 4.0.1 + strip-bom: 3.0.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regenerator-runtime@0.14.1: {} + + requires-port@1.0.0: {} + + resolve-from@5.0.0: {} + + resolve-path@1.4.0: + dependencies: + http-errors: 1.6.3 + path-is-absolute: 1.0.1 + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + reusify@1.0.4: {} + + run-applescript@5.0.0: + dependencies: + execa: 5.1.1 + + run-async@3.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.1: + dependencies: + tslib: 2.7.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@7.6.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + setprototypeof@1.1.0: {} + + setprototypeof@1.2.0: {} + + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@1.0.0: {} + + shebang-regex@3.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + slash@3.0.0: {} + + source-map-js@1.2.0: {} + + spawndamnit@2.0.0: + dependencies: + cross-spawn: 5.1.0 + signal-exit: 3.0.7 + + sprintf-js@1.0.3: {} + + statuses@1.5.0: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + strip-bom@3.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-final-newline@3.0.0: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@3.4.10: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.1 + postcss: 8.4.41 + postcss-import: 15.1.0(postcss@8.4.41) + postcss-js: 4.0.1(postcss@8.4.41) + postcss-load-config: 4.0.2(postcss@8.4.41) + postcss-nested: 6.2.0(postcss@8.4.41) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + term-size@2.2.1: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + titleize@3.0.0: {} + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + ts-interface-checker@0.1.13: {} + + tslib@2.7.0: {} + + tsscmp@1.0.6: {} + + type-fest@0.21.3: {} + + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + + ua-parser-js@1.0.38: {} + + undici-types@6.19.8: {} + + universalify@0.1.2: {} + + universalify@2.0.1: {} + + untildify@4.0.0: {} + + update-browserslist-db@1.1.0(browserslist@4.23.3): + dependencies: + browserslist: 4.23.3 + escalade: 3.2.0 + picocolors: 1.0.1 + + util-deprecate@1.0.2: {} + + vary@1.1.2: {} + + walkdir@0.4.1: {} + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + which-pm@2.2.0: + dependencies: + load-yaml-file: 0.2.0 + path-exists: 4.0.0 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + ws@8.18.0: {} + + yallist@2.1.2: {} + + yaml@2.5.0: {} + + ylru@1.4.0: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@1.5.0(zod@3.23.8): + dependencies: + zod: 3.23.8 + + zod@3.23.8: {} diff --git a/packages/tailwindcss/pnpm-workspace.yaml b/packages/tailwindcss/pnpm-workspace.yaml new file mode 100644 index 0000000..bf93c07 --- /dev/null +++ b/packages/tailwindcss/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - '.' + - playground diff --git a/packages/tailwindcss/scripts/func.js b/packages/tailwindcss/scripts/func.js new file mode 100644 index 0000000..ebd41be --- /dev/null +++ b/packages/tailwindcss/scripts/func.js @@ -0,0 +1,3 @@ +import binPath from "./index.js"; + +export default (options)=>[binPath,options]; diff --git a/packages/tailwindcss/scripts/index.d.ts b/packages/tailwindcss/scripts/index.d.ts new file mode 100644 index 0000000..2a1c6bb --- /dev/null +++ b/packages/tailwindcss/scripts/index.d.ts @@ -0,0 +1,3 @@ +import type { IPluginOptions } from '../options'; +declare const binPath: (options?: IPluginOptions) => [string, IPluginOptions]; +export default binPath; diff --git a/packages/tailwindcss/scripts/index.js b/packages/tailwindcss/scripts/index.js new file mode 100644 index 0000000..a65c8ea --- /dev/null +++ b/packages/tailwindcss/scripts/index.js @@ -0,0 +1,124 @@ +import { existsSync, readFileSync } from 'fs'; +import { createRequire } from 'module'; +import { dirname, join } from 'path'; +import { fileURLToPath } from 'url'; + +const { platform, arch } = process; +const currentDir = dirname(fileURLToPath(import.meta.url)); + +let binPath = null; + +const require = createRequire(import.meta.url); + +function isMusl() { + // For Node 10 + if (!process.report || typeof process.report.getReport !== 'function') { + try { + return readFileSync('/usr/bin/ldd', 'utf8').includes('musl'); + } catch (e) { + return true; + } + } else { + const { glibcVersionRuntime } = process.report.getReport().header; + return !glibcVersionRuntime; + } +} + +switch (platform) { + case 'win32': + switch (arch) { + case 'x64': + if (existsSync(join(currentDir, '../npm/win32-x64-msvc/index.farm'))) { + binPath = join(currentDir, '../npm/win32-x64-msvc/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-win32-x64-msvc'); + } + + break; + case 'ia32': + if (existsSync(join(currentDir, '../npm/win32-ia32-msvc/index.farm'))) { + binPath = join(currentDir, '../npm/win32-ia32-msvc/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-win32-ia32-msvc'); + } + + break; + case 'arm64': + if (existsSync(join(currentDir, '../npm/win32-arm64-msvc/index.farm'))) { + binPath = join(currentDir, '../npm/win32-arm64-msvc/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-win32-arm64-msvc'); + } + + break; + default: + throw new Error(`Unsupported architecture on Windows: ${arch}`); + } + break; + case 'darwin': + switch (arch) { + case 'x64': + if (existsSync(join(currentDir, '../npm/darwin-x64/index.farm'))) { + binPath = join(currentDir, '../npm/darwin-x64/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-darwin-x64'); + } + break; + case 'arm64': + if (existsSync(join(currentDir, '../npm/darwin-arm64/index.farm'))) { + binPath = join(currentDir, '../npm/darwin-arm64/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-darwin-arm64'); + } + break; + default: + throw new Error(`Unsupported architecture on macOS: ${arch}`); + } + break; + case 'linux': + switch (arch) { + case 'x64': + if (isMusl()) { + if (existsSync(join(currentDir, '../npm/linux-x64-musl/index.farm'))) { + binPath = join(currentDir, '../npm/linux-x64-musl/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-linux-x64-musl'); + } + } else { + if (existsSync(join(currentDir, '../npm/linux-x64-gnu/index.farm'))) { + binPath = join(currentDir, '../npm/linux-x64-gnu/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-linux-x64-gnu'); + } + } + + break; + + case 'arm64': + if (isMusl()) { + if ( + existsSync(join(currentDir, '../npm/linux-arm64-musl/index.farm')) + ) { + binPath = join(currentDir, '../npm/linux-arm64-musl/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-linux-arm64-musl'); + } + } else { + if ( + existsSync(join(currentDir, '../npm/linux-arm64-gnu/index.farm')) + ) { + binPath = join(currentDir, '../npm/linux-arm64-gnu/index.farm'); + } else { + binPath = require.resolve('farmfe-plugin-tailwindcss-linux-arm64-gnu'); + } + } + break; + default: + throw new Error(`Unsupported architecture on Linux: ${arch}`); + } + break; + default: + throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`); +} + +export default binPath; diff --git a/packages/tailwindcss/scripts/watch.sh b/packages/tailwindcss/scripts/watch.sh new file mode 100644 index 0000000..255a484 --- /dev/null +++ b/packages/tailwindcss/scripts/watch.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +npm run build diff --git a/packages/tailwindcss/src/lib.rs b/packages/tailwindcss/src/lib.rs new file mode 100644 index 0000000..1866924 --- /dev/null +++ b/packages/tailwindcss/src/lib.rs @@ -0,0 +1,58 @@ +#![deny(clippy::all)] +use farmfe_core::{config::Config, plugin::Plugin}; +use farmfe_macro_plugin::farm_plugin; +use std::path::PathBuf; +use tailwind_ast::parse_tailwind; +use tailwind_css::{TailwindBuilder, TailwindInstruction}; +use tailwindcss_oxide::{ChangedContent, Scanner}; +#[farm_plugin] +pub struct FarmfePluginTailwindcss {} +impl FarmfePluginTailwindcss { + fn new(config: &Config, options: String) -> Self { + parse_oxide_string(); + Self {} + } +} + +impl Plugin for FarmfePluginTailwindcss { + fn name(&self) -> &str { + "FarmfePluginTailwindcss" + } +} + +fn parse_oxide_string() -> String { + fn filter_tailwind_atom_css(css: Vec) -> Vec { + let need_filter = vec![ + "content".to_string(), + "word-break".to_string(), + "text-overflow".to_string(), + ]; + css + .iter() + .filter(|&c| { + let styles = parse_tailwind(c).unwrap()[0].clone(); + if need_filter.contains(c) { + return false; + } + TailwindInstruction::from(styles).get_instance().is_ok() + }) + .cloned() + .collect() + } + + let mut tailwind = TailwindBuilder::default(); + let mut scanner = Scanner::new(None, None); + let res = scanner.scan_content( + [ChangedContent { + file: Some(PathBuf::from( + "/Users/cherry7/Documents/open/farm-fe/plugins/packages/tailwindcss/playground/index.html", + )), + content: None, + }] + .to_vec(), + ); + let styles: String = filter_tailwind_atom_css(res).join(" "); + tailwind.trace(&styles, false).unwrap(); + let bundle = tailwind.bundle().unwrap(); + bundle +} diff --git a/packages/tailwindcss/src/test.css b/packages/tailwindcss/src/test.css new file mode 100644 index 0000000..e69de29 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 82af36e..46de642 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,6 +33,12 @@ importers: packages/strip: {} + packages/tailwindcss: + devDependencies: + '@farmfe/plugin-tools': + specifier: latest + version: 0.1.1 + packages/url: {} packages/virtual: {} From e2e378c7998decefaca7a2a74dd1ab20bb0e5d0e Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Sun, 1 Sep 2024 08:39:43 +0800 Subject: [PATCH 02/12] feat: tailwindcss parse --- packages/tailwindcss/src/lib.rs | 44 +++---------------------------- packages/tailwindcss/src/parse.rs | 39 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 41 deletions(-) create mode 100644 packages/tailwindcss/src/parse.rs diff --git a/packages/tailwindcss/src/lib.rs b/packages/tailwindcss/src/lib.rs index 1866924..be02788 100644 --- a/packages/tailwindcss/src/lib.rs +++ b/packages/tailwindcss/src/lib.rs @@ -1,10 +1,9 @@ #![deny(clippy::all)] +mod parse; + use farmfe_core::{config::Config, plugin::Plugin}; use farmfe_macro_plugin::farm_plugin; -use std::path::PathBuf; -use tailwind_ast::parse_tailwind; -use tailwind_css::{TailwindBuilder, TailwindInstruction}; -use tailwindcss_oxide::{ChangedContent, Scanner}; +use parse::parse_oxide_string; #[farm_plugin] pub struct FarmfePluginTailwindcss {} impl FarmfePluginTailwindcss { @@ -19,40 +18,3 @@ impl Plugin for FarmfePluginTailwindcss { "FarmfePluginTailwindcss" } } - -fn parse_oxide_string() -> String { - fn filter_tailwind_atom_css(css: Vec) -> Vec { - let need_filter = vec![ - "content".to_string(), - "word-break".to_string(), - "text-overflow".to_string(), - ]; - css - .iter() - .filter(|&c| { - let styles = parse_tailwind(c).unwrap()[0].clone(); - if need_filter.contains(c) { - return false; - } - TailwindInstruction::from(styles).get_instance().is_ok() - }) - .cloned() - .collect() - } - - let mut tailwind = TailwindBuilder::default(); - let mut scanner = Scanner::new(None, None); - let res = scanner.scan_content( - [ChangedContent { - file: Some(PathBuf::from( - "/Users/cherry7/Documents/open/farm-fe/plugins/packages/tailwindcss/playground/index.html", - )), - content: None, - }] - .to_vec(), - ); - let styles: String = filter_tailwind_atom_css(res).join(" "); - tailwind.trace(&styles, false).unwrap(); - let bundle = tailwind.bundle().unwrap(); - bundle -} diff --git a/packages/tailwindcss/src/parse.rs b/packages/tailwindcss/src/parse.rs new file mode 100644 index 0000000..ba5ae45 --- /dev/null +++ b/packages/tailwindcss/src/parse.rs @@ -0,0 +1,39 @@ +use std::path::PathBuf; +use tailwind_ast::parse_tailwind; +use tailwind_css::{TailwindBuilder, TailwindInstruction}; +use tailwindcss_oxide::{ChangedContent, Scanner}; + +pub fn filter_tailwind_atom_css(css: Vec) -> Vec { + let need_filter = vec![ + "content".to_string(), + "word-break".to_string(), + "text-overflow".to_string(), + ]; + css + .iter() + .filter(|&c| { + let styles = parse_tailwind(c).unwrap()[0].clone(); + if need_filter.contains(c) { + return false; + } + TailwindInstruction::from(styles).get_instance().is_ok() + }) + .cloned() + .collect() +} + +pub fn parse_oxide_string(file_path: &str) -> String { + let mut tailwind = TailwindBuilder::default(); + let mut scanner = Scanner::new(None, None); + let res = scanner.scan_content( + [ChangedContent { + file: Some(PathBuf::from(file_path)), + content: None, + }] + .to_vec(), + ); + let styles: String = filter_tailwind_atom_css(res).join(" "); + tailwind.trace(&styles, false).unwrap(); + let bundle = tailwind.bundle().unwrap(); + bundle +} From 67d556f3c3fcbb5979a0262401f3c5502879f97f Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Sun, 1 Sep 2024 22:41:35 +0800 Subject: [PATCH 03/12] feat: parse_tailwind_config --- Cargo.lock | 4 + packages/tailwindcss/Cargo.toml | 6 + .../tailwindcss/playground/farm.config.ts | 16 ++- .../tailwindcss/playground/tailwind.config.js | 11 +- packages/tailwindcss/src/config.rs | 136 ++++++++++++++++++ packages/tailwindcss/src/lib.rs | 20 ++- packages/tailwindcss/src/parser/mod.rs | 2 + .../src/parser/parse_tailwind_config.rs | 48 +++++++ .../parse_tailwind_css.rs} | 1 - 9 files changed, 233 insertions(+), 11 deletions(-) create mode 100644 packages/tailwindcss/src/config.rs create mode 100644 packages/tailwindcss/src/parser/mod.rs create mode 100644 packages/tailwindcss/src/parser/parse_tailwind_config.rs rename packages/tailwindcss/src/{parse.rs => parser/parse_tailwind_css.rs} (99%) diff --git a/Cargo.lock b/Cargo.lock index 1afb043..8b805a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -865,9 +865,13 @@ dependencies = [ name = "farmfe_plugin_tailwindcss" version = "0.0.1" dependencies = [ + "css-color", "farmfe_core", "farmfe_macro_plugin", + "farmfe_toolkit", "farmfe_toolkit_plugin_types", + "serde", + "serde_json", "tailwind-ast", "tailwind-css", "tailwindcss-oxide", diff --git a/packages/tailwindcss/Cargo.toml b/packages/tailwindcss/Cargo.toml index 3544919..6b72b15 100644 --- a/packages/tailwindcss/Cargo.toml +++ b/packages/tailwindcss/Cargo.toml @@ -10,6 +10,12 @@ crate-type = ["cdylib", "rlib"] farmfe_toolkit_plugin_types = { workspace = true } farmfe_macro_plugin = { workspace = true } farmfe_core = { workspace = true } +farmfe_toolkit= { workspace = true } + tailwind-css = "0.13.0" tailwind-ast = "0.4.1" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0" +css-color = "0.2.8" + tailwindcss-oxide = { path = "./crates/oxide" } diff --git a/packages/tailwindcss/playground/farm.config.ts b/packages/tailwindcss/playground/farm.config.ts index 820d98b..335edcf 100644 --- a/packages/tailwindcss/playground/farm.config.ts +++ b/packages/tailwindcss/playground/farm.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from "@farmfe/core"; import react from '@farmfe/plugin-react'; -import farmPlugin from '@farmfe/plugin-tailwindcss'; +import tailwindcss from '@farmfe/plugin-tailwindcss'; export default defineConfig({ compilation: { @@ -12,6 +12,18 @@ export default defineConfig({ }, plugins: [ react({ runtime: "automatic" }), - farmPlugin() + tailwindcss({ + content: ["./src/**/*.{js,jsx,ts,tsx}"], + theme: { + colors: { + 'primary': '#0070f3', + 'secondary': '#0070f3', + 'tertiary': '#0070f3', + 'quaternary': '#0070f3', + 'quinary': '#0070f3', + 'senary': '#0070f3', + } + } + }) ], }); diff --git a/packages/tailwindcss/playground/tailwind.config.js b/packages/tailwindcss/playground/tailwind.config.js index 57b6e16..5c8088b 100644 --- a/packages/tailwindcss/playground/tailwind.config.js +++ b/packages/tailwindcss/playground/tailwind.config.js @@ -6,8 +6,15 @@ export default { ], theme: { extend: { - }, + colors: { + 'primary': '#0070f3', + 'secondary': '#0070f3', + 'tertiary': '#0070f3', + 'quaternary': '#0070f3', + 'quinary': '#0070f3', + 'senary': '#0070f3', + } + } }, - plugins: [], } diff --git a/packages/tailwindcss/src/config.rs b/packages/tailwindcss/src/config.rs new file mode 100644 index 0000000..be48715 --- /dev/null +++ b/packages/tailwindcss/src/config.rs @@ -0,0 +1,136 @@ +use std::collections::BTreeMap; + +use serde::de::{self}; +use serde::{self, Deserialize}; +use tailwind_css::{BreakPointSystem, PaletteSystem}; + +use std::collections::HashMap; + +use serde::Deserializer; + +#[derive(Clone, Debug)] +pub enum LengthUnit { + Fraction(u32, u32), + Unit(f32, &'static str), +} + +#[derive(Deserialize, Clone, Debug)] +#[serde(untagged)] +enum LengthUnitHelper { + Fraction { numerator: u32, denominator: u32 }, + Unit { value: f32, unit: String }, +} +impl<'de> Deserialize<'de> for LengthUnit { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let helper = LengthUnitHelper::deserialize(deserializer)?; + match helper { + LengthUnitHelper::Fraction { + numerator, + denominator, + } => Ok(LengthUnit::Fraction(numerator, denominator)), + LengthUnitHelper::Unit { value, unit } => Ok(LengthUnit::Unit( + value, + match unit.as_str() { + "px" => "px", + "em" => "em", + _ => return Err(de::Error::custom(format!("unexpected unit: {}", unit))), + }, + )), + } + } +} + +#[derive(serde::Deserialize, Clone, Debug)] +pub struct FontSize { + pub size: LengthUnit, + pub height: LengthUnit, +} + +#[derive(serde::Deserialize, Clone, Debug)] +pub struct FontSystem { + pub size: BTreeMap, + pub family: BTreeMap>, + pub tracking: BTreeMap, +} + +// BreakPointSystem +#[derive(serde::Deserialize, Clone, Debug)] +pub struct BreakPoint { + /// min-width + /// unit: px + pub width: usize, +} + +#[derive(serde::Deserialize, Clone, Debug, PartialEq)] +pub struct Srgb { + /// The red component. + pub red: f32, + /// The green component. + pub green: f32, + /// The blue component. + pub blue: f32, + /// The alpha component. + pub alpha: f32, +} + +#[derive(serde::Deserialize, Clone, Debug)] +pub struct Palette { + /// Allow gradients? + pub gradient: bool, + /// min-width + /// unit: px + pub key_points: BTreeMap, +} + +#[derive(serde::Deserialize, Clone, Debug)] +pub struct PreflightSystem { + /// disable all preflight + pub disable: bool, + /// ## Default margins are removed + /// Preflight removes all of the default margins from elements like headings, blockquotes, paragraphs, etc. + /// This makes it harder to accidentally rely on margin values applied by the user-agent stylesheet that are not part of your spacing scale. + pub remove_margins: bool, + /// ## Headings are unstyled + /// All heading elements are completely unstyled by default, and have the same font-size and font-weight as normal text. + pub unstyle_head: bool, + /// ## Lists are unstyled + /// Ordered and unordered lists are unstyled by default, with no bullets/numbers and no margin or padding. + pub unstyle_list: bool, + /// ## Images are block-level + /// Images and other replaced elements (like svg, video, canvas, and others) are display: block by default. + pub block_level_image: bool, + /// ## Border styles are reset globally + /// In order to make it easy to add a border by simply adding the border class, Tailwind overrides the default border styles for all elements with the following rules: + pub unstyle_border: bool, + /// ## Buttons have a default outline + /// To ensure that we provide accessible styles out of the box, we made sure that buttons have a default outline. You can of course override this by applying focus:ring or similar utilities to your buttons. + pub button_outline: bool, + /// Custom field for preflight + pub custom: String, +} + +#[derive(serde::Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct Theme { + pub colors: Option>, + pub fonts: Option, + pub preflight: Option, + pub screens: Option>, +} + +#[derive(serde::Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct TailwindCssConfig { + pub content: Option>, + pub theme: Option, +} + +pub struct TailwindRsConfig { + pub palettes: Option, + pub fonts: Option, + pub preflight: Option, + pub screens: Option, +} diff --git a/packages/tailwindcss/src/lib.rs b/packages/tailwindcss/src/lib.rs index be02788..1b4d9f3 100644 --- a/packages/tailwindcss/src/lib.rs +++ b/packages/tailwindcss/src/lib.rs @@ -1,15 +1,23 @@ #![deny(clippy::all)] -mod parse; +mod config; +mod parser; -use farmfe_core::{config::Config, plugin::Plugin}; +use config::TailwindCssConfig; +use farmfe_core::{ + config::Config, + plugin::Plugin, + serde_json::{self}, +}; use farmfe_macro_plugin::farm_plugin; -use parse::parse_oxide_string; + #[farm_plugin] -pub struct FarmfePluginTailwindcss {} +pub struct FarmfePluginTailwindcss { + tw_config: TailwindCssConfig, +} impl FarmfePluginTailwindcss { fn new(config: &Config, options: String) -> Self { - parse_oxide_string(); - Self {} + let tw_config: TailwindCssConfig = serde_json::from_str(&options).unwrap(); + Self { tw_config } } } diff --git a/packages/tailwindcss/src/parser/mod.rs b/packages/tailwindcss/src/parser/mod.rs new file mode 100644 index 0000000..1e7f7db --- /dev/null +++ b/packages/tailwindcss/src/parser/mod.rs @@ -0,0 +1,2 @@ +mod parse_tailwind_css; +mod parse_tailwind_config; diff --git a/packages/tailwindcss/src/parser/parse_tailwind_config.rs b/packages/tailwindcss/src/parser/parse_tailwind_config.rs new file mode 100644 index 0000000..4738013 --- /dev/null +++ b/packages/tailwindcss/src/parser/parse_tailwind_config.rs @@ -0,0 +1,48 @@ +use crate::config::{TailwindCssConfig, TailwindRsConfig}; +use css_color::Srgb; +use std::{collections::BTreeMap, str::FromStr}; +use tailwind_css::{BreakPointSystem, Palette, PaletteSystem}; +pub fn parse_tailwind_config( + config: &TailwindCssConfig, +) -> Result { + let mut rs_config = TailwindRsConfig { + palettes: None, + fonts: None, + preflight: None, + screens: None, + }; + + if let Some(theme) = config.theme.as_ref() { + if let Some(colors) = theme.colors.as_ref() { + let mut palettes = PaletteSystem::default(); + for (key, value) in colors.iter() { + let mut colors = BTreeMap::default(); + colors.insert(50, Srgb::from_str(value).unwrap()); + // TODO Palette is private + palettes.register( + key.to_string(), + Palette { + gradient: false, + key_points: colors, + }, + ); + } + rs_config.palettes = Some(palettes); + } + if let Some(_fonts) = theme.fonts.as_ref() { + rs_config.fonts = theme.fonts.clone(); + } + + if let Some(_) = theme.preflight.as_ref() { + rs_config.preflight = theme.preflight.clone() + } + + if let Some(screens) = theme.screens.as_ref() { + let mut rs_screes = BreakPointSystem::default(); + screens.into_iter().for_each(|s| { + rs_screes.register(s.0.clone(), s.1.clone()); + }) + } + } + Ok(rs_config) +} diff --git a/packages/tailwindcss/src/parse.rs b/packages/tailwindcss/src/parser/parse_tailwind_css.rs similarity index 99% rename from packages/tailwindcss/src/parse.rs rename to packages/tailwindcss/src/parser/parse_tailwind_css.rs index ba5ae45..4faf1d0 100644 --- a/packages/tailwindcss/src/parse.rs +++ b/packages/tailwindcss/src/parser/parse_tailwind_css.rs @@ -2,7 +2,6 @@ use std::path::PathBuf; use tailwind_ast::parse_tailwind; use tailwind_css::{TailwindBuilder, TailwindInstruction}; use tailwindcss_oxide::{ChangedContent, Scanner}; - pub fn filter_tailwind_atom_css(css: Vec) -> Vec { let need_filter = vec![ "content".to_string(), From 3257e2b21379cde8089719ab691ec9fa31bbdef7 Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Mon, 2 Sep 2024 00:07:12 +0800 Subject: [PATCH 04/12] feat: get_tailwind_builder --- packages/tailwindcss/playground/index.html | 2 - packages/tailwindcss/playground/src/main.tsx | 2 +- packages/tailwindcss/src/config.rs | 62 ++++------------ packages/tailwindcss/src/lib.rs | 3 +- .../src/parser/get_tailwind_builder.rs | 21 ++++++ packages/tailwindcss/src/parser/mod.rs | 5 +- .../src/parser/parse_tailwind_config.rs | 73 ++++++++++++------- .../src/parser/parse_tailwind_css.rs | 24 +++--- 8 files changed, 104 insertions(+), 88 deletions(-) create mode 100644 packages/tailwindcss/src/parser/get_tailwind_builder.rs diff --git a/packages/tailwindcss/playground/index.html b/packages/tailwindcss/playground/index.html index 8d229f7..9d43228 100644 --- a/packages/tailwindcss/playground/index.html +++ b/packages/tailwindcss/playground/index.html @@ -10,7 +10,5 @@
- -
diff --git a/packages/tailwindcss/playground/src/main.tsx b/packages/tailwindcss/playground/src/main.tsx index 67c183c..a8794b3 100644 --- a/packages/tailwindcss/playground/src/main.tsx +++ b/packages/tailwindcss/playground/src/main.tsx @@ -7,7 +7,7 @@ export function Main() { console.log("rendering Main component") return ( <> -
+
Farm logo diff --git a/packages/tailwindcss/src/config.rs b/packages/tailwindcss/src/config.rs index be48715..8d6590a 100644 --- a/packages/tailwindcss/src/config.rs +++ b/packages/tailwindcss/src/config.rs @@ -1,58 +1,21 @@ +use serde::{self}; use std::collections::BTreeMap; - -use serde::de::{self}; -use serde::{self, Deserialize}; -use tailwind_css::{BreakPointSystem, PaletteSystem}; +use tailwind_css::{ + BreakPointSystem, FontSystem as RsFontSystem, PaletteSystem, PreflightSystem as RsPreflightSystem, +}; use std::collections::HashMap; -use serde::Deserializer; - -#[derive(Clone, Debug)] -pub enum LengthUnit { - Fraction(u32, u32), - Unit(f32, &'static str), -} - -#[derive(Deserialize, Clone, Debug)] -#[serde(untagged)] -enum LengthUnitHelper { - Fraction { numerator: u32, denominator: u32 }, - Unit { value: f32, unit: String }, -} -impl<'de> Deserialize<'de> for LengthUnit { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let helper = LengthUnitHelper::deserialize(deserializer)?; - match helper { - LengthUnitHelper::Fraction { - numerator, - denominator, - } => Ok(LengthUnit::Fraction(numerator, denominator)), - LengthUnitHelper::Unit { value, unit } => Ok(LengthUnit::Unit( - value, - match unit.as_str() { - "px" => "px", - "em" => "em", - _ => return Err(de::Error::custom(format!("unexpected unit: {}", unit))), - }, - )), - } - } -} - #[derive(serde::Deserialize, Clone, Debug)] pub struct FontSize { - pub size: LengthUnit, - pub height: LengthUnit, + pub size: f32, + pub height: f32, } #[derive(serde::Deserialize, Clone, Debug)] pub struct FontSystem { pub size: BTreeMap, - pub family: BTreeMap>, + pub family: BTreeMap, pub tracking: BTreeMap, } @@ -126,11 +89,18 @@ pub struct Theme { pub struct TailwindCssConfig { pub content: Option>, pub theme: Option, + // compile other config eg: inline trace ... ? } pub struct TailwindRsConfig { pub palettes: Option, - pub fonts: Option, - pub preflight: Option, + pub fonts: Option, + pub preflight: Option, pub screens: Option, } + +// tailwindcss_oxide config + +pub struct TailwindOxideConfig { + pub content: Option>, +} diff --git a/packages/tailwindcss/src/lib.rs b/packages/tailwindcss/src/lib.rs index 1b4d9f3..9b236f3 100644 --- a/packages/tailwindcss/src/lib.rs +++ b/packages/tailwindcss/src/lib.rs @@ -9,14 +9,13 @@ use farmfe_core::{ serde_json::{self}, }; use farmfe_macro_plugin::farm_plugin; - #[farm_plugin] pub struct FarmfePluginTailwindcss { tw_config: TailwindCssConfig, } impl FarmfePluginTailwindcss { fn new(config: &Config, options: String) -> Self { - let tw_config: TailwindCssConfig = serde_json::from_str(&options).unwrap(); + let tw_config: TailwindCssConfig = serde_json::from_str(&options).unwrap(); Self { tw_config } } } diff --git a/packages/tailwindcss/src/parser/get_tailwind_builder.rs b/packages/tailwindcss/src/parser/get_tailwind_builder.rs new file mode 100644 index 0000000..6720b07 --- /dev/null +++ b/packages/tailwindcss/src/parser/get_tailwind_builder.rs @@ -0,0 +1,21 @@ +use crate::config::TailwindCssConfig; +use crate::parser::parse_tailwind_config::parse_tailwind_config; +use tailwind_css::TailwindBuilder; + +fn get_tailwind_builder(config: &TailwindCssConfig) -> TailwindBuilder { + let tw_config = parse_tailwind_config(config); + let mut builder = TailwindBuilder::default(); + if tw_config.fonts.is_some() { + builder.fonts = tw_config.fonts.unwrap(); + } + if tw_config.palettes.is_some() { + builder.palettes = tw_config.palettes.unwrap(); + } + if tw_config.preflight.is_some() { + builder.preflight = tw_config.preflight.unwrap(); + } + if tw_config.screens.is_some() { + builder.screens = tw_config.screens.unwrap(); + } + builder +} diff --git a/packages/tailwindcss/src/parser/mod.rs b/packages/tailwindcss/src/parser/mod.rs index 1e7f7db..8a1f9d7 100644 --- a/packages/tailwindcss/src/parser/mod.rs +++ b/packages/tailwindcss/src/parser/mod.rs @@ -1,2 +1,3 @@ -mod parse_tailwind_css; -mod parse_tailwind_config; +pub mod parse_tailwind_css; +pub mod parse_tailwind_config; +pub mod get_tailwind_builder; diff --git a/packages/tailwindcss/src/parser/parse_tailwind_config.rs b/packages/tailwindcss/src/parser/parse_tailwind_config.rs index 4738013..67ea7de 100644 --- a/packages/tailwindcss/src/parser/parse_tailwind_config.rs +++ b/packages/tailwindcss/src/parser/parse_tailwind_config.rs @@ -1,10 +1,10 @@ use crate::config::{TailwindCssConfig, TailwindRsConfig}; use css_color::Srgb; use std::{collections::BTreeMap, str::FromStr}; -use tailwind_css::{BreakPointSystem, Palette, PaletteSystem}; -pub fn parse_tailwind_config( - config: &TailwindCssConfig, -) -> Result { +use tailwind_css::{ + BreakPointSystem, FontSize, FontSystem, Palette, PaletteSystem, PreflightSystem, +}; +pub fn parse_tailwind_config(config: &TailwindCssConfig) -> TailwindRsConfig { let mut rs_config = TailwindRsConfig { palettes: None, fonts: None, @@ -13,28 +13,51 @@ pub fn parse_tailwind_config( }; if let Some(theme) = config.theme.as_ref() { - if let Some(colors) = theme.colors.as_ref() { - let mut palettes = PaletteSystem::default(); - for (key, value) in colors.iter() { - let mut colors = BTreeMap::default(); - colors.insert(50, Srgb::from_str(value).unwrap()); - // TODO Palette is private - palettes.register( - key.to_string(), - Palette { - gradient: false, - key_points: colors, - }, - ); - } - rs_config.palettes = Some(palettes); - } - if let Some(_fonts) = theme.fonts.as_ref() { - rs_config.fonts = theme.fonts.clone(); + // if let Some(colors) = theme.colors.as_ref() { + // let mut palettes = PaletteSystem::default(); + // for (key, value) in colors.iter() { + // let mut colors = BTreeMap::default(); + // colors.insert(50, Srgb::from_str(value).unwrap()); + // // TODO Palette is private + // palettes.register( + // key.to_string(), + // Palette { + // gradient: false, + // key_points: colors, + // }, + // ); + // } + // rs_config.palettes = Some(palettes); + // } + if let Some(fonts) = theme.fonts.as_ref() { + let size = fonts.size.clone(); + let family = fonts.family.clone(); + let tracking = fonts.tracking.clone(); + let mut rs_fonts = FontSystem::default(); + size.into_iter().for_each(|s| { + rs_fonts.insert_size(s.0, FontSize::new(s.1.size, s.1.height)); + }); + family.into_iter().for_each(|f| { + rs_fonts.insert_family(f.0, &f.1); + }); + tracking.into_iter().for_each(|t| { + rs_fonts.insert_tracking(t.0, t.1); + }); + + rs_config.fonts = Some(rs_fonts); } - if let Some(_) = theme.preflight.as_ref() { - rs_config.preflight = theme.preflight.clone() + if let Some(preflight) = theme.preflight.as_ref() { + let mut rs_preflight = PreflightSystem::default(); + rs_preflight.disable = preflight.disable; + rs_preflight.remove_margins = preflight.remove_margins; + rs_preflight.unstyle_head = preflight.unstyle_head; + rs_preflight.unstyle_list = preflight.unstyle_list; + rs_preflight.block_level_image = preflight.block_level_image; + rs_preflight.unstyle_border = preflight.unstyle_border; + rs_preflight.button_outline = preflight.button_outline; + rs_preflight.custom = preflight.custom.clone(); + rs_config.preflight = Some(rs_preflight); } if let Some(screens) = theme.screens.as_ref() { @@ -44,5 +67,5 @@ pub fn parse_tailwind_config( }) } } - Ok(rs_config) + rs_config } diff --git a/packages/tailwindcss/src/parser/parse_tailwind_css.rs b/packages/tailwindcss/src/parser/parse_tailwind_css.rs index 4faf1d0..552ac32 100644 --- a/packages/tailwindcss/src/parser/parse_tailwind_css.rs +++ b/packages/tailwindcss/src/parser/parse_tailwind_css.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use tailwind_ast::parse_tailwind; use tailwind_css::{TailwindBuilder, TailwindInstruction}; -use tailwindcss_oxide::{ChangedContent, Scanner}; +use tailwindcss_oxide::{scanner::detect_sources::DetectSources, GlobEntry, Scanner}; pub fn filter_tailwind_atom_css(css: Vec) -> Vec { let need_filter = vec![ "content".to_string(), @@ -21,18 +21,22 @@ pub fn filter_tailwind_atom_css(css: Vec) -> Vec { .collect() } -pub fn parse_oxide_string(file_path: &str) -> String { +pub fn parse_tailwind_css(base: &str, contents: Vec) -> String { let mut tailwind = TailwindBuilder::default(); - let mut scanner = Scanner::new(None, None); - let res = scanner.scan_content( - [ChangedContent { - file: Some(PathBuf::from(file_path)), - content: None, - }] - .to_vec(), - ); + let sources = contents + .iter() + .map(|c| { + return GlobEntry { + base: base.to_string(), + pattern: c.clone(), + }; + }) + .collect(); + let mut scanner = Scanner::new(Some(DetectSources::new(PathBuf::from(base))), Some(sources)); + let res = scanner.scan(); let styles: String = filter_tailwind_atom_css(res).join(" "); tailwind.trace(&styles, false).unwrap(); let bundle = tailwind.bundle().unwrap(); + println!("bundle: {:?}", bundle); bundle } From 9ea734bd9a03f3c4b057a31486e8ac6cc8887262 Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Mon, 2 Sep 2024 00:31:19 +0800 Subject: [PATCH 05/12] feat: get_tailwind_builder --- .../tailwindcss/src/parser/parse_tailwind_css.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/tailwindcss/src/parser/parse_tailwind_css.rs b/packages/tailwindcss/src/parser/parse_tailwind_css.rs index 552ac32..ee3d996 100644 --- a/packages/tailwindcss/src/parser/parse_tailwind_css.rs +++ b/packages/tailwindcss/src/parser/parse_tailwind_css.rs @@ -21,8 +21,11 @@ pub fn filter_tailwind_atom_css(css: Vec) -> Vec { .collect() } -pub fn parse_tailwind_css(base: &str, contents: Vec) -> String { - let mut tailwind = TailwindBuilder::default(); +pub fn parse_tailwind_css( + tw_builder: &mut TailwindBuilder, + base: &str, + contents: Vec, +) -> String { let sources = contents .iter() .map(|c| { @@ -35,8 +38,7 @@ pub fn parse_tailwind_css(base: &str, contents: Vec) -> String { let mut scanner = Scanner::new(Some(DetectSources::new(PathBuf::from(base))), Some(sources)); let res = scanner.scan(); let styles: String = filter_tailwind_atom_css(res).join(" "); - tailwind.trace(&styles, false).unwrap(); - let bundle = tailwind.bundle().unwrap(); - println!("bundle: {:?}", bundle); + tw_builder.trace(&styles, false).unwrap(); + let bundle = tw_builder.bundle().unwrap(); bundle } From e131007bc3886ff79b3c0056e268385654579096 Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Mon, 2 Sep 2024 09:42:01 +0800 Subject: [PATCH 06/12] feat: parse_tailwind_css_with_changed --- .../src/parser/parse_tailwind_css.rs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/tailwindcss/src/parser/parse_tailwind_css.rs b/packages/tailwindcss/src/parser/parse_tailwind_css.rs index ee3d996..1f40ccb 100644 --- a/packages/tailwindcss/src/parser/parse_tailwind_css.rs +++ b/packages/tailwindcss/src/parser/parse_tailwind_css.rs @@ -1,7 +1,10 @@ use std::path::PathBuf; use tailwind_ast::parse_tailwind; use tailwind_css::{TailwindBuilder, TailwindInstruction}; -use tailwindcss_oxide::{scanner::detect_sources::DetectSources, GlobEntry, Scanner}; +use tailwindcss_oxide::{ + scanner::detect_sources::DetectSources, ChangedContent, GlobEntry, Scanner, +}; + pub fn filter_tailwind_atom_css(css: Vec) -> Vec { let need_filter = vec![ "content".to_string(), @@ -25,7 +28,7 @@ pub fn parse_tailwind_css( tw_builder: &mut TailwindBuilder, base: &str, contents: Vec, -) -> String { +) -> (String, Scanner) { let sources = contents .iter() .map(|c| { @@ -40,5 +43,26 @@ pub fn parse_tailwind_css( let styles: String = filter_tailwind_atom_css(res).join(" "); tw_builder.trace(&styles, false).unwrap(); let bundle = tw_builder.bundle().unwrap(); + (bundle, scanner) +} + +pub fn parse_tailwind_css_with_changed( + tw_builder: &mut TailwindBuilder, + scanner: &mut Scanner, + changed_files: Vec, +) -> String { + let changed_content = changed_files + .iter() + .map(|c| { + return ChangedContent { + file: Some(PathBuf::from(c)), + content: None, + }; + }) + .collect(); + let res = scanner.scan_content(changed_content); + let styles: String = filter_tailwind_atom_css(res).join(" "); + tw_builder.trace(&styles, false).unwrap(); + let bundle = tw_builder.bundle().unwrap(); bundle } From d983aebf438663a3561e52cd0e65e7485d797a3b Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Mon, 2 Sep 2024 09:57:44 +0800 Subject: [PATCH 07/12] feat: arc shard --- packages/tailwindcss/src/lib.rs | 24 ++++++++++++++++++- .../src/parser/get_tailwind_builder.rs | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/packages/tailwindcss/src/lib.rs b/packages/tailwindcss/src/lib.rs index 9b236f3..d5924b9 100644 --- a/packages/tailwindcss/src/lib.rs +++ b/packages/tailwindcss/src/lib.rs @@ -2,6 +2,8 @@ mod config; mod parser; +use std::sync::{Arc, Mutex}; + use config::TailwindCssConfig; use farmfe_core::{ config::Config, @@ -9,14 +11,34 @@ use farmfe_core::{ serde_json::{self}, }; use farmfe_macro_plugin::farm_plugin; +use parser::{ + get_tailwind_builder::get_tailwind_builder, + parse_tailwind_css::{parse_tailwind_css, parse_tailwind_css_with_changed}, +}; +use tailwind_css::TailwindBuilder; +use tailwindcss_oxide::Scanner; + #[farm_plugin] pub struct FarmfePluginTailwindcss { tw_config: TailwindCssConfig, + tw_builder: Arc>, + tw_scanner: Arc>, + tw_bundle: Arc>, } impl FarmfePluginTailwindcss { fn new(config: &Config, options: String) -> Self { + let base = config.root.clone(); let tw_config: TailwindCssConfig = serde_json::from_str(&options).unwrap(); - Self { tw_config } + let contents = tw_config.content.clone(); + let mut tw_builder = get_tailwind_builder(&tw_config); + let (tw_bundle, tw_scanner) = parse_tailwind_css(&mut tw_builder, &base, contents.unwrap()); + + Self { + tw_config, + tw_builder: Arc::new(Mutex::new(tw_builder)), + tw_scanner: Arc::new(Mutex::new(tw_scanner)), + tw_bundle: Arc::new(Mutex::new(tw_bundle)), + } } } diff --git a/packages/tailwindcss/src/parser/get_tailwind_builder.rs b/packages/tailwindcss/src/parser/get_tailwind_builder.rs index 6720b07..11e0973 100644 --- a/packages/tailwindcss/src/parser/get_tailwind_builder.rs +++ b/packages/tailwindcss/src/parser/get_tailwind_builder.rs @@ -2,7 +2,7 @@ use crate::config::TailwindCssConfig; use crate::parser::parse_tailwind_config::parse_tailwind_config; use tailwind_css::TailwindBuilder; -fn get_tailwind_builder(config: &TailwindCssConfig) -> TailwindBuilder { +pub fn get_tailwind_builder(config: &TailwindCssConfig) -> TailwindBuilder { let tw_config = parse_tailwind_config(config); let mut builder = TailwindBuilder::default(); if tw_config.fonts.is_some() { From cec21c69129973181e7c7d3617383ac7966af38b Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Mon, 2 Sep 2024 13:33:47 +0800 Subject: [PATCH 08/12] fix: atomic rules that do not conform to the specification --- packages/tailwindcss/playground/src/main.tsx | 2 +- packages/tailwindcss/src/lib.rs | 35 +++++++++++- .../src/parser/parse_tailwind_css.rs | 57 ++++++++++++++++--- 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/packages/tailwindcss/playground/src/main.tsx b/packages/tailwindcss/playground/src/main.tsx index a8794b3..2a77810 100644 --- a/packages/tailwindcss/playground/src/main.tsx +++ b/packages/tailwindcss/playground/src/main.tsx @@ -7,7 +7,7 @@ export function Main() { console.log("rendering Main component") return ( <> -
+
Farm logo diff --git a/packages/tailwindcss/src/lib.rs b/packages/tailwindcss/src/lib.rs index d5924b9..b43fdbb 100644 --- a/packages/tailwindcss/src/lib.rs +++ b/packages/tailwindcss/src/lib.rs @@ -2,11 +2,15 @@ mod config; mod parser; -use std::sync::{Arc, Mutex}; +use std::{ + path::Path, + sync::{Arc, Mutex}, +}; use config::TailwindCssConfig; use farmfe_core::{ config::Config, + module::ModuleType, plugin::Plugin, serde_json::{self}, }; @@ -30,9 +34,11 @@ impl FarmfePluginTailwindcss { let base = config.root.clone(); let tw_config: TailwindCssConfig = serde_json::from_str(&options).unwrap(); let contents = tw_config.content.clone(); + if contents.is_none() { + panic!("tailwindcss config content is required"); + } let mut tw_builder = get_tailwind_builder(&tw_config); let (tw_bundle, tw_scanner) = parse_tailwind_css(&mut tw_builder, &base, contents.unwrap()); - Self { tw_config, tw_builder: Arc::new(Mutex::new(tw_builder)), @@ -46,4 +52,29 @@ impl Plugin for FarmfePluginTailwindcss { fn name(&self) -> &str { "FarmfePluginTailwindcss" } + fn transform( + &self, + param: &farmfe_core::plugin::PluginTransformHookParam, + _context: &Arc, + ) -> farmfe_core::error::Result> { + if Path::new(param.resolved_path).is_file() && !param.resolved_path.contains("node_modules") + && vec![ + ModuleType::Tsx, + ModuleType::Jsx, + ModuleType::Js, + ModuleType::Html, + ] + .contains(¶m.module_type) + { + let mut tw_builder = self.tw_builder.lock().unwrap(); + let mut tw_scanner = self.tw_scanner.lock().unwrap(); + let mut tw_bundle = self.tw_bundle.lock().unwrap(); + let changed_files = vec![param.resolved_path.to_string()]; + println!("changed_files: {:?}", changed_files); + let tw_css = parse_tailwind_css_with_changed(&mut tw_builder, &mut tw_scanner, changed_files); + println!("tw_css: {:?}", tw_css); + return Ok(None); + } + return Ok(None); + } } diff --git a/packages/tailwindcss/src/parser/parse_tailwind_css.rs b/packages/tailwindcss/src/parser/parse_tailwind_css.rs index 1f40ccb..6845432 100644 --- a/packages/tailwindcss/src/parser/parse_tailwind_css.rs +++ b/packages/tailwindcss/src/parser/parse_tailwind_css.rs @@ -1,3 +1,4 @@ +use farmfe_toolkit::fs::read_file_utf8; use std::path::PathBuf; use tailwind_ast::parse_tailwind; use tailwind_css::{TailwindBuilder, TailwindInstruction}; @@ -14,11 +15,21 @@ pub fn filter_tailwind_atom_css(css: Vec) -> Vec { css .iter() .filter(|&c| { - let styles = parse_tailwind(c).unwrap()[0].clone(); if need_filter.contains(c) { return false; } - TailwindInstruction::from(styles).get_instance().is_ok() + let styles = parse_tailwind(c).unwrap()[0].clone(); + match TailwindInstruction::from(styles).get_instance() { + Ok(rule) => { + if rule.id().contains("[]") { // ignore atomic rules that do not conform to the specification + return false; + } + return true; + } + Err(_e) => { + return false; + } + } }) .cloned() .collect() @@ -39,9 +50,21 @@ pub fn parse_tailwind_css( }) .collect(); let mut scanner = Scanner::new(Some(DetectSources::new(PathBuf::from(base))), Some(sources)); - let res = scanner.scan(); - let styles: String = filter_tailwind_atom_css(res).join(" "); - tw_builder.trace(&styles, false).unwrap(); + let rules = scanner.scan(); + if rules.is_empty() { + return (String::new(), scanner); + } + let styles = filter_tailwind_atom_css(rules); + if styles.is_empty() { + return (String::new(), scanner); + } + let styles_str = styles.join(" "); + if styles_str.is_empty() { + println!("Unsupported atomic rules: {:#?}", styles); + return (String::new(), scanner); + } + println!("tailwindcss atomic rules: {:#?}", styles); + tw_builder.trace(&styles_str, false).unwrap(); let bundle = tw_builder.bundle().unwrap(); (bundle, scanner) } @@ -56,13 +79,29 @@ pub fn parse_tailwind_css_with_changed( .map(|c| { return ChangedContent { file: Some(PathBuf::from(c)), - content: None, + content: Some(read_file_utf8(c).unwrap()), }; }) .collect(); - let res = scanner.scan_content(changed_content); - let styles: String = filter_tailwind_atom_css(res).join(" "); - tw_builder.trace(&styles, false).unwrap(); + let rules = scanner.scan_content(changed_content); + if rules.is_empty() { + return String::new(); + } + let styles = filter_tailwind_atom_css(rules); + if styles.is_empty() { + return String::new(); + } + let styles_str = styles.join(" "); + if styles_str.is_empty() { + println!("Unsupported atomic rules: {:#?}", styles); + return String::new(); + } + tw_builder.trace(&styles_str, false).unwrap(); let bundle = tw_builder.bundle().unwrap(); bundle } + +fn clean_tailwind_css(css: &str) -> String { + // + todo!() +} From 126dd0c71f961528e458e0e315244b633608cda4 Mon Sep 17 00:00:00 2001 From: CCherry07 <2405693142@qq.com> Date: Mon, 2 Sep 2024 13:40:09 +0800 Subject: [PATCH 09/12] chore: code --- packages/tailwindcss/playground/src/components/home.tsx | 3 +++ packages/tailwindcss/playground/src/main.tsx | 4 +++- packages/tailwindcss/src/lib.rs | 1 - packages/tailwindcss/src/parser/parse_tailwind_css.rs | 1 - 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 packages/tailwindcss/playground/src/components/home.tsx diff --git a/packages/tailwindcss/playground/src/components/home.tsx b/packages/tailwindcss/playground/src/components/home.tsx new file mode 100644 index 0000000..3c993fb --- /dev/null +++ b/packages/tailwindcss/playground/src/components/home.tsx @@ -0,0 +1,3 @@ +export function Home() { + return
+} diff --git a/packages/tailwindcss/playground/src/main.tsx b/packages/tailwindcss/playground/src/main.tsx index 2a77810..fc04ed8 100644 --- a/packages/tailwindcss/playground/src/main.tsx +++ b/packages/tailwindcss/playground/src/main.tsx @@ -2,12 +2,13 @@ import React, { useState } from "react"; import "./main.css"; import reactLogo from "./assets/react.svg"; import FarmLogo from "./assets/logo.png"; +import { Home } from './components/home' export function Main() { const [count, setCount] = useState(0); console.log("rendering Main component") return ( <> -
+
Farm logo @@ -16,6 +17,7 @@ export function Main() {

Farm + React

+
-

Farm + React

+

Farm + React