diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e2433d3b7ae..0153ad227ace 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,7 @@ env: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: diff --git a/.github/workflows/integration-tests-oxide.yml b/.github/workflows/integration-tests-oxide.yml index 9da82b4b873b..e647b9fb321c 100644 --- a/.github/workflows/integration-tests-oxide.yml +++ b/.github/workflows/integration-tests-oxide.yml @@ -18,6 +18,7 @@ env: jobs: test: runs-on: ubuntu-latest + timeout-minutes: 15 strategy: matrix: diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 2ad36d3dd670..c0ccebe4310e 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -18,6 +18,7 @@ jobs: contents: write # for softprops/action-gh-release to create GitHub release runs-on: macos-11 + timeout-minutes: 15 strategy: matrix: diff --git a/.github/workflows/release-insiders-oxide.yml b/.github/workflows/release-insiders-oxide.yml index 4130349ba190..5ddab368c771 100644 --- a/.github/workflows/release-insiders-oxide.yml +++ b/.github/workflows/release-insiders-oxide.yml @@ -26,6 +26,7 @@ jobs: name: Build ${{ matrix.target }} (OXIDE) runs-on: ${{ matrix.os }} + timeout-minutes: 15 steps: - uses: actions/checkout@v3 @@ -96,6 +97,7 @@ jobs: build-apple-silicon: name: Build Apple Silicon (OXIDE) runs-on: macos-latest + timeout-minutes: 15 steps: - uses: actions/checkout@v3 @@ -188,6 +190,7 @@ jobs: name: Build ${{ matrix.target }} (OXIDE) runs-on: ubuntu-latest + timeout-minutes: 15 container: image: ${{ matrix.image }} @@ -264,6 +267,7 @@ jobs: release: runs-on: ubuntu-latest + timeout-minutes: 15 name: Build and release needs: - build @@ -355,6 +359,7 @@ jobs: tailwind_oxide_release: runs-on: ubuntu-latest + timeout-minutes: 15 name: Build and release Tailwind CSS needs: diff --git a/.github/workflows/release-oxide.yml b/.github/workflows/release-oxide.yml index ad71ec657ce9..0adb204b37af 100644 --- a/.github/workflows/release-oxide.yml +++ b/.github/workflows/release-oxide.yml @@ -24,6 +24,7 @@ jobs: name: Build ${{ matrix.target }} (OXIDE) runs-on: ${{ matrix.os }} + timeout-minutes: 15 steps: - uses: actions/checkout@v3 @@ -75,6 +76,7 @@ jobs: oxide-build-apple-silicon: name: Build Apple Silicon (OXIDE) runs-on: macos-latest + timeout-minutes: 15 steps: - uses: actions/checkout@v3 @@ -156,6 +158,7 @@ jobs: name: Build ${{ matrix.target }} (OXIDE) runs-on: ubuntu-latest + timeout-minutes: 15 container: image: ${{ matrix.image }} @@ -213,6 +216,7 @@ jobs: oxide-release: runs-on: ubuntu-latest + timeout-minutes: 15 name: Build and release needs: - oxide-build @@ -285,6 +289,7 @@ jobs: tailwind_oxide_release: runs-on: ubuntu-latest + timeout-minutes: 15 name: Build and release Tailwind CSS needs: diff --git a/CHANGELOG.md b/CHANGELOG.md index fd899dc532aa..1cf0e2aa4a21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Nothing yet! +## [3.3.3] - 2023-07-13 + +### Fixed + +- Fix issue where some pseudo-element variants generated the wrong selector ([#10943](https://github.com/tailwindlabs/tailwindcss/pull/10943), [#10962](https://github.com/tailwindlabs/tailwindcss/pull/10962), [#11111](https://github.com/tailwindlabs/tailwindcss/pull/11111)) +- Make font settings propagate into buttons, inputs, etc. ([#10940](https://github.com/tailwindlabs/tailwindcss/pull/10940)) +- Fix parsing of `theme()` inside `calc()` when there are no spaces around operators ([#11157](https://github.com/tailwindlabs/tailwindcss/pull/11157)) +- Ensure `repeating-conic-gradient` is detected as an image ([#11180](https://github.com/tailwindlabs/tailwindcss/pull/11180)) +- Move unknown pseudo-elements outside of `:is` by default ([#11345](https://github.com/tailwindlabs/tailwindcss/pull/11345)) +- Escape animation names when prefixes contain special characters ([#11470](https://github.com/tailwindlabs/tailwindcss/pull/11470)) +- Don't prefix arbitrary classes in `group` and `peer` variants ([#11454](https://github.com/tailwindlabs/tailwindcss/pull/11454)) +- Sort classes using position of first matching rule ([#11504](https://github.com/tailwindlabs/tailwindcss/pull/11504)) +- Allow variant to be an at-rule without a prelude ([#11589](https://github.com/tailwindlabs/tailwindcss/pull/11589)) +- Make PostCSS plugin async to improve performance ([#11548](https://github.com/tailwindlabs/tailwindcss/pull/11548)) +- Don’t error when a config file is missing ([f97759f](https://github.com/tailwindlabs/tailwindcss/commit/f97759f808d15ace66647b1405744fcf95a392e5)) + +### Added + +- Add `aria-busy` utility ([#10966](https://github.com/tailwindlabs/tailwindcss/pull/10966)) + +### Changed + +- Reset padding for `` elements in preflight ([#11069](https://github.com/tailwindlabs/tailwindcss/pull/11069)) + ## [3.3.2] - 2023-04-25 ### Fixed @@ -2236,7 +2260,8 @@ No release notes - Everything! -[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.2...HEAD +[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.3...HEAD +[3.3.3]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.2...v3.3.3 [3.3.2]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.1...v3.3.2 [3.3.1]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.0...v3.3.1 [3.3.0]: https://github.com/tailwindlabs/tailwindcss/compare/v3.2.7...v3.3.0 diff --git a/package-lock.json b/package-lock.json index ecdc2141e39c..b7c2921c521e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "tailwindcss", - "version": "3.3.2", + "version": "3.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tailwindcss", - "version": "3.3.2", + "version": "3.3.3", "license": "MIT", "workspaces": [ "integrations/*", @@ -36,7 +36,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, @@ -27115,7 +27114,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "prettier": "^2.8.8", "resolve": "^1.22.2", "rimraf": "^5.0.0", diff --git a/package-lock.stable.json b/package-lock.stable.json index a80b9c55ca2c..0ef9743a2e4d 100644 --- a/package-lock.stable.json +++ b/package-lock.stable.json @@ -1,12 +1,12 @@ { "name": "tailwindcss", - "version": "3.3.2", + "version": "3.3.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tailwindcss", - "version": "3.3.2", + "version": "3.3.3", "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -29,7 +29,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, @@ -777,14 +776,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz", "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "dev": true, "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": ">=12" } @@ -793,14 +788,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz", "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": ">=12" } @@ -809,14 +800,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz", "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "android" - ], + "os": ["android"], "engines": { "node": ">=12" } @@ -825,14 +812,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz", "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">=12" } @@ -841,14 +824,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz", "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">=12" } @@ -857,14 +836,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz", "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "freebsd" - ], + "os": ["freebsd"], "engines": { "node": ">=12" } @@ -873,14 +848,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz", "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "freebsd" - ], + "os": ["freebsd"], "engines": { "node": ">=12" } @@ -889,14 +860,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz", "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -905,14 +872,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz", "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -921,14 +884,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz", "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==", - "cpu": [ - "ia32" - ], + "cpu": ["ia32"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -937,14 +896,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz", "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==", - "cpu": [ - "loong64" - ], + "cpu": ["loong64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -953,14 +908,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz", "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==", - "cpu": [ - "mips64el" - ], + "cpu": ["mips64el"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -969,14 +920,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz", "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==", - "cpu": [ - "ppc64" - ], + "cpu": ["ppc64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -985,14 +932,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz", "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==", - "cpu": [ - "riscv64" - ], + "cpu": ["riscv64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -1001,14 +944,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz", "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==", - "cpu": [ - "s390x" - ], + "cpu": ["s390x"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -1017,14 +956,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz", "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=12" } @@ -1033,14 +968,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz", "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "netbsd" - ], + "os": ["netbsd"], "engines": { "node": ">=12" } @@ -1049,14 +980,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz", "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "openbsd" - ], + "os": ["openbsd"], "engines": { "node": ">=12" } @@ -1065,14 +992,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz", "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "sunos" - ], + "os": ["sunos"], "engines": { "node": ">=12" } @@ -1081,14 +1004,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz", "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=12" } @@ -1097,14 +1016,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz", "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==", - "cpu": [ - "ia32" - ], + "cpu": ["ia32"], "dev": true, "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=12" } @@ -1113,14 +1028,10 @@ "version": "0.17.18", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz", "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=12" } @@ -1981,14 +1892,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.55.tgz", "integrity": "sha512-UnHC8aPg/JvHhgXxTU6EhTtfnYNS7nhq8EKB8laNPxlHbwEyMBVQ2QuJHlNCtFtvSfX/uH5l04Ld1iGXnBTfdQ==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">=10" } @@ -1997,14 +1904,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.55.tgz", "integrity": "sha512-VNJkFVARrktIqtaLrD1NFA54gqekH7eAUcUY2U2SdHwO67HYjfMXMxlugLP5PDasSKpTkrVooUdhkffoA5W50g==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">=10" } @@ -2013,14 +1916,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.55.tgz", "integrity": "sha512-6OcohhIFKKNW/TpJt26Tpul8zyL7dmp1Lnyj2BX9ycsZZ5UnsNiGqn37mrqJgVTx/ansEmbyOmKu2mzm/Ct6cQ==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=10" } @@ -2029,14 +1928,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.55.tgz", "integrity": "sha512-MfZtXGBv21XWwvrSMP0CMxScDolT/iv5PRl9UBprYUehwWr7BNjA3V9W7QQ+kKoPyORWk7LX7OpJZF3FnO618Q==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=10" } @@ -2045,14 +1940,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.55.tgz", "integrity": "sha512-iZJo+7L5lv10W0f0C6SlyteAyMJt5Tp+aH3+nlAwKdtc+VjyL1sGhR8DJMXp2/buBRZJ9tjEtpXKDaWUdSdF7Q==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=10" } @@ -2061,14 +1952,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.55.tgz", "integrity": "sha512-Rmc8ny/mslzzz0+wNK9/mLdyAWVbMZHRSvljhpzASmq48NBkmZ5vk9/WID6MnUz2e9cQ0JxJQs8t39KlFJtW3g==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=10" } @@ -2077,14 +1964,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.55.tgz", "integrity": "sha512-Ymoc4xxINzS93ZjVd2UZfLZk1jF6wHjdCbC1JF+0zK3IrNrxCIDoWoaAj0+Bbvyo3hD1Xg/cneSTsqX8amnnuQ==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">=10" } @@ -2093,14 +1976,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.55.tgz", "integrity": "sha512-OhnmFstq2qRU2GI5I0G/8L+vc2rx8+w+IOA6EZBrY4FuMCbPIZKKzlnAIxYn2W+yD4gvBzYP3tgEcaDfQk6EkA==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=10" } @@ -2109,14 +1988,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.55.tgz", "integrity": "sha512-3VR5rHZ6uoL/Vo3djV30GgX2oyDwWWsk+Yp+nyvYyBaKYiH2zeHfxdYRLSQV3W7kSlCAH3oDYpSljrWZ0t5XEQ==", - "cpu": [ - "ia32" - ], + "cpu": ["ia32"], "dev": true, "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=10" } @@ -2125,14 +2000,10 @@ "version": "1.3.55", "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.55.tgz", "integrity": "sha512-KBtMFtRwnbxBugYf6i2ePqEGdxsk715KcqGMjGhxNg7BTACnXnhj37irHu2e7A7wZffbkUVUYuj/JEgVkEjSxg==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">=10" } @@ -4194,9 +4065,7 @@ "version": "2.3.2", "license": "MIT", "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -5843,14 +5712,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.18.0.tgz", "integrity": "sha512-OqjydwtiNPgdH1ByIjA1YzqvDG/OMR6L3LPN6wRl1729LB0y4Mik7L06kmZaTb+pvUHr+NmDd2KCwnlrQ4zO3w==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">= 12.0.0" }, @@ -5863,14 +5728,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.18.0.tgz", "integrity": "sha512-mNiuPHj89/JHZmJMp+5H8EZSt6EL5DZRWJ31O6k3DrLLnRIQjXuXdDdN8kP7LoIkeWI5xvyD60CsReJm+YWYAw==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "darwin" - ], + "os": ["darwin"], "engines": { "node": ">= 12.0.0" }, @@ -5883,14 +5744,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.18.0.tgz", "integrity": "sha512-S+25JjI6601HiAVoTDXW6SqH+E94a+FHA7WQqseyNHunOgVWKcAkNEc2LJvVxgwTq6z41sDIb9/M3Z9wa9lk4A==", - "cpu": [ - "arm" - ], + "cpu": ["arm"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -5903,14 +5760,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.18.0.tgz", "integrity": "sha512-JSqh4+21dCgBecIQUet35dtE4PhhSEMyqe3y0ZNQrAJQ5kyUPSQHiw81WXnPJcOSTTpG0TyMLiC8K//+BsFGQA==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -5923,14 +5776,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz", "integrity": "sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -5943,14 +5792,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.18.0.tgz", "integrity": "sha512-plCPGQJtDZHcLVKVRLnQVF2XRsIC32WvuJhQ7fJ7F6BV98b/VZX0OlX05qUaOESD9dCDHjYSfxsgcvOKgCWh7A==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -5963,14 +5808,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.18.0.tgz", "integrity": "sha512-na+BGtVU6fpZvOHKhnlA0XHeibkT3/46nj6vLluG3kzdJYoBKU6dIl7DSOk++8jv4ybZyFJ0aOFMMSc8g2h58A==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "linux" - ], + "os": ["linux"], "engines": { "node": ">= 12.0.0" }, @@ -5983,14 +5824,10 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.18.0.tgz", "integrity": "sha512-5qeAH4RMNy2yMNEl7e5TI6upt/7xD2ZpHWH4RkT8iJ7/6POS5mjHbXWUO9Q1hhDhqkdzGa76uAdMzEouIeCyNw==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "win32" - ], + "os": ["win32"], "engines": { "node": ">= 12.0.0" }, @@ -8053,79 +7890,55 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-1.9.3.tgz", "integrity": "sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "darwin" - ] + "os": ["darwin"] }, "node_modules/turbo-darwin-arm64": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-1.9.3.tgz", "integrity": "sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "darwin" - ] + "os": ["darwin"] }, "node_modules/turbo-linux-64": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-1.9.3.tgz", "integrity": "sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "linux" - ] + "os": ["linux"] }, "node_modules/turbo-linux-arm64": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-1.9.3.tgz", "integrity": "sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "linux" - ] + "os": ["linux"] }, "node_modules/turbo-windows-64": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-1.9.3.tgz", "integrity": "sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==", - "cpu": [ - "x64" - ], + "cpu": ["x64"], "dev": true, "optional": true, - "os": [ - "win32" - ] + "os": ["win32"] }, "node_modules/turbo-windows-arm64": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-1.9.3.tgz", "integrity": "sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==", - "cpu": [ - "arm64" - ], + "cpu": ["arm64"], "dev": true, "optional": true, - "os": [ - "win32" - ] + "os": ["win32"] }, "node_modules/type-detect": { "version": "4.0.8", @@ -8327,9 +8140,7 @@ "version": "0.1.0", "extraneous": true, "license": "MIT", - "workspaces": [ - "node" - ] + "workspaces": ["node"] }, "oxide-node-api-shim": { "name": "@tailwindcss/oxide-shim", diff --git a/package.json b/package.json index e91533f0b0af..80fef174b106 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tailwindcss", - "version": "3.3.2", + "version": "3.3.3", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "main": "lib/index.js", @@ -90,7 +90,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, diff --git a/package.stable.json b/package.stable.json index 308740509d42..5e29bee6db9d 100644 --- a/package.stable.json +++ b/package.stable.json @@ -1,6 +1,6 @@ { "name": "tailwindcss", - "version": "3.3.2", + "version": "3.3.3", "description": "A utility-first CSS framework for rapidly building custom user interfaces.", "license": "MIT", "main": "lib/index.js", @@ -87,30 +87,20 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, - "browserslist": [ - "> 1%", - "not edge <= 18", - "not ie 11", - "not op_mini all" - ], + "browserslist": ["> 1%", "not edge <= 18", "not ie 11", "not op_mini all"], "jest": { "testTimeout": 30000, - "setupFilesAfterEnv": [ - "/jest/customMatchers.js" - ], + "setupFilesAfterEnv": ["/jest/customMatchers.js"], "testPathIgnorePatterns": [ "/node_modules/", "/integrations/", "/standalone-cli/", "\\.test\\.skip\\.js$" ], - "transformIgnorePatterns": [ - "node_modules/(?!lightningcss)" - ], + "transformIgnorePatterns": ["node_modules/(?!lightningcss)"], "transform": { "\\.js$": "@swc/jest", "\\.ts$": "@swc/jest" diff --git a/src/cli/build/plugin.js b/src/cli/build/plugin.js index 9f3f6f52ff27..6af590dc5efe 100644 --- a/src/cli/build/plugin.js +++ b/src/cli/build/plugin.js @@ -278,9 +278,9 @@ export async function createProcessor(args, cliConfigPath) { let tailwindPlugin = () => { return { postcssPlugin: 'tailwindcss', - Once(root, { result }) { + async Once(root, { result }) { env.DEBUG && console.time('Compiling CSS') - tailwind(({ createContext }) => { + await tailwind(({ createContext }) => { console.error() console.error('Rebuilding...') diff --git a/src/corePlugins.js b/src/corePlugins.js index e4d2ff279a30..5db1fdb74e7b 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -22,6 +22,7 @@ import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadow import { removeAlphaVariables } from './util/removeAlphaVariables' import { flagEnabled } from './featureFlags' import { normalize } from './util/dataTypes' +import { INTERNAL_FEATURES } from './lib/setupContextUtils' export let variantPlugins = { pseudoElementVariants: ({ addVariant }) => { @@ -80,7 +81,7 @@ export let variantPlugins = { }) }, - pseudoClassVariants: ({ addVariant, matchVariant, config }) => { + pseudoClassVariants: ({ addVariant, matchVariant, config, prefix }) => { let pseudoVariants = [ // Positional ['first', '&:first-child'], @@ -151,12 +152,12 @@ export let variantPlugins = { let variants = { group: (_, { modifier }) => modifier - ? [`:merge(.group\\/${escapeClassName(modifier)})`, ' &'] - : [`:merge(.group)`, ' &'], + ? [`:merge(${prefix('.group')}\\/${escapeClassName(modifier)})`, ' &'] + : [`:merge(${prefix('.group')})`, ' &'], peer: (_, { modifier }) => modifier - ? [`:merge(.peer\\/${escapeClassName(modifier)})`, ' ~ &'] - : [`:merge(.peer)`, ' ~ &'], + ? [`:merge(${prefix('.peer')}\\/${escapeClassName(modifier)})`, ' ~ &'] + : [`:merge(${prefix('.peer')})`, ' ~ &'], } for (let [name, fn] of Object.entries(variants)) { @@ -192,7 +193,12 @@ export let variantPlugins = { return result.slice(0, start) + a + result.slice(start + 1, end) + b + result.slice(end) }, - { values: Object.fromEntries(pseudoVariants) } + { + values: Object.fromEntries(pseudoVariants), + [INTERNAL_FEATURES]: { + respectPrefix: false, + }, + } ) } }, @@ -913,7 +919,7 @@ export let corePlugins = { }, animation: ({ matchUtilities, theme, config }) => { - let prefixName = (name) => `${config('prefix')}${escapeClassName(name)}` + let prefixName = (name) => escapeClassName(config('prefix') + name) let keyframes = Object.fromEntries( Object.entries(theme('keyframes') ?? {}).map(([key, value]) => { return [key, { [`@keyframes ${prefixName(key)}`]: value }] diff --git a/src/css/preflight.css b/src/css/preflight.css index fab875d95904..e5e52cd8f323 100644 --- a/src/css/preflight.css +++ b/src/css/preflight.css @@ -163,6 +163,8 @@ optgroup, select, textarea { font-family: inherit; /* 1 */ + font-feature-settings: inherit; /* 1 */ + font-variation-settings: inherit; /* 1 */ font-size: 100%; /* 1 */ font-weight: inherit; /* 1 */ line-height: inherit; /* 1 */ @@ -300,6 +302,13 @@ menu { padding: 0; } +/* +Reset default styling for dialogs. +*/ +dialog { + padding: 0; +} + /* Prevent resizing textareas horizontally by default. */ diff --git a/src/lib/evaluateTailwindFunctions.js b/src/lib/evaluateTailwindFunctions.js index 7e04ef428174..ff73f466c979 100644 --- a/src/lib/evaluateTailwindFunctions.js +++ b/src/lib/evaluateTailwindFunctions.js @@ -1,7 +1,7 @@ import dlv from 'dlv' import didYouMean from 'didyoumean' import transformThemeValue from '../util/transformThemeValue' -import parseValue from 'postcss-value-parser' +import parseValue from '../value-parser/index' import { normalizeScreens } from '../util/normalizeScreens' import buildMediaQuery from '../util/buildMediaQuery' import { toPath } from '../util/toPath' @@ -146,6 +146,9 @@ function resolveVNode(node, vNode, functions) { } function resolveFunctions(node, input, functions) { + let hasAnyFn = Object.keys(functions).some((fn) => input.includes(`${fn}(`)) + if (!hasAnyFn) return input + return parseValue(input) .walk((vNode) => { resolveVNode(node, vNode, functions) diff --git a/src/lib/expandTailwindAtRules.js b/src/lib/expandTailwindAtRules.js index bf7d8f17f8ed..835c1622f1ad 100644 --- a/src/lib/expandTailwindAtRules.js +++ b/src/lib/expandTailwindAtRules.js @@ -98,7 +98,7 @@ function buildStylesheet(rules, context) { } export default function expandTailwindAtRules(context) { - return (root) => { + return async (root) => { let layerNodes = { base: null, components: null, @@ -145,12 +145,14 @@ export default function expandTailwindAtRules(context) { // getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen) // } } else { - for (let { file, content, extension } of context.changedContent) { - let transformer = getTransformer(context.tailwindConfig, extension) - let extractor = getExtractor(context, extension) - content = file ? fs.readFileSync(file, 'utf8') : content - getClassCandidates(transformer(content), extractor, candidates, seen) - } + await Promise.all( + context.changedContent.map(async ({ file, content, extension }) => { + let transformer = getTransformer(context.tailwindConfig, extension) + let extractor = getExtractor(context, extension) + content = file ? await fs.promises.readFile(file, 'utf8') : content + getClassCandidates(transformer(content), extractor, candidates, seen) + }) + ) } env.DEBUG && console.timeEnd('Reading changed files') diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index 60da2323fc94..6ced76937f1a 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -13,7 +13,7 @@ import { } from '../util/formatVariantSelector' import { asClass } from '../util/nameClass' import { normalize } from '../util/dataTypes' -import { isValidVariantFormatString, parseVariant } from './setupContextUtils' +import { isValidVariantFormatString, parseVariant, INTERNAL_FEATURES } from './setupContextUtils' import isValidArbitraryValue from '../util/isSyntacticallyValidPropertyValue' import { splitAtTopLevelOnly } from '../util/splitAtTopLevelOnly.js' import { flagEnabled } from '../featureFlags' @@ -193,13 +193,13 @@ function applyVariant(variant, matches, context) { // group[:hover] (`-` is missing) let match = /(.)(-?)\[(.*)\]/g.exec(variant) if (match) { - let [, char, seperator, value] = match + let [, char, separator, value] = match // @-[200px] case - if (char === '@' && seperator === '-') return [] + if (char === '@' && separator === '-') return [] // group[:hover] case - if (char !== '@' && seperator === '') return [] + if (char !== '@' && separator === '') return [] - variant = variant.replace(`${seperator}[${value}]`, '') + variant = variant.replace(`${separator}[${value}]`, '') args.value = value } } @@ -230,9 +230,16 @@ function applyVariant(variant, matches, context) { if (context.variantMap.has(variant)) { let isArbitraryVariant = isArbitraryValue(variant) + let internalFeatures = context.variantOptions.get(variant)?.[INTERNAL_FEATURES] ?? {} let variantFunctionTuples = context.variantMap.get(variant).slice() let result = [] + let respectPrefix = (() => { + if (isArbitraryVariant) return false + if (internalFeatures.respectPrefix === false) return false + return true + })() + for (let [meta, rule] of matches) { // Don't generate variants for user css if (meta.layer === 'user') { @@ -293,7 +300,7 @@ function applyVariant(variant, matches, context) { format(selectorFormat) { collectedFormats.push({ format: selectorFormat, - isArbitraryVariant, + respectPrefix, }) }, args, @@ -322,7 +329,7 @@ function applyVariant(variant, matches, context) { if (typeof ruleWithVariant === 'string') { collectedFormats.push({ format: ruleWithVariant, - isArbitraryVariant, + respectPrefix, }) } @@ -366,7 +373,7 @@ function applyVariant(variant, matches, context) { // format: .foo & collectedFormats.push({ format: modified.replace(rebuiltBase, '&'), - isArbitraryVariant, + respectPrefix, }) rule.selector = before }) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 0749da68dbdc..4d85e9d8ce67 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -24,6 +24,8 @@ import { Offsets } from './offsets.js' import { flagEnabled } from '../featureFlags.js' import { finalizeSelector, formatVariantSelector } from '../util/formatVariantSelector' +export const INTERNAL_FEATURES = Symbol() + const VARIANT_TYPES = { AddVariant: Symbol.for('ADD_VARIANT'), MatchVariant: Symbol.for('MATCH_VARIANT'), @@ -230,8 +232,8 @@ export function parseVariant(variant) { return ({ format }) => format(str) } - let [, name, params] = /@(.*?)( .+|[({].*)/g.exec(str) - return ({ wrap }) => wrap(postcss.atRule({ name, params: params.trim() })) + let [, name, params] = /@(\S*)( .+|[({].*)?/g.exec(str) + return ({ wrap }) => wrap(postcss.atRule({ name, params: params?.trim() ?? '' })) }) .reverse() @@ -949,7 +951,11 @@ function registerPlugins(plugins, context) { let idx = BigInt(parasiteUtilities.length) for (const [, rule] of rules) { - sortedClassNames.set(rule.raws.tailwind.candidate, idx++) + let candidate = rule.raws.tailwind.candidate + + // When multiple rules match a candidate + // always take the position of the first one + sortedClassNames.set(candidate, sortedClassNames.get(candidate) ?? idx++) } return classes.map((className) => { @@ -1119,17 +1125,24 @@ function registerPlugins(plugins, context) { } let isArbitraryVariant = !(value in (options.values ?? {})) + let internalFeatures = options[INTERNAL_FEATURES] ?? {} + + let respectPrefix = (() => { + if (isArbitraryVariant) return false + if (internalFeatures.respectPrefix === false) return false + return true + })() formatStrings = formatStrings.map((format) => format.map((str) => ({ format: str, - isArbitraryVariant, + respectPrefix, })) ) manualFormatStrings = manualFormatStrings.map((format) => ({ format, - isArbitraryVariant, + respectPrefix, })) let opts = { diff --git a/src/lib/setupTrackingContext.js b/src/lib/setupTrackingContext.js index 2b281377d5f1..70e7cb684139 100644 --- a/src/lib/setupTrackingContext.js +++ b/src/lib/setupTrackingContext.js @@ -63,9 +63,7 @@ function getTailwindConfig(configOrPath) { } // It's a plain object, not a path - let newConfig = resolveConfig( - configOrPath.config === undefined ? configOrPath : configOrPath.config - ) + let newConfig = resolveConfig(configOrPath?.config ?? configOrPath ?? {}) newConfig = validateConfig(newConfig) diff --git a/src/plugin.js b/src/plugin.js index 59f5fab014c6..bbb8cc14dd37 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -13,7 +13,7 @@ module.exports = function tailwindcss(configOrPath) { console.time('JIT TOTAL') return root }, - function (root, result) { + async function (root, result) { // Use the path for the `@config` directive if it exists, otherwise use the // path for the file being processed configOrPath = findAtConfigPath(root, result) ?? configOrPath @@ -25,14 +25,14 @@ module.exports = function tailwindcss(configOrPath) { for (const root of roots) { if (root.type === 'root') { - processTailwindFeatures(context)(root, result) + await processTailwindFeatures(context)(root, result) } } return } - processTailwindFeatures(context)(root, result) + await processTailwindFeatures(context)(root, result) }, __OXIDE__ && function lightningCssPlugin(_root, result) { diff --git a/src/processTailwindFeatures.js b/src/processTailwindFeatures.js index 952e17a13762..fa363b003bf2 100644 --- a/src/processTailwindFeatures.js +++ b/src/processTailwindFeatures.js @@ -12,7 +12,7 @@ import { createContext } from './lib/setupContextUtils' import { issueFlagNotices } from './featureFlags' export default function processTailwindFeatures(setupContext) { - return function (root, result) { + return async function (root, result) { let { tailwindDirectives, applyDirectives } = normalizeTailwindDirectives(root) detectNesting()(root, result) @@ -44,7 +44,8 @@ export default function processTailwindFeatures(setupContext) { issueFlagNotices(context.tailwindConfig) - expandTailwindAtRules(context)(root, result) + await expandTailwindAtRules(context)(root, result) + // Partition apply rules that are generated by // addComponents, addUtilities and so on. partitionApplyAtRules()(root, result) diff --git a/src/util/dataTypes.js b/src/util/dataTypes.js index 55cc005799f6..2f01aa3d20d0 100644 --- a/src/util/dataTypes.js +++ b/src/util/dataTypes.js @@ -49,10 +49,22 @@ export function normalize(value, isRoot = true) { value = value.trim() } - // Add spaces around operators inside math functions like calc() that do not follow an operator - // or '('. - value = value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => { + value = normalizeMathOperatorSpacing(value) + + return value +} + +/** + * Add spaces around operators inside math functions + * like calc() that do not follow an operator or '('. + * + * @param {string} value + * @returns {string} + */ +function normalizeMathOperatorSpacing(value) { + return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => { let vars = [] + return match .replace(/var\((--.+?)[,)]/g, (match, g1) => { vars.push(g1) @@ -61,8 +73,6 @@ export function normalize(value, isRoot = true) { .replace(/(-?\d*\.?\d(?!\b-\d.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, '$1 $2 ') .replace(placeholderRe, () => vars.shift()) }) - - return value } export function url(value) { @@ -178,11 +188,12 @@ export function image(value) { } let gradientTypes = new Set([ + 'conic-gradient', 'linear-gradient', 'radial-gradient', + 'repeating-conic-gradient', 'repeating-linear-gradient', 'repeating-radial-gradient', - 'conic-gradient', ]) export function gradient(value) { value = normalize(value) diff --git a/src/util/formatVariantSelector.js b/src/util/formatVariantSelector.js index e3016e804779..d2594358e494 100644 --- a/src/util/formatVariantSelector.js +++ b/src/util/formatVariantSelector.js @@ -9,7 +9,7 @@ import { movePseudos } from './pseudoElements' /** @typedef {import('postcss-selector-parser').Pseudo} Pseudo */ /** @typedef {import('postcss-selector-parser').Node} Node */ -/** @typedef {{format: string, isArbitraryVariant: boolean}[]} RawFormats */ +/** @typedef {{format: string, respectPrefix: boolean}[]} RawFormats */ /** @typedef {import('postcss-selector-parser').Root} ParsedFormats */ /** @typedef {RawFormats | ParsedFormats} AcceptedFormats */ @@ -29,7 +29,7 @@ export function formatVariantSelector(formats, { context, candidate }) { return { ...format, - ast: format.isArbitraryVariant ? ast : prefixSelector(prefix, ast), + ast: format.respectPrefix ? prefixSelector(prefix, ast) : ast, } }) diff --git a/src/util/prefixSelector.js b/src/util/prefixSelector.js index 0e7bb445bdd9..93cbeb957682 100644 --- a/src/util/prefixSelector.js +++ b/src/util/prefixSelector.js @@ -17,6 +17,7 @@ export default function (prefix, selector, prependNegative = false) { return selector } + /** @type {import('postcss-selector-parser').Root} */ let ast = typeof selector === 'string' ? parser().astSync(selector) : selector ast.walkClasses((classSelector) => { diff --git a/src/util/pseudoElements.js b/src/util/pseudoElements.js index 9f0e54c67948..5795cdd42045 100644 --- a/src/util/pseudoElements.js +++ b/src/util/pseudoElements.js @@ -19,40 +19,37 @@ // **Jumpable** // Any terminal element may "jump" over combinators when moving to the end of the selector // -// This is a backwards-compat quirk of :before and :after variants. +// This is a backwards-compat quirk of pseudo element variants from earlier versions of Tailwind CSS. /** @typedef {'terminal' | 'actionable' | 'jumpable'} PseudoProperty */ /** @type {Record} */ let elementProperties = { + // Pseudo elements from the spec '::after': ['terminal', 'jumpable'], - '::backdrop': ['terminal'], + '::backdrop': ['terminal', 'jumpable'], '::before': ['terminal', 'jumpable'], '::cue': ['terminal'], '::cue-region': ['terminal'], '::first-letter': ['terminal', 'jumpable'], '::first-line': ['terminal', 'jumpable'], '::grammar-error': ['terminal'], - '::marker': ['terminal'], + '::marker': ['terminal', 'jumpable'], '::part': ['terminal', 'actionable'], - '::placeholder': ['terminal'], - '::selection': ['terminal'], + '::placeholder': ['terminal', 'jumpable'], + '::selection': ['terminal', 'jumpable'], '::slotted': ['terminal'], '::spelling-error': ['terminal'], '::target-text': ['terminal'], - // other + // Pseudo elements from the spec with special rules '::file-selector-button': ['terminal', 'actionable'], - '::-webkit-progress-bar': ['terminal', 'actionable'], - // Webkit scroll bar pseudo elements can be combined with user-action pseudo classes - '::-webkit-scrollbar': ['terminal', 'actionable'], - '::-webkit-scrollbar-button': ['terminal', 'actionable'], - '::-webkit-scrollbar-thumb': ['terminal', 'actionable'], - '::-webkit-scrollbar-track': ['terminal', 'actionable'], - '::-webkit-scrollbar-track-piece': ['terminal', 'actionable'], - '::-webkit-scrollbar-corner': ['terminal', 'actionable'], - '::-webkit-resizer': ['terminal', 'actionable'], + // Library-specific pseudo elements used by component libraries + // These are Shadow DOM-like + '::deep': ['actionable'], + '::v-deep': ['actionable'], + '::ng-deep': ['actionable'], // Note: As a rule, double colons (::) should be used instead of a single colon // (:). This distinguishes pseudo-classes from pseudo-elements. However, since @@ -65,8 +62,8 @@ let elementProperties = { // The default value is used when the pseudo-element is not recognized // Because it's not recognized, we don't know if it's terminal or not - // So we assume it can't be moved AND can have user-action pseudo classes attached to it - __default__: ['actionable'], + // So we assume it can be moved AND can have user-action pseudo classes attached to it + __default__: ['terminal', 'actionable'], } /** diff --git a/src/value-parser/LICENSE b/src/value-parser/LICENSE new file mode 100644 index 000000000000..6dcaefcbed35 --- /dev/null +++ b/src/value-parser/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) Bogdan Chadkin + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/value-parser/README.md b/src/value-parser/README.md new file mode 100644 index 000000000000..ea9e20291d2e --- /dev/null +++ b/src/value-parser/README.md @@ -0,0 +1,3 @@ +# postcss-value-parser (forked + inlined) + +This is a customized version of of [PostCSS Value Parser](https://github.com/TrySound/postcss-value-parser) to fix some bugs around parsing CSS functions. diff --git a/src/value-parser/index.d.ts b/src/value-parser/index.d.ts new file mode 100644 index 000000000000..0c0c4b991123 --- /dev/null +++ b/src/value-parser/index.d.ts @@ -0,0 +1,177 @@ +declare namespace postcssValueParser { + interface BaseNode { + /** + * The offset, inclusive, inside the CSS value at which the node starts. + */ + sourceIndex: number + + /** + * The offset, exclusive, inside the CSS value at which the node ends. + */ + sourceEndIndex: number + + /** + * The node's characteristic value + */ + value: string + } + + interface ClosableNode { + /** + * Whether the parsed CSS value ended before the node was properly closed + */ + unclosed?: true + } + + interface AdjacentAwareNode { + /** + * The token at the start of the node + */ + before: string + + /** + * The token at the end of the node + */ + after: string + } + + interface CommentNode extends BaseNode, ClosableNode { + type: 'comment' + } + + interface DivNode extends BaseNode, AdjacentAwareNode { + type: 'div' + } + + interface FunctionNode extends BaseNode, ClosableNode, AdjacentAwareNode { + type: 'function' + + /** + * Nodes inside the function + */ + nodes: Node[] + } + + interface SpaceNode extends BaseNode { + type: 'space' + } + + interface StringNode extends BaseNode, ClosableNode { + type: 'string' + + /** + * The quote type delimiting the string + */ + quote: '"' | "'" + } + + interface UnicodeRangeNode extends BaseNode { + type: 'unicode-range' + } + + interface WordNode extends BaseNode { + type: 'word' + } + + /** + * Any node parsed from a CSS value + */ + type Node = + | CommentNode + | DivNode + | FunctionNode + | SpaceNode + | StringNode + | UnicodeRangeNode + | WordNode + + interface CustomStringifierCallback { + /** + * @param node The node to stringify + * @returns The serialized CSS representation of the node + */ + (nodes: Node): string | undefined + } + + interface WalkCallback { + /** + * @param node The currently visited node + * @param index The index of the node in the series of parsed nodes + * @param nodes The series of parsed nodes + * @returns Returning `false` will prevent traversal of descendant nodes (only applies if `bubble` was set to `true` in the `walk()` call) + */ + (node: Node, index: number, nodes: Node[]): void | boolean + } + + /** + * A CSS dimension, decomposed into its numeric and unit parts + */ + interface Dimension { + number: string + unit: string + } + + /** + * A wrapper around a parsed CSS value that allows for inspecting and walking nodes + */ + interface ParsedValue { + /** + * The series of parsed nodes + */ + nodes: Node[] + + /** + * Walk all parsed nodes, applying a callback + * + * @param callback A visitor callback that will be executed for each node + * @param bubble When set to `true`, walking will be done inside-out instead of outside-in + */ + walk(callback: WalkCallback, bubble?: boolean): this + } + + interface ValueParser { + /** + * Decompose a CSS dimension into its numeric and unit part + * + * @param value The dimension to decompose + * @returns An object representing `number` and `unit` part of the dimension or `false` if the decomposing fails + */ + unit(value: string): Dimension | false + + /** + * Serialize a series of nodes into a CSS value + * + * @param nodes The nodes to stringify + * @param custom A custom stringifier callback + * @returns The generated CSS value + */ + stringify(nodes: Node | Node[], custom?: CustomStringifierCallback): string + + /** + * Walk a series of nodes, applying a callback + * + * @param nodes The nodes to walk + * @param callback A visitor callback that will be executed for each node + * @param bubble When set to `true`, walking will be done inside-out instead of outside-in + */ + walk(nodes: Node[], callback: WalkCallback, bubble?: boolean): void + + /** + * Parse a CSS value into a series of nodes to operate on + * + * @param value The value to parse + */ + new (value: string): ParsedValue + + /** + * Parse a CSS value into a series of nodes to operate on + * + * @param value The value to parse + */ + (value: string): ParsedValue + } +} + +declare const postcssValueParser: postcssValueParser.ValueParser + +export = postcssValueParser diff --git a/src/value-parser/index.js b/src/value-parser/index.js new file mode 100644 index 000000000000..5587ccfa3822 --- /dev/null +++ b/src/value-parser/index.js @@ -0,0 +1,28 @@ +var parse = require('./parse') +var walk = require('./walk') +var stringify = require('./stringify') + +function ValueParser(value) { + if (this instanceof ValueParser) { + this.nodes = parse(value) + return this + } + return new ValueParser(value) +} + +ValueParser.prototype.toString = function () { + return Array.isArray(this.nodes) ? stringify(this.nodes) : '' +} + +ValueParser.prototype.walk = function (cb, bubble) { + walk(this.nodes, cb, bubble) + return this +} + +ValueParser.unit = require('./unit') + +ValueParser.walk = walk + +ValueParser.stringify = stringify + +module.exports = ValueParser diff --git a/src/value-parser/parse.js b/src/value-parser/parse.js new file mode 100644 index 000000000000..445599664225 --- /dev/null +++ b/src/value-parser/parse.js @@ -0,0 +1,303 @@ +var openParentheses = '('.charCodeAt(0) +var closeParentheses = ')'.charCodeAt(0) +var singleQuote = "'".charCodeAt(0) +var doubleQuote = '"'.charCodeAt(0) +var backslash = '\\'.charCodeAt(0) +var slash = '/'.charCodeAt(0) +var comma = ','.charCodeAt(0) +var colon = ':'.charCodeAt(0) +var star = '*'.charCodeAt(0) +var uLower = 'u'.charCodeAt(0) +var uUpper = 'U'.charCodeAt(0) +var plus = '+'.charCodeAt(0) +var isUnicodeRange = /^[a-f0-9?-]+$/i + +module.exports = function (input) { + var tokens = [] + var value = input + + var next, quote, prev, token, escape, escapePos, whitespacePos, parenthesesOpenPos + var pos = 0 + var code = value.charCodeAt(pos) + var max = value.length + var stack = [{ nodes: tokens }] + var balanced = 0 + var parent + + var name = '' + var before = '' + var after = '' + + while (pos < max) { + // Whitespaces + if (code <= 32) { + next = pos + do { + next += 1 + code = value.charCodeAt(next) + } while (code <= 32) + token = value.slice(pos, next) + + prev = tokens[tokens.length - 1] + if (code === closeParentheses && balanced) { + after = token + } else if (prev && prev.type === 'div') { + prev.after = token + prev.sourceEndIndex += token.length + } else if ( + code === comma || + code === colon || + (code === slash && + value.charCodeAt(next + 1) !== star && + (!parent || (parent && parent.type === 'function' && false))) + ) { + before = token + } else { + tokens.push({ + type: 'space', + sourceIndex: pos, + sourceEndIndex: next, + value: token, + }) + } + + pos = next + + // Quotes + } else if (code === singleQuote || code === doubleQuote) { + next = pos + quote = code === singleQuote ? "'" : '"' + token = { + type: 'string', + sourceIndex: pos, + quote: quote, + } + do { + escape = false + next = value.indexOf(quote, next + 1) + if (~next) { + escapePos = next + while (value.charCodeAt(escapePos - 1) === backslash) { + escapePos -= 1 + escape = !escape + } + } else { + value += quote + next = value.length - 1 + token.unclosed = true + } + } while (escape) + token.value = value.slice(pos + 1, next) + token.sourceEndIndex = token.unclosed ? next : next + 1 + tokens.push(token) + pos = next + 1 + code = value.charCodeAt(pos) + + // Comments + } else if (code === slash && value.charCodeAt(pos + 1) === star) { + next = value.indexOf('*/', pos) + + token = { + type: 'comment', + sourceIndex: pos, + sourceEndIndex: next + 2, + } + + if (next === -1) { + token.unclosed = true + next = value.length + token.sourceEndIndex = next + } + + token.value = value.slice(pos + 2, next) + tokens.push(token) + + pos = next + 2 + code = value.charCodeAt(pos) + + // Operation within calc + } else if ((code === slash || code === star) && parent && parent.type === 'function' && true) { + token = value[pos] + tokens.push({ + type: 'word', + sourceIndex: pos - before.length, + sourceEndIndex: pos + token.length, + value: token, + }) + pos += 1 + code = value.charCodeAt(pos) + + // Dividers + } else if (code === slash || code === comma || code === colon) { + token = value[pos] + + tokens.push({ + type: 'div', + sourceIndex: pos - before.length, + sourceEndIndex: pos + token.length, + value: token, + before: before, + after: '', + }) + before = '' + + pos += 1 + code = value.charCodeAt(pos) + + // Open parentheses + } else if (openParentheses === code) { + // Whitespaces after open parentheses + next = pos + do { + next += 1 + code = value.charCodeAt(next) + } while (code <= 32) + parenthesesOpenPos = pos + token = { + type: 'function', + sourceIndex: pos - name.length, + value: name, + before: value.slice(parenthesesOpenPos + 1, next), + } + pos = next + + if (name === 'url' && code !== singleQuote && code !== doubleQuote) { + next -= 1 + do { + escape = false + next = value.indexOf(')', next + 1) + if (~next) { + escapePos = next + while (value.charCodeAt(escapePos - 1) === backslash) { + escapePos -= 1 + escape = !escape + } + } else { + value += ')' + next = value.length - 1 + token.unclosed = true + } + } while (escape) + // Whitespaces before closed + whitespacePos = next + do { + whitespacePos -= 1 + code = value.charCodeAt(whitespacePos) + } while (code <= 32) + if (parenthesesOpenPos < whitespacePos) { + if (pos !== whitespacePos + 1) { + token.nodes = [ + { + type: 'word', + sourceIndex: pos, + sourceEndIndex: whitespacePos + 1, + value: value.slice(pos, whitespacePos + 1), + }, + ] + } else { + token.nodes = [] + } + if (token.unclosed && whitespacePos + 1 !== next) { + token.after = '' + token.nodes.push({ + type: 'space', + sourceIndex: whitespacePos + 1, + sourceEndIndex: next, + value: value.slice(whitespacePos + 1, next), + }) + } else { + token.after = value.slice(whitespacePos + 1, next) + token.sourceEndIndex = next + } + } else { + token.after = '' + token.nodes = [] + } + pos = next + 1 + token.sourceEndIndex = token.unclosed ? next : pos + code = value.charCodeAt(pos) + tokens.push(token) + } else { + balanced += 1 + token.after = '' + token.sourceEndIndex = pos + 1 + tokens.push(token) + stack.push(token) + tokens = token.nodes = [] + parent = token + } + name = '' + + // Close parentheses + } else if (closeParentheses === code && balanced) { + pos += 1 + code = value.charCodeAt(pos) + + parent.after = after + parent.sourceEndIndex += after.length + after = '' + balanced -= 1 + stack[stack.length - 1].sourceEndIndex = pos + stack.pop() + parent = stack[balanced] + tokens = parent.nodes + + // Words + } else { + next = pos + do { + if (code === backslash) { + next += 1 + } + next += 1 + code = value.charCodeAt(next) + } while ( + next < max && + !( + code <= 32 || + code === singleQuote || + code === doubleQuote || + code === comma || + code === colon || + code === slash || + code === openParentheses || + (code === star && parent && parent.type === 'function' && true) || + (code === slash && parent.type === 'function' && true) || + (code === closeParentheses && balanced) + ) + ) + token = value.slice(pos, next) + + if (openParentheses === code) { + name = token + } else if ( + (uLower === token.charCodeAt(0) || uUpper === token.charCodeAt(0)) && + plus === token.charCodeAt(1) && + isUnicodeRange.test(token.slice(2)) + ) { + tokens.push({ + type: 'unicode-range', + sourceIndex: pos, + sourceEndIndex: next, + value: token, + }) + } else { + tokens.push({ + type: 'word', + sourceIndex: pos, + sourceEndIndex: next, + value: token, + }) + } + + pos = next + } + } + + for (pos = stack.length - 1; pos; pos -= 1) { + stack[pos].unclosed = true + stack[pos].sourceEndIndex = value.length + } + + return stack[0].nodes +} diff --git a/src/value-parser/stringify.js b/src/value-parser/stringify.js new file mode 100644 index 000000000000..c95890610a67 --- /dev/null +++ b/src/value-parser/stringify.js @@ -0,0 +1,41 @@ +function stringifyNode(node, custom) { + var type = node.type + var value = node.value + var buf + var customResult + + if (custom && (customResult = custom(node)) !== undefined) { + return customResult + } else if (type === 'word' || type === 'space') { + return value + } else if (type === 'string') { + buf = node.quote || '' + return buf + value + (node.unclosed ? '' : buf) + } else if (type === 'comment') { + return '/*' + value + (node.unclosed ? '' : '*/') + } else if (type === 'div') { + return (node.before || '') + value + (node.after || '') + } else if (Array.isArray(node.nodes)) { + buf = stringify(node.nodes, custom) + if (type !== 'function') { + return buf + } + return value + '(' + (node.before || '') + buf + (node.after || '') + (node.unclosed ? '' : ')') + } + return value +} + +function stringify(nodes, custom) { + var result, i + + if (Array.isArray(nodes)) { + result = '' + for (i = nodes.length - 1; ~i; i -= 1) { + result = stringifyNode(nodes[i], custom) + result + } + return result + } + return stringifyNode(nodes, custom) +} + +module.exports = stringify diff --git a/src/value-parser/unit.js b/src/value-parser/unit.js new file mode 100644 index 000000000000..42d6cd3a7265 --- /dev/null +++ b/src/value-parser/unit.js @@ -0,0 +1,118 @@ +var minus = '-'.charCodeAt(0) +var plus = '+'.charCodeAt(0) +var dot = '.'.charCodeAt(0) +var exp = 'e'.charCodeAt(0) +var EXP = 'E'.charCodeAt(0) + +// Check if three code points would start a number +// https://www.w3.org/TR/css-syntax-3/#starts-with-a-number +function likeNumber(value) { + var code = value.charCodeAt(0) + var nextCode + + if (code === plus || code === minus) { + nextCode = value.charCodeAt(1) + + if (nextCode >= 48 && nextCode <= 57) { + return true + } + + var nextNextCode = value.charCodeAt(2) + + if (nextCode === dot && nextNextCode >= 48 && nextNextCode <= 57) { + return true + } + + return false + } + + if (code === dot) { + nextCode = value.charCodeAt(1) + + if (nextCode >= 48 && nextCode <= 57) { + return true + } + + return false + } + + if (code >= 48 && code <= 57) { + return true + } + + return false +} + +// Consume a number +// https://www.w3.org/TR/css-syntax-3/#consume-number +module.exports = function (value) { + var pos = 0 + var length = value.length + var code + var nextCode + var nextNextCode + + if (length === 0 || !likeNumber(value)) { + return false + } + + code = value.charCodeAt(pos) + + if (code === plus || code === minus) { + pos++ + } + + while (pos < length) { + code = value.charCodeAt(pos) + + if (code < 48 || code > 57) { + break + } + + pos += 1 + } + + code = value.charCodeAt(pos) + nextCode = value.charCodeAt(pos + 1) + + if (code === dot && nextCode >= 48 && nextCode <= 57) { + pos += 2 + + while (pos < length) { + code = value.charCodeAt(pos) + + if (code < 48 || code > 57) { + break + } + + pos += 1 + } + } + + code = value.charCodeAt(pos) + nextCode = value.charCodeAt(pos + 1) + nextNextCode = value.charCodeAt(pos + 2) + + if ( + (code === exp || code === EXP) && + ((nextCode >= 48 && nextCode <= 57) || + ((nextCode === plus || nextCode === minus) && nextNextCode >= 48 && nextNextCode <= 57)) + ) { + pos += nextCode === plus || nextCode === minus ? 3 : 2 + + while (pos < length) { + code = value.charCodeAt(pos) + + if (code < 48 || code > 57) { + break + } + + pos += 1 + } + } + + return { + number: value.slice(0, pos), + unit: value.slice(pos), + } +} diff --git a/src/value-parser/walk.js b/src/value-parser/walk.js new file mode 100644 index 000000000000..dd20a439259a --- /dev/null +++ b/src/value-parser/walk.js @@ -0,0 +1,18 @@ +module.exports = function walk(nodes, cb, bubble) { + var i, max, node, result + + for (i = 0, max = nodes.length; i < max; i += 1) { + node = nodes[i] + if (!bubble) { + result = cb(node, i, nodes) + } + + if (result !== false && node.type === 'function' && Array.isArray(node.nodes)) { + walk(node.nodes, cb, bubble) + } + + if (bubble) { + cb(node, i, nodes) + } + } +} diff --git a/stubs/config.full.js b/stubs/config.full.js index c32ccd3a0ea2..2dd01c6fd467 100644 --- a/stubs/config.full.js +++ b/stubs/config.full.js @@ -15,6 +15,7 @@ module.exports = { bounce: 'bounce 1s infinite', }, aria: { + busy: 'busy="true"', checked: 'checked="true"', disabled: 'disabled="true"', expanded: 'expanded="true"', diff --git a/tests/animations.test.js b/tests/animations.test.js index 958f24252fdc..b8d0d2406306 100644 --- a/tests/animations.test.js +++ b/tests/animations.test.js @@ -276,3 +276,33 @@ crosscheck(() => { }) }) }) + +test('special character prefixes are escaped in animation names', () => { + let config = { + prefix: '@', + content: [{ raw: `
` }], + theme: { + extend: { + keyframes: { + one: { to: { transform: 'rotate(360deg)' } }, + }, + animation: { + one: 'one 2s', + }, + }, + }, + } + + return run('@tailwind utilities', config).then((result) => { + expect(result.css).toMatchFormattedCss(css` + @keyframes \@one { + to { + transform: rotate(360deg); + } + } + .\@animate-one { + animation: 2s \@one; + } + `) + }) +}) diff --git a/tests/arbitrary-values.oxide.test.css b/tests/arbitrary-values.oxide.test.css index 97d838d7d452..214d086017ee 100644 --- a/tests/arbitrary-values.oxide.test.css +++ b/tests/arbitrary-values.oxide.test.css @@ -652,6 +652,9 @@ .bg-\[linear-gradient\(to_left\,rgb\(var\(--green\)\)\,blue\)\] { background-image: linear-gradient(to left, rgb(var(--green)), blue); } +.bg-\[repeating-conic-gradient\(\#F8F9FA_0\%_25\%\,_white_0\%_50\%\)\] { + background-image: repeating-conic-gradient(#f8f9fa 0% 25%, white 0% 50%); +} .bg-\[url\(\'\/path-to-image\.png\'\)\] { background-image: url('/path-to-image.png'); } diff --git a/tests/arbitrary-values.test.css b/tests/arbitrary-values.test.css index dee7669917e4..6f43a5277cc6 100644 --- a/tests/arbitrary-values.test.css +++ b/tests/arbitrary-values.test.css @@ -681,6 +681,9 @@ .bg-\[linear-gradient\(to_left\,rgb\(var\(--green\)\)\,blue\)\] { background-image: linear-gradient(to left, rgb(var(--green)), blue); } +.bg-\[repeating-conic-gradient\(\#F8F9FA_0\%_25\%\,_white_0\%_50\%\)\] { + background-image: repeating-conic-gradient(#f8f9fa 0% 25%, white 0% 50%); +} .bg-\[url\(\'\/path-to-image\.png\'\)\] { background-image: url('/path-to-image.png'); } diff --git a/tests/arbitrary-values.test.html b/tests/arbitrary-values.test.html index 7e65dbf45968..5dd6fab846d0 100644 --- a/tests/arbitrary-values.test.html +++ b/tests/arbitrary-values.test.html @@ -206,6 +206,7 @@
+
diff --git a/tests/evaluateTailwindFunctions.test.js b/tests/evaluateTailwindFunctions.test.js index e4085e03e3b4..34eb5adc0ab2 100644 --- a/tests/evaluateTailwindFunctions.test.js +++ b/tests/evaluateTailwindFunctions.test.js @@ -1383,5 +1383,36 @@ crosscheck(({ stable, oxide }) => { // 4. But we've not received any further logs about it expect().toHaveBeenWarnedWith(['invalid-theme-key-in-class']) }) + + test('it works mayhaps', async () => { + let input = css` + .test { + /* prettier-ignore */ + inset: calc(-1 * (2*theme("spacing.4"))); + /* prettier-ignore */ + padding: calc(-1 * (2* theme("spacing.4"))); + } + ` + + let output = css` + .test { + /* prettier-ignore */ + inset: calc(-1 * (2*1rem)); + /* prettier-ignore */ + padding: calc(-1 * (2* 1rem)); + } + ` + + return run(input, { + theme: { + spacing: { + 4: '1rem', + }, + }, + }).then((result) => { + expect(result.css).toMatchCss(output) + expect(result.warnings().length).toBe(0) + }) + }) }) }) diff --git a/tests/format-variant-selector.test.js b/tests/format-variant-selector.test.js index 0cd0b05ffd76..c94fb95f615c 100644 --- a/tests/format-variant-selector.test.js +++ b/tests/format-variant-selector.test.js @@ -6,7 +6,7 @@ crosscheck(() => { let selector = '.text-center' let candidate = 'hover:text-center' - let formats = [{ format: '&:hover', isArbitraryVariant: false }] + let formats = [{ format: '&:hover', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate })).toEqual('.hover\\:text-center:hover') }) @@ -16,8 +16,8 @@ crosscheck(() => { let candidate = 'focus:hover:text-center' let formats = [ - { format: '&:hover', isArbitraryVariant: false }, - { format: '&:focus', isArbitraryVariant: false }, + { format: '&:hover', respectPrefix: true }, + { format: '&:focus', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -29,7 +29,7 @@ crosscheck(() => { let selector = '.bg-\\[rgba\\(0\\,0\\,0\\)\\]' let candidate = 'hover:bg-[rgba(0,0,0)]' - let formats = [{ format: '&:hover', isArbitraryVariant: false }] + let formats = [{ format: '&:hover', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover' @@ -40,7 +40,7 @@ crosscheck(() => { let selector = '.bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]' let candidate = 'hover:bg-[rgba(0,0,0)]' - let formats = [{ format: '&:hover', isArbitraryVariant: false }] + let formats = [{ format: '&:hover', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( '.hover\\:bg-\\[rgba\\(0\\2c 0\\2c 0\\)\\]:hover' @@ -51,7 +51,7 @@ crosscheck(() => { let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' let candidate = 'hover:space-x-4' - let formats = [{ format: '&:hover', isArbitraryVariant: false }] + let formats = [{ format: '&:hover', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( '.hover\\:space-x-4:hover > :not([hidden]) ~ :not([hidden])' @@ -63,9 +63,9 @@ crosscheck(() => { let candidate = 'disabled:focus:hover:space-x-4' let formats = [ - { format: '&:hover', isArbitraryVariant: false }, - { format: '&:focus', isArbitraryVariant: false }, - { format: '&:disabled', isArbitraryVariant: false }, + { format: '&:hover', respectPrefix: true }, + { format: '&:focus', respectPrefix: true }, + { format: '&:disabled', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -77,7 +77,7 @@ crosscheck(() => { let selector = '.text-center' let candidate = 'group-hover:text-center' - let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }] + let formats = [{ format: ':merge(.group):hover &', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( '.group:hover .group-hover\\:text-center' @@ -89,8 +89,8 @@ crosscheck(() => { let candidate = 'group-focus:group-hover:text-center' let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: ':merge(.group):focus &', isArbitraryVariant: false }, + { format: ':merge(.group):hover &', respectPrefix: true }, + { format: ':merge(.group):focus &', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -102,7 +102,7 @@ crosscheck(() => { let selector = '.space-x-4 ~ :not([hidden]) ~ :not([hidden])' let candidate = 'group-hover:space-x-4' - let formats = [{ format: ':merge(.group):hover &', isArbitraryVariant: false }] + let formats = [{ format: ':merge(.group):hover &', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( '.group:hover .group-hover\\:space-x-4 ~ :not([hidden]) ~ :not([hidden])' @@ -114,8 +114,8 @@ crosscheck(() => { let candidate = 'group-focus:group-hover:space-x-4' let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: ':merge(.group):focus &', isArbitraryVariant: false }, + { format: ':merge(.group):hover &', respectPrefix: true }, + { format: ':merge(.group):focus &', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -128,7 +128,7 @@ crosscheck(() => { let candidate = 'peer-focus:group-hover:text-center' let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: ':merge(.group):hover &', respectPrefix: true }, { format: ':merge(.peer):focus ~ &' }, ] @@ -142,8 +142,8 @@ crosscheck(() => { let candidate = 'group-hover:peer-focus:text-center' let formats = [ - { format: ':merge(.peer):focus ~ &', isArbitraryVariant: false }, - { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: ':merge(.peer):focus ~ &', respectPrefix: true }, + { format: ':merge(.group):hover &', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -156,10 +156,10 @@ crosscheck(() => { let candidate = 'peer-focus:group-focus:peer-hover:group-hover:foo' let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: ':merge(.peer):hover ~ &', isArbitraryVariant: false }, - { format: ':merge(.group):focus &', isArbitraryVariant: false }, - { format: ':merge(.peer):focus ~ &', isArbitraryVariant: false }, + { format: ':merge(.group):hover &', respectPrefix: true }, + { format: ':merge(.peer):hover ~ &', respectPrefix: true }, + { format: ':merge(.group):focus &', respectPrefix: true }, + { format: ':merge(.peer):focus ~ &', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -171,8 +171,8 @@ crosscheck(() => { let selector = '.text-center' let candidate = 'group-hover:prose-headings:text-center' let formats = [ - { format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings - { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover + { format: ':where(&) :is(h1, h2, h3, h4)', respectPrefix: true }, // Prose Headings + { format: ':merge(.group):hover &', respectPrefix: true }, // Group Hover ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -184,8 +184,8 @@ crosscheck(() => { let selector = '.text-center' let candidate = 'prose-headings:group-hover:text-center' let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group Hover - { format: ':where(&) :is(h1, h2, h3, h4)', isArbitraryVariant: false }, // Prose Headings + { format: ':merge(.group):hover &', respectPrefix: true }, // Group Hover + { format: ':where(&) :is(h1, h2, h3, h4)', respectPrefix: true }, // Prose Headings ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -197,12 +197,12 @@ crosscheck(() => { let selector = '.space-x-4 > :not([hidden]) ~ :not([hidden])' let candidate = 'peer-disabled:peer-first-child:group-hover:group-focus:focus:hover:space-x-4' let formats = [ - { format: '&:hover', isArbitraryVariant: false }, // Hover - { format: '&:focus', isArbitraryVariant: false }, // Focus - { format: ':merge(.group):focus &', isArbitraryVariant: false }, // Group focus - { format: ':merge(.group):hover &', isArbitraryVariant: false }, // Group hover - { format: ':merge(.peer):first-child ~ &', isArbitraryVariant: false }, // Peer first-child - { format: ':merge(.peer):disabled ~ &', isArbitraryVariant: false }, // Peer disabled + { format: '&:hover', respectPrefix: true }, // Hover + { format: '&:focus', respectPrefix: true }, // Focus + { format: ':merge(.group):focus &', respectPrefix: true }, // Group focus + { format: ':merge(.group):hover &', respectPrefix: true }, // Group hover + { format: ':merge(.peer):first-child ~ &', respectPrefix: true }, // Peer first-child + { format: ':merge(.peer):disabled ~ &', respectPrefix: true }, // Peer disabled ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -223,7 +223,7 @@ crosscheck(() => { let context = { tailwindConfig: { prefix: 'tw-' } } let selector = '.tw-text-center' let candidate = 'foo:tw-text-center' - let formats = [{ format: '.foo &', isArbitraryVariant: false }] + let formats = [{ format: '.foo &', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate, context })).toEqual( '.tw-foo .foo\\:tw-text-center' @@ -234,7 +234,7 @@ crosscheck(() => { let context = { tailwindConfig: { prefix: 'tw-' } } let selector = '.tw-text-center' let candidate = '[.foo_&]:tw-text-center' - let formats = [{ format: '.foo &', isArbitraryVariant: true }] + let formats = [{ format: '.foo &', respectPrefix: false }] expect(finalizeSelector(selector, formats, { candidate, context })).toEqual( '.foo .\\[\\.foo_\\&\\]\\:tw-text-center' @@ -247,8 +247,8 @@ crosscheck(() => { let selector = '.text-center' let candidate = 'text-center' let formats = [ - { format: ':merge(.group):focus > &', isArbitraryVariant: true }, - { format: ':merge(.group):hover &', isArbitraryVariant: true }, + { format: ':merge(.group):focus > &', respectPrefix: false }, + { format: ':merge(.group):hover &', respectPrefix: false }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -261,7 +261,7 @@ crosscheck(() => { let selector = '.placeholder-red-500::placeholder' let candidate = 'hover:placeholder-red-500' - let formats = [{ format: '&:hover', isArbitraryVariant: false }] + let formats = [{ format: '&:hover', respectPrefix: true }] expect(finalizeSelector(selector, formats, { candidate })).toEqual( '.hover\\:placeholder-red-500:hover::placeholder' @@ -273,8 +273,8 @@ crosscheck(() => { let candidate = 'group-hover:hover:space-x-4' let formats = [ - { format: '&:hover', isArbitraryVariant: false }, - { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: '&:hover', respectPrefix: true }, + { format: ':merge(.group):hover &', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -287,8 +287,8 @@ crosscheck(() => { let candidate = 'dark:group-hover:text-center' let formats = [ - { format: ':merge(.group):hover &', isArbitraryVariant: false }, - { format: '.dark &', isArbitraryVariant: false }, + { format: ':merge(.group):hover &', respectPrefix: true }, + { format: '.dark &', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -302,7 +302,7 @@ crosscheck(() => { let formats = [ { format: '.dark &' }, - { format: ':merge(.group):hover &', isArbitraryVariant: false }, + { format: ':merge(.group):hover &', respectPrefix: true }, ] expect(finalizeSelector(selector, formats, { candidate })).toEqual( @@ -352,8 +352,12 @@ crosscheck(() => { ${'#app :is(:is(.dark &)::before)'} | ${'#app :is(:is(.dark &))::before'} ${'#app :is(.foo::file-selector-button)'} | ${'#app :is(.foo)::file-selector-button'} ${'#app :is(.foo::-webkit-progress-bar)'} | ${'#app :is(.foo)::-webkit-progress-bar'} + ${'.parent::marker li'} | ${'.parent li::marker'} + ${'.parent::selection li'} | ${'.parent li::selection'} + ${'.parent::placeholder input'} | ${'.parent input::placeholder'} + ${'.parent::backdrop dialog'} | ${'.parent dialog::backdrop'} `('should translate "$before" into "$after"', ({ before, after }) => { - let result = finalizeSelector('.a', [{ format: before, isArbitraryVariant: false }], { + let result = finalizeSelector('.a', [{ format: before, respectPrefix: true }], { candidate: 'a', }) diff --git a/tests/getSortOrder.test.js b/tests/getSortOrder.test.js index 4526c4874f69..1916e265f72a 100644 --- a/tests/getSortOrder.test.js +++ b/tests/getSortOrder.test.js @@ -143,3 +143,41 @@ crosscheck(() => { } }) }) + +it('sorts based on first occurence of a candidate / rule', () => { + let classes = [ + ['foo-1 foo', 'foo foo-1'], + ['bar', 'bar'], + ['foo-1 foo', 'foo foo-1'], + ] + + let config = { + theme: {}, + plugins: [ + function ({ addComponents }) { + addComponents({ + '.foo': { display: 'block' }, + '.foo-1': { display: 'block' }, + '.bar': { display: 'block' }, + + // This rule matches both the candidate `foo` and `bar` + // But when sorting `foo` — we've already got a + // position for `foo` so we should use it + '.bar .foo': { display: 'block' }, + }) + }, + ], + } + + // Same context, different class lists + let context = createContext(resolveConfig(config)) + for (const [input, output] of classes) { + expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) + } + + // Different context, different class lists + for (const [input, output] of classes) { + context = createContext(resolveConfig(config)) + expect(defaultSort(context.getClassOrder(input.split(' ')))).toEqual(output) + } +}) diff --git a/tests/getVariants.test.js b/tests/getVariants.test.js index 8d79f82433a8..8c4022186a98 100644 --- a/tests/getVariants.test.js +++ b/tests/getVariants.test.js @@ -67,6 +67,25 @@ crosscheck(() => { expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual(['.foo .group\\/foo &']) }) + it('should provide selectors for complex matchVariant variants like `group` (when using a prefix)', () => { + let config = { prefix: 'tw-' } + let context = createContext(resolveConfig(config)) + + let variants = context.getVariants() + + let variant = variants.find((v) => v.name === 'group') + expect(variant.selectors()).toEqual(['.tw-group &']) + expect(variant.selectors({})).toEqual(['.tw-group &']) + expect(variant.selectors({ value: 'hover' })).toEqual(['.tw-group:hover &']) + expect(variant.selectors({ value: '.foo_&' })).toEqual(['.foo .tw-group &']) + expect(variant.selectors({ modifier: 'foo', value: 'hover' })).toEqual([ + '.tw-group\\/foo:hover &', + ]) + expect(variant.selectors({ modifier: 'foo', value: '.foo_&' })).toEqual([ + '.foo .tw-group\\/foo &', + ]) + }) + it('should provide selectors for variants with atrules', () => { let config = {} let context = createContext(resolveConfig(config)) diff --git a/tests/prefix.test.js b/tests/prefix.test.js index ea3d041ffeac..cf5085509d7a 100644 --- a/tests/prefix.test.js +++ b/tests/prefix.test.js @@ -623,3 +623,33 @@ crosscheck(({ stable, oxide }) => { `) }) }) + +test('does not prefix arbitrary group/peer classes', async () => { + let config = { + prefix: 'tw-', + content: [ + { + raw: html` +
+
+
+
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + ` + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .tw-group.foo .group-\[\&\.foo\]\:tw-flex, + .tw-peer.foo ~ .peer-\[\&\.foo\]\:tw-flex { + display: flex; + } + `) +}) diff --git a/tests/source-maps.test.js b/tests/source-maps.test.js index dc0092dbc443..a19aea687bdd 100644 --- a/tests/source-maps.test.js +++ b/tests/source-maps.test.js @@ -179,219 +179,226 @@ crosscheck(({ stable, oxide }) => { '2:20 -> 160:1', '2:6 -> 162:0', '2:6-20 -> 167:2-30', - '2:6-20 -> 168:2-25', - '2:6-20 -> 169:2-30', - '2:6-20 -> 170:2-30', - '2:6-20 -> 171:2-24', - '2:6-20 -> 172:2-19', - '2:6-20 -> 173:2-20', - '2:20 -> 174:0', - '2:6 -> 176:0', - '2:20 -> 178:1', - '2:6 -> 180:0', - '2:6-20 -> 182:2-22', - '2:20 -> 183:0', - '2:6 -> 185:0', - '2:20 -> 188:1', - '2:6 -> 190:0', - '2:6-20 -> 194:2-36', - '2:6-20 -> 195:2-39', - '2:6-20 -> 196:2-32', - '2:20 -> 197:0', - '2:6 -> 199:0', - '2:20 -> 201:1', - '2:6 -> 203:0', - '2:6-20 -> 204:2-15', - '2:20 -> 205:0', - '2:6 -> 207:0', - '2:20 -> 209:1', - '2:6 -> 211:0', - '2:6-20 -> 212:2-18', - '2:20 -> 213:0', - '2:6 -> 215:0', - '2:20 -> 217:1', - '2:6 -> 219:0', - '2:6-20 -> 220:2-26', - '2:20 -> 221:0', - '2:6 -> 223:0', - '2:20 -> 225:1', - '2:6 -> 227:0', - '2:6-20 -> 229:2-14', - '2:20 -> 230:0', - '2:6 -> 232:0', - '2:20 -> 235:1', - '2:6 -> 237:0', - '2:6-20 -> 238:2-39', - '2:6-20 -> 239:2-30', - '2:20 -> 240:0', - '2:6 -> 242:0', - '2:20 -> 244:1', - '2:6 -> 246:0', - '2:6-20 -> 247:2-26', - '2:20 -> 248:0', - '2:6 -> 250:0', - '2:20 -> 253:1', - '2:6 -> 255:0', - '2:6-20 -> 256:2-36', - '2:6-20 -> 257:2-23', - '2:20 -> 258:0', - '2:6 -> 260:0', - '2:20 -> 262:1', - '2:6 -> 264:0', - '2:6-20 -> 265:2-20', - '2:20 -> 266:0', - '2:6 -> 268:0', - '2:20 -> 270:1', - '2:6 -> 272:0', - '2:6-20 -> 285:2-11', - '2:20 -> 286:0', - '2:6 -> 288:0', - '2:6-20 -> 289:2-11', - '2:6-20 -> 290:2-12', - '2:20 -> 291:0', - '2:6 -> 293:0', - '2:6-20 -> 294:2-12', - '2:20 -> 295:0', - '2:6 -> 297:0', - '2:6-20 -> 300:2-18', - '2:6-20 -> 301:2-11', - '2:6-20 -> 302:2-12', - '2:20 -> 303:0', - '2:6 -> 305:0', - '2:20 -> 307:1', - '2:6 -> 309:0', - '2:6-20 -> 310:2-18', - '2:20 -> 311:0', - '2:6 -> 313:0', + '2:6-20 -> 168:2-40', + '2:6-20 -> 169:2-42', + '2:6-20 -> 170:2-25', + '2:6-20 -> 171:2-30', + '2:6-20 -> 172:2-30', + '2:6-20 -> 173:2-24', + '2:6-20 -> 174:2-19', + '2:6-20 -> 175:2-20', + '2:20 -> 176:0', + '2:6 -> 178:0', + '2:20 -> 180:1', + '2:6 -> 182:0', + '2:6-20 -> 184:2-22', + '2:20 -> 185:0', + '2:6 -> 187:0', + '2:20 -> 190:1', + '2:6 -> 192:0', + '2:6-20 -> 196:2-36', + '2:6-20 -> 197:2-39', + '2:6-20 -> 198:2-32', + '2:20 -> 199:0', + '2:6 -> 201:0', + '2:20 -> 203:1', + '2:6 -> 205:0', + '2:6-20 -> 206:2-15', + '2:20 -> 207:0', + '2:6 -> 209:0', + '2:20 -> 211:1', + '2:6 -> 213:0', + '2:6-20 -> 214:2-18', + '2:20 -> 215:0', + '2:6 -> 217:0', + '2:20 -> 219:1', + '2:6 -> 221:0', + '2:6-20 -> 222:2-26', + '2:20 -> 223:0', + '2:6 -> 225:0', + '2:20 -> 227:1', + '2:6 -> 229:0', + '2:6-20 -> 231:2-14', + '2:20 -> 232:0', + '2:6 -> 234:0', + '2:20 -> 237:1', + '2:6 -> 239:0', + '2:6-20 -> 240:2-39', + '2:6-20 -> 241:2-30', + '2:20 -> 242:0', + '2:6 -> 244:0', + '2:20 -> 246:1', + '2:6 -> 248:0', + '2:6-20 -> 249:2-26', + '2:20 -> 250:0', + '2:6 -> 252:0', + '2:20 -> 255:1', + '2:6 -> 257:0', + '2:6-20 -> 258:2-36', + '2:6-20 -> 259:2-23', + '2:20 -> 260:0', + '2:6 -> 262:0', + '2:20 -> 264:1', + '2:6 -> 266:0', + '2:6-20 -> 267:2-20', + '2:20 -> 268:0', + '2:6 -> 270:0', + '2:20 -> 272:1', + '2:6 -> 274:0', + '2:6-20 -> 287:2-11', + '2:20 -> 288:0', + '2:6 -> 290:0', + '2:6-20 -> 291:2-11', + '2:6-20 -> 292:2-12', + '2:20 -> 293:0', + '2:6 -> 295:0', + '2:6-20 -> 296:2-12', + '2:20 -> 297:0', + '2:6 -> 299:0', + '2:6-20 -> 302:2-18', + '2:6-20 -> 303:2-11', + '2:6-20 -> 304:2-12', + '2:20 -> 305:0', + '2:6 -> 307:0', + '2:20 -> 309:1', + '2:6 -> 310:0', + '2:6-20 -> 311:2-12', + '2:20 -> 312:0', + '2:6 -> 314:0', '2:20 -> 316:1', '2:6 -> 318:0', - '2:6-20 -> 320:2-20', - '2:6-20 -> 321:2-24', - '2:20 -> 322:0', - '2:6 -> 324:0', - '2:20 -> 326:1', - '2:6 -> 328:0', - '2:6-20 -> 330:2-17', + '2:6-20 -> 319:2-18', + '2:20 -> 320:0', + '2:6 -> 322:0', + '2:20 -> 325:1', + '2:6 -> 327:0', + '2:6-20 -> 329:2-20', + '2:6-20 -> 330:2-24', '2:20 -> 331:0', '2:6 -> 333:0', '2:20 -> 335:1', - '2:6 -> 336:0', - '2:6-20 -> 337:2-17', - '2:20 -> 338:0', - '2:6 -> 340:0', + '2:6 -> 337:0', + '2:6-20 -> 339:2-17', + '2:20 -> 340:0', + '2:6 -> 342:0', '2:20 -> 344:1', - '2:6 -> 346:0', - '2:6-20 -> 354:2-24', - '2:6-20 -> 355:2-32', - '2:20 -> 356:0', - '2:6 -> 358:0', - '2:20 -> 360:1', - '2:6 -> 362:0', - '2:6-20 -> 364:2-17', - '2:6-20 -> 365:2-14', - '2:20 -> 366:0', - '2:6-20 -> 368:0-72', - '2:6 -> 369:0', - '2:6-20 -> 370:2-15', - '2:20 -> 371:0', - '2:6 -> 373:0', - '2:6-20 -> 374:2-26', - '2:6-20 -> 375:2-26', - '2:6-20 -> 376:2-21', - '2:6-20 -> 377:2-21', - '2:6-20 -> 378:2-16', - '2:6-20 -> 379:2-16', - '2:6-20 -> 380:2-16', - '2:6-20 -> 381:2-17', - '2:6-20 -> 382:2-17', - '2:6-20 -> 383:2-15', - '2:6-20 -> 384:2-15', - '2:6-20 -> 385:2-20', - '2:6-20 -> 386:2-40', - '2:6-20 -> 387:2-32', - '2:6-20 -> 388:2-31', - '2:6-20 -> 389:2-30', + '2:6 -> 345:0', + '2:6-20 -> 346:2-17', + '2:20 -> 347:0', + '2:6 -> 349:0', + '2:20 -> 353:1', + '2:6 -> 355:0', + '2:6-20 -> 363:2-24', + '2:6-20 -> 364:2-32', + '2:20 -> 365:0', + '2:6 -> 367:0', + '2:20 -> 369:1', + '2:6 -> 371:0', + '2:6-20 -> 373:2-17', + '2:6-20 -> 374:2-14', + '2:20 -> 375:0', + '2:6-20 -> 377:0-72', + '2:6 -> 378:0', + '2:6-20 -> 379:2-15', + '2:20 -> 380:0', + '2:6 -> 382:0', + '2:6-20 -> 383:2-26', + '2:6-20 -> 384:2-26', + '2:6-20 -> 385:2-21', + '2:6-20 -> 386:2-21', + '2:6-20 -> 387:2-16', + '2:6-20 -> 388:2-16', + '2:6-20 -> 389:2-16', '2:6-20 -> 390:2-17', - '2:6-20 -> 391:2-22', - '2:6-20 -> 392:2-24', - '2:6-20 -> 393:2-25', - '2:6-20 -> 394:2-26', - '2:6-20 -> 395:2-20', - '2:6-20 -> 396:2-29', - '2:6-20 -> 397:2-30', - '2:6-20 -> 398:2-40', - '2:6-20 -> 399:2-36', - '2:6-20 -> 400:2-29', + '2:6-20 -> 391:2-17', + '2:6-20 -> 392:2-15', + '2:6-20 -> 393:2-15', + '2:6-20 -> 394:2-20', + '2:6-20 -> 395:2-40', + '2:6-20 -> 396:2-32', + '2:6-20 -> 397:2-31', + '2:6-20 -> 398:2-30', + '2:6-20 -> 399:2-17', + '2:6-20 -> 400:2-22', '2:6-20 -> 401:2-24', - '2:6-20 -> 402:2-32', - '2:6-20 -> 403:2-14', + '2:6-20 -> 402:2-25', + '2:6-20 -> 403:2-26', '2:6-20 -> 404:2-20', - '2:6-20 -> 405:2-18', - '2:6-20 -> 406:2-19', - '2:6-20 -> 407:2-20', - '2:6-20 -> 408:2-16', - '2:6-20 -> 409:2-18', - '2:6-20 -> 410:2-15', - '2:6-20 -> 411:2-21', - '2:6-20 -> 412:2-23', - '2:6-20 -> 413:2-29', - '2:6-20 -> 414:2-27', - '2:6-20 -> 415:2-28', - '2:6-20 -> 416:2-29', - '2:6-20 -> 417:2-25', - '2:6-20 -> 418:2-26', - '2:6-20 -> 419:2-27', - '2:6 -> 420:2', - '2:20 -> 421:0', - '2:6 -> 423:0', - '2:6-20 -> 424:2-26', - '2:6-20 -> 425:2-26', - '2:6-20 -> 426:2-21', - '2:6-20 -> 427:2-21', - '2:6-20 -> 428:2-16', - '2:6-20 -> 429:2-16', - '2:6-20 -> 430:2-16', - '2:6-20 -> 431:2-17', - '2:6-20 -> 432:2-17', - '2:6-20 -> 433:2-15', - '2:6-20 -> 434:2-15', - '2:6-20 -> 435:2-20', - '2:6-20 -> 436:2-40', - '2:6-20 -> 437:2-32', - '2:6-20 -> 438:2-31', - '2:6-20 -> 439:2-30', + '2:6-20 -> 405:2-29', + '2:6-20 -> 406:2-30', + '2:6-20 -> 407:2-40', + '2:6-20 -> 408:2-36', + '2:6-20 -> 409:2-29', + '2:6-20 -> 410:2-24', + '2:6-20 -> 411:2-32', + '2:6-20 -> 412:2-14', + '2:6-20 -> 413:2-20', + '2:6-20 -> 414:2-18', + '2:6-20 -> 415:2-19', + '2:6-20 -> 416:2-20', + '2:6-20 -> 417:2-16', + '2:6-20 -> 418:2-18', + '2:6-20 -> 419:2-15', + '2:6-20 -> 420:2-21', + '2:6-20 -> 421:2-23', + '2:6-20 -> 422:2-29', + '2:6-20 -> 423:2-27', + '2:6-20 -> 424:2-28', + '2:6-20 -> 425:2-29', + '2:6-20 -> 426:2-25', + '2:6-20 -> 427:2-26', + '2:6-20 -> 428:2-27', + '2:6 -> 429:2', + '2:20 -> 430:0', + '2:6 -> 432:0', + '2:6-20 -> 433:2-26', + '2:6-20 -> 434:2-26', + '2:6-20 -> 435:2-21', + '2:6-20 -> 436:2-21', + '2:6-20 -> 437:2-16', + '2:6-20 -> 438:2-16', + '2:6-20 -> 439:2-16', '2:6-20 -> 440:2-17', - '2:6-20 -> 441:2-22', - '2:6-20 -> 442:2-24', - '2:6-20 -> 443:2-25', - '2:6-20 -> 444:2-26', - '2:6-20 -> 445:2-20', - '2:6-20 -> 446:2-29', - '2:6-20 -> 447:2-30', - '2:6-20 -> 448:2-40', - '2:6-20 -> 449:2-36', - '2:6-20 -> 450:2-29', + '2:6-20 -> 441:2-17', + '2:6-20 -> 442:2-15', + '2:6-20 -> 443:2-15', + '2:6-20 -> 444:2-20', + '2:6-20 -> 445:2-40', + '2:6-20 -> 446:2-32', + '2:6-20 -> 447:2-31', + '2:6-20 -> 448:2-30', + '2:6-20 -> 449:2-17', + '2:6-20 -> 450:2-22', '2:6-20 -> 451:2-24', - '2:6-20 -> 452:2-32', - '2:6-20 -> 453:2-14', + '2:6-20 -> 452:2-25', + '2:6-20 -> 453:2-26', '2:6-20 -> 454:2-20', - '2:6-20 -> 455:2-18', - '2:6-20 -> 456:2-19', - '2:6-20 -> 457:2-20', - '2:6-20 -> 458:2-16', - '2:6-20 -> 459:2-18', - '2:6-20 -> 460:2-15', - '2:6-20 -> 461:2-21', - '2:6-20 -> 462:2-23', - '2:6-20 -> 463:2-29', - '2:6-20 -> 464:2-27', - '2:6-20 -> 465:2-28', - '2:6-20 -> 466:2-29', - '2:6-20 -> 467:2-25', - '2:6-20 -> 468:2-26', - '2:6-20 -> 469:2-27', - '2:6 -> 470:2', - '2:20 -> 471:0', + '2:6-20 -> 455:2-29', + '2:6-20 -> 456:2-30', + '2:6-20 -> 457:2-40', + '2:6-20 -> 458:2-36', + '2:6-20 -> 459:2-29', + '2:6-20 -> 460:2-24', + '2:6-20 -> 461:2-32', + '2:6-20 -> 462:2-14', + '2:6-20 -> 463:2-20', + '2:6-20 -> 464:2-18', + '2:6-20 -> 465:2-19', + '2:6-20 -> 466:2-20', + '2:6-20 -> 467:2-16', + '2:6-20 -> 468:2-18', + '2:6-20 -> 469:2-15', + '2:6-20 -> 470:2-21', + '2:6-20 -> 471:2-23', + '2:6-20 -> 472:2-29', + '2:6-20 -> 473:2-27', + '2:6-20 -> 474:2-28', + '2:6-20 -> 475:2-29', + '2:6-20 -> 476:2-25', + '2:6-20 -> 477:2-26', + '2:6-20 -> 478:2-27', + '2:6 -> 479:2', + '2:20 -> 480:0', ]) }) diff --git a/tests/util/apply-important-selector.test.js b/tests/util/apply-important-selector.test.js index 0ec22792fe96..3f4ef294c92f 100644 --- a/tests/util/apply-important-selector.test.js +++ b/tests/util/apply-important-selector.test.js @@ -18,6 +18,10 @@ crosscheck(() => { ${':is(.foo) :is(.bar)'} | ${'#app :is(:is(.foo) :is(.bar))'} ${':is(.foo)::before'} | ${'#app :is(.foo)::before'} ${'.foo:before'} | ${'#app :is(.foo):before'} + ${'.foo::some-uknown-pseudo'} | ${'#app :is(.foo)::some-uknown-pseudo'} + ${'.foo::some-uknown-pseudo:hover'} | ${'#app :is(.foo)::some-uknown-pseudo:hover'} + ${'.foo:focus::some-uknown-pseudo:hover'} | ${'#app :is(.foo:focus)::some-uknown-pseudo:hover'} + ${'.foo:hover::some-uknown-pseudo:focus'} | ${'#app :is(.foo:hover)::some-uknown-pseudo:focus'} `('should generate "$after" from "$before"', ({ before, after }) => { expect(applyImportantSelector(before, '#app')).toEqual(after) }) diff --git a/tests/variants.test.js b/tests/variants.test.js index da62419d4c18..55facc122f41 100644 --- a/tests/variants.test.js +++ b/tests/variants.test.js @@ -66,6 +66,31 @@ crosscheck(({ stable, oxide }) => { }) describe('custom advanced variants', () => { + test('at-rules without params', () => { + let config = { + content: [ + { + raw: html`
`, + }, + ], + plugins: [ + function ({ addVariant }) { + addVariant('ogre', '@layer') + }, + ], + } + + return run('@tailwind components; @tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + @layer { + .ogre\:text-center { + text-align: center; + } + } + `) + }) + }) + test('prose-headings usage on its own', () => { let config = { content: [