diff --git a/CHANGELOG.md b/CHANGELOG.md index a2039b711..0e338fdc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add standalone CLI build for 64-bit Windows on ARM (`node16-win-arm64`) ([#10001](https://github.com/tailwindlabs/tailwindcss/pull/10001)) - Add `delay-0` and `duration-0` by default ([#10294](https://github.com/tailwindlabs/tailwindcss/pull/10294)) - Add logical properties support for inline direction ([#10166](https://github.com/tailwindlabs/tailwindcss/pull/10166)) +- Use `Lightning CSS` in the PostCSS Plugin ([#10399](https://github.com/tailwindlabs/tailwindcss/pull/10399)) ### Fixed diff --git a/integrations/postcss-cli/tests/integration.test.js b/integrations/postcss-cli/tests/integration.test.js index 929be5c50..964c57b82 100644 --- a/integrations/postcss-cli/tests/integration.test.js +++ b/integrations/postcss-cli/tests/integration.test.js @@ -222,11 +222,9 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } + .font-bold { font-weight: 700; } diff --git a/integrations/rollup-sass/tests/integration.test.js b/integrations/rollup-sass/tests/integration.test.js index 0c15ecedc..cdf9a11fb 100644 --- a/integrations/rollup-sass/tests/integration.test.js +++ b/integrations/rollup-sass/tests/integration.test.js @@ -220,11 +220,9 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } + .font-bold { font-weight: 700; } @@ -307,11 +305,9 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } + .font-bold { font-weight: 700; } diff --git a/integrations/rollup/tests/integration.test.js b/integrations/rollup/tests/integration.test.js index 65f813ff5..63a28bcb5 100644 --- a/integrations/rollup/tests/integration.test.js +++ b/integrations/rollup/tests/integration.test.js @@ -220,11 +220,9 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } + .font-bold { font-weight: 700; } diff --git a/integrations/tailwindcss-cli/tests/integration.test.js b/integrations/tailwindcss-cli/tests/integration.test.js index ddba060bc..42e35a895 100644 --- a/integrations/tailwindcss-cli/tests/integration.test.js +++ b/integrations/tailwindcss-cli/tests/integration.test.js @@ -472,10 +472,7 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } .font-bold { font-weight: 700; diff --git a/integrations/vite/tests/integration.test.js b/integrations/vite/tests/integration.test.js index 43a3052d8..b2d08bc27 100644 --- a/integrations/vite/tests/integration.test.js +++ b/integrations/vite/tests/integration.test.js @@ -263,11 +263,9 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } + .font-bold { font-weight: 700; } diff --git a/integrations/webpack-4/tests/integration.test.js b/integrations/webpack-4/tests/integration.test.js index 68b43b9a0..63ab3cbbf 100644 --- a/integrations/webpack-4/tests/integration.test.js +++ b/integrations/webpack-4/tests/integration.test.js @@ -227,11 +227,9 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } + .font-bold { font-weight: 700; } diff --git a/integrations/webpack-5/tests/integration.test.js b/integrations/webpack-5/tests/integration.test.js index 3eafb767a..95f3d5f5f 100644 --- a/integrations/webpack-5/tests/integration.test.js +++ b/integrations/webpack-5/tests/integration.test.js @@ -227,11 +227,9 @@ describe('watcher', () => { css` .btn { border-radius: 0.25rem; - padding-left: 0.5rem; - padding-right: 0.5rem; - padding-top: 0.25rem; - padding-bottom: 0.25rem; + padding: 0.25rem 0.5rem; } + .font-bold { font-weight: 700; } diff --git a/jest/customMatchers.js b/jest/customMatchers.js index 385faf7bd..1ea532f0c 100644 --- a/jest/customMatchers.js +++ b/jest/customMatchers.js @@ -1,85 +1,127 @@ const prettier = require('prettier') const { diff } = require('jest-diff') +const lightningcss = require('lightningcss') -function format(input) { +function formatPrettier(input) { return prettier.format(input, { parser: 'css', printWidth: 100, }) } +function format(input) { + try { + return lightningcss + .transform({ + filename: 'input.css', + code: Buffer.from(input), + minify: false, + targets: { chrome: 106 << 16 }, + drafts: { + nesting: true, + customMedia: true, + }, + }) + .code.toString('utf8') + } catch (err) { + try { + // Lightning CSS is pretty strict, so it will fail for `@media screen(md) {}` for example, + // in that case we can fallback to prettier since it doesn't really care. However if an + // actual syntax error is made, then we still want to show the proper error. + return formatPrettier(input.replace(/\n/g, '')) + } catch { + let lines = err.source.split('\n') + let e = new Error( + [ + 'Error formatting using Lightning CSS:', + '', + ...[ + '```css', + ...lines.slice(Math.max(err.loc.line - 3, 0), err.loc.line), + ' '.repeat(err.loc.column - 1) + '^-- ' + err.toString(), + ...lines.slice(err.loc.line, err.loc.line + 2), + '```', + ], + ].join('\n') + ) + if (Error.captureStackTrace) { + Error.captureStackTrace(e, toMatchFormattedCss) + } + throw e + } + } +} + +function toMatchFormattedCss(received = '', argument = '') { + let options = { + comment: 'formatCSS(received) === formatCSS(argument)', + isNot: this.isNot, + promise: this.promise, + } + + let formattedReceived = format(received) + let formattedArgument = format(argument) + + let pass = formattedReceived === formattedArgument + + let message = pass + ? () => { + return ( + this.utils.matcherHint('toMatchFormattedCss', undefined, undefined, options) + + '\n\n' + + `Expected: not ${this.utils.printExpected(formattedReceived)}\n` + + `Received: ${this.utils.printReceived(formattedArgument)}` + ) + } + : () => { + let actual = formatPrettier(formattedReceived) + let expected = formatPrettier(formattedArgument) + + let diffString = diff(expected, actual, { + expand: this.expand, + }) + + return ( + this.utils.matcherHint('toMatchFormattedCss', undefined, undefined, options) + + '\n\n' + + (diffString && diffString.includes('- Expect') + ? `Difference:\n\n${diffString}` + : `Expected: ${this.utils.printExpected(expected)}\n` + + `Received: ${this.utils.printReceived(actual)}`) + ) + } + + return { actual: received, message, pass } +} + expect.extend({ // Compare two CSS strings with all whitespace removed // This is probably naive but it's fast and works well enough. - toMatchCss(received, argument) { - function stripped(str) { - return str.replace(/\s/g, '').replace(/;/g, '') - } - - const options = { - comment: 'stripped(received) === stripped(argument)', - isNot: this.isNot, - promise: this.promise, - } - - const pass = stripped(received) === stripped(argument) - - const message = pass - ? () => { - return ( - this.utils.matcherHint('toMatchCss', undefined, undefined, options) + - '\n\n' + - `Expected: not ${this.utils.printExpected(format(received))}\n` + - `Received: ${this.utils.printReceived(format(argument))}` - ) - } - : () => { - const actual = format(received) - const expected = format(argument) - - const diffString = diff(expected, actual, { - expand: this.expand, - }) - - return ( - this.utils.matcherHint('toMatchCss', undefined, undefined, options) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(actual)}`) - ) - } - - return { actual: received, message, pass } - }, + toMatchCss: toMatchFormattedCss, + toMatchFormattedCss: toMatchFormattedCss, toIncludeCss(received, argument) { - function stripped(str) { - return str.replace('/* prettier-ignore */', '').replace(/\s/g, '').replace(/;/g, '') - } - - const options = { + let options = { comment: 'stripped(received).includes(stripped(argument))', isNot: this.isNot, promise: this.promise, } - const pass = stripped(received).includes(stripped(argument)) + let pass = format(received).includes(format(argument)) - const message = pass + let message = pass ? () => { return ( this.utils.matcherHint('toIncludeCss', undefined, undefined, options) + '\n\n' + - `Expected: not ${this.utils.printExpected(format(received))}\n` + - `Received: ${this.utils.printReceived(format(argument))}` + `Expected: not ${this.utils.printExpected(formatPrettier(received))}\n` + + `Received: ${this.utils.printReceived(formatPrettier(argument))}` ) } : () => { - const actual = format(received) - const expected = format(argument) + let actual = formatPrettier(received) + let expected = formatPrettier(argument) - const diffString = diff(expected, actual, { + let diffString = diff(expected, actual, { expand: this.expand, }) @@ -96,55 +138,3 @@ expect.extend({ return { actual: received, message, pass } }, }) - -expect.extend({ - // Compare two CSS strings with all whitespace removed - // This is probably naive but it's fast and works well enough. - toMatchFormattedCss(received = '', argument = '') { - function format(input) { - return prettier.format(input.replace(/\n/g, ''), { - parser: 'css', - printWidth: 100, - }) - } - const options = { - comment: 'stripped(received) === stripped(argument)', - isNot: this.isNot, - promise: this.promise, - } - - let formattedReceived = format(received) - let formattedArgument = format(argument) - - const pass = formattedReceived === formattedArgument - - const message = pass - ? () => { - return ( - this.utils.matcherHint('toMatchCss', undefined, undefined, options) + - '\n\n' + - `Expected: not ${this.utils.printExpected(formattedReceived)}\n` + - `Received: ${this.utils.printReceived(formattedArgument)}` - ) - } - : () => { - const actual = formattedReceived - const expected = formattedArgument - - const diffString = diff(expected, actual, { - expand: this.expand, - }) - - return ( - this.utils.matcherHint('toMatchCss', undefined, undefined, options) + - '\n\n' + - (diffString && diffString.includes('- Expect') - ? `Difference:\n\n${diffString}` - : `Expected: ${this.utils.printExpected(expected)}\n` + - `Received: ${this.utils.printReceived(actual)}`) - ) - } - - return { actual: received, message, pass } - }, -}) diff --git a/package-lock.json b/package-lock.json index 2bd976cbf..1bec10384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "lightningcss": "^1.17.1", + "lightningcss": "^1.18.0", "lilconfig": "^2.0.6", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -25284,7 +25284,7 @@ "is-glob": "^4.0.3", "jest": "^28.1.3", "jest-diff": "^28.1.3", - "lightningcss": "^1.17.1", + "lightningcss": "^1.18.0", "lilconfig": "^2.0.6", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", diff --git a/package-lock.stable.json b/package-lock.stable.json index ff0e0dcf5..6c40c1957 100644 --- a/package-lock.stable.json +++ b/package-lock.stable.json @@ -44,6 +44,7 @@ "@swc/jest": "0.2.24", "@swc/register": "0.1.10", "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", "concurrently": "^7.5.0", "cssnano": "^5.1.14", "esbuild": "^0.16.10", @@ -52,6 +53,7 @@ "eslint-plugin-prettier": "^4.2.1", "jest": "^28.1.3", "jest-diff": "^28.1.3", + "lightningcss": "^1.18.0", "prettier": "^2.8.1", "rimraf": "^3.0.0", "source-map-js": "^1.0.2", @@ -3518,6 +3520,18 @@ "version": "1.0.0", "license": "MIT" }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "dev": true, @@ -5876,6 +5890,192 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.18.0.tgz", + "integrity": "sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==", + "dev": true, + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.18.0", + "lightningcss-darwin-x64": "1.18.0", + "lightningcss-linux-arm-gnueabihf": "1.18.0", + "lightningcss-linux-arm64-gnu": "1.18.0", + "lightningcss-linux-arm64-musl": "1.18.0", + "lightningcss-linux-x64-gnu": "1.18.0", + "lightningcss-linux-x64-musl": "1.18.0", + "lightningcss-win32-x64-msvc": "1.18.0" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "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" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "2.0.6", "license": "MIT", @@ -10238,6 +10438,12 @@ "defined": { "version": "1.0.0" }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true + }, "detect-newline": { "version": "3.1.0", "dev": true @@ -11842,6 +12048,79 @@ } } }, + "lightningcss": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.18.0.tgz", + "integrity": "sha512-uk10tNxi5fhZqU93vtYiQgx/8a9f0Kvtj5AXIm+VlOXY+t/DWDmCZWJEkZJmmALgvbS6aAW8or+Kq85eJ6TDTw==", + "dev": true, + "requires": { + "detect-libc": "^1.0.3", + "lightningcss-darwin-arm64": "1.18.0", + "lightningcss-darwin-x64": "1.18.0", + "lightningcss-linux-arm-gnueabihf": "1.18.0", + "lightningcss-linux-arm64-gnu": "1.18.0", + "lightningcss-linux-arm64-musl": "1.18.0", + "lightningcss-linux-x64-gnu": "1.18.0", + "lightningcss-linux-x64-musl": "1.18.0", + "lightningcss-win32-x64-msvc": "1.18.0" + } + }, + "lightningcss-darwin-arm64": { + "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==", + "dev": true, + "optional": true + }, + "lightningcss-darwin-x64": { + "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==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm-gnueabihf": { + "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==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm64-gnu": { + "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==", + "dev": true, + "optional": true + }, + "lightningcss-linux-arm64-musl": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.18.0.tgz", + "integrity": "sha512-2FWHa8iUhShnZnqhn2wfIcK5adJat9hAAaX7etNsoXJymlliDIOFuBQEsba2KBAZSM4QqfQtvRdR7m8i0I7ybQ==", + "dev": true, + "optional": true + }, + "lightningcss-linux-x64-gnu": { + "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==", + "dev": true, + "optional": true + }, + "lightningcss-linux-x64-musl": { + "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==", + "dev": true, + "optional": true + }, + "lightningcss-win32-x64-msvc": { + "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==", + "dev": true, + "optional": true + }, "lilconfig": { "version": "2.0.6" }, diff --git a/package.json b/package.json index 14a5d5977..0a4cd90e1 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "lightningcss": "^1.17.1", + "lightningcss": "^1.18.0", "lilconfig": "^2.0.6", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", diff --git a/package.stable.json b/package.stable.json index 15eefa949..7161e4c54 100644 --- a/package.stable.json +++ b/package.stable.json @@ -52,6 +52,7 @@ "@swc/jest": "0.2.24", "@swc/register": "0.1.10", "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", "concurrently": "^7.5.0", "cssnano": "^5.1.14", "esbuild": "^0.16.10", @@ -60,6 +61,7 @@ "eslint-plugin-prettier": "^4.2.1", "jest": "^28.1.3", "jest-diff": "^28.1.3", + "lightningcss": "^1.18.0", "prettier": "^2.8.1", "rimraf": "^3.0.0", "source-map-js": "^1.0.2", @@ -84,7 +86,7 @@ "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.21", + "postcss": "^8.0.9", "postcss-import": "^14.1.0", "postcss-js": "^4.0.0", "postcss-load-config": "^3.1.4", @@ -104,6 +106,9 @@ "/standalone-cli/", "\\.test\\.skip\\.js$" ], + "transformIgnorePatterns": [ + "node_modules/(?!lightningcss)" + ], "transform": { "\\.js$": "@swc/jest", "\\.ts$": "@swc/jest" diff --git a/src/plugin.js b/src/plugin.js index 7dc41c317..4fa8ce757 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -34,6 +34,66 @@ module.exports = function tailwindcss(configOrPath) { processTailwindFeatures(context)(root, result) }, + env.OXIDE && + function lightningCssPlugin(_root, result) { + let postcss = require('postcss') + let lightningcss = require('lightningcss') + let browserslist = require('browserslist') + + try { + let transformed = lightningcss.transform({ + filename: result.opts.from, + code: Buffer.from(result.root.toString()), + minify: false, + sourceMap: !!result.map, + inputSourceMap: result.map ? result.map.toString() : undefined, + targets: + typeof process !== 'undefined' && process.env.JEST_WORKER_ID + ? { chrome: 106 << 16 } + : lightningcss.browserslistToTargets( + browserslist(require('../package.json').browserslist) + ), + + drafts: { + nesting: true, + customMedia: true, + }, + }) + + result.map = Object.assign(result.map ?? {}, { + toJSON() { + return transformed.map.toJSON() + }, + toString() { + return transformed.map.toString() + }, + }) + + result.root = postcss.parse(transformed.code.toString('utf8')) + } catch (err) { + if (typeof process !== 'undefined' && process.env.JEST_WORKER_ID) { + let lines = err.source.split('\n') + err = new Error( + [ + 'Error formatting using Lightning CSS:', + '', + ...[ + '```css', + ...lines.slice(Math.max(err.loc.line - 3, 0), err.loc.line), + ' '.repeat(err.loc.column - 1) + '^-- ' + err.toString(), + ...lines.slice(err.loc.line, err.loc.line + 2), + '```', + ], + ].join('\n') + ) + } + + if (Error.captureStackTrace) { + Error.captureStackTrace(err, lightningCssPlugin) + } + throw err + } + }, env.DEBUG && function (root) { console.timeEnd('JIT TOTAL') diff --git a/tests/arbitrary-values.oxide.test.css b/tests/arbitrary-values.oxide.test.css index 34bb93bc6..326149b90 100644 --- a/tests/arbitrary-values.oxide.test.css +++ b/tests/arbitrary-values.oxide.test.css @@ -182,20 +182,16 @@ width: var(--width, calc(100% + 1rem)); } .w-\[\{\{\}\}\] { - width: { - { - } - } + width: {{} }; } .w-\[\{\}\] { - width: { - } + width: {}; } .min-w-\[3\.23rem\] { min-width: 3.23rem; } .min-w-\[calc\(1-\(\(12-3\)\*0\.5\)\)\] { - min-width: calc(1 - ((12 - 3) * 0.5)); + min-width: -3.5px; } .min-w-\[calc\(1-\(var\(--something\)\*0\.5\)\)\] { min-width: calc(1 - (var(--something) * 0.5)); @@ -420,7 +416,7 @@ scroll-padding-top: var(--scroll-padding); } .list-\[\'\\1f44d\'\] { - list-style-type: '\1F44D'; + list-style-type: '\1f44d'; } .list-\[var\(--value\)\] { list-style-type: var(--value); @@ -685,13 +681,7 @@ .bg-\[image\:var\(--value\)\2c var\(--value\)\] { background-image: var(--value), var(--value); } -.bg-\[linear-gradient\(\#eee\2c - \#fff\)\2c - conic-gradient\(red\2c - orange\2c - yellow\2c - green\2c - blue\)\] { +.bg-\[linear-gradient\(\#eee\,\#fff\)\,conic-gradient\(red\,orange\,yellow\,green\,blue\)\] { background-image: linear-gradient(#eee, #fff), conic-gradient(red, orange, yellow, green, blue); } .bg-\[linear-gradient\(\#eee\2c \#fff\)\] { diff --git a/tests/arbitrary-values.test.css b/tests/arbitrary-values.test.css index 3098a954a..e446fcab3 100644 --- a/tests/arbitrary-values.test.css +++ b/tests/arbitrary-values.test.css @@ -182,14 +182,10 @@ width: var(--width, calc(100% + 1rem)); } .w-\[\{\{\}\}\] { - width: { - { - } - } + width: {{} }; } .w-\[\{\}\] { - width: { - } + width: {}; } .min-w-\[3\.23rem\] { min-width: 3.23rem; @@ -420,7 +416,7 @@ scroll-padding-top: var(--scroll-padding); } .list-\[\'\\1f44d\'\] { - list-style-type: '\1F44D'; + list-style-type: '\1f44d'; } .list-\[var\(--value\)\] { list-style-type: var(--value); @@ -685,13 +681,7 @@ .bg-\[image\:var\(--value\)\2c var\(--value\)\] { background-image: var(--value), var(--value); } -.bg-\[linear-gradient\(\#eee\2c - \#fff\)\2c - conic-gradient\(red\2c - orange\2c - yellow\2c - green\2c - blue\)\] { +.bg-\[linear-gradient\(\#eee\,\#fff\)\,conic-gradient\(red\,orange\,yellow\,green\,blue\)\] { background-image: linear-gradient(#eee, #fff), conic-gradient(red, orange, yellow, green, blue); } .bg-\[linear-gradient\(\#eee\2c \#fff\)\] { diff --git a/tests/arbitrary-values.test.html b/tests/arbitrary-values.test.html index 56a9d3ae6..047dae8ca 100644 --- a/tests/arbitrary-values.test.html +++ b/tests/arbitrary-values.test.html @@ -123,7 +123,7 @@
-
+
diff --git a/tests/arbitrary-variants.test.js b/tests/arbitrary-variants.test.js index af29b4927..1b7c08290 100644 --- a/tests/arbitrary-variants.test.js +++ b/tests/arbitrary-variants.test.js @@ -644,15 +644,15 @@ crosscheck(({ stable, oxide }) => {
-
+
-
-
+
+
@@ -667,43 +667,37 @@ crosscheck(({ stable, oxide }) => { ` return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .aria-checked\:underline[aria-checked='true'] { - text-decoration-line: underline; - } - .aria-\[labelledby\=a_b\]\:underline[aria-labelledby='a b'] { - text-decoration-line: underline; - } - .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'] { - text-decoration-line: underline; - } - .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline { - text-decoration-line: underline; - } - .group[aria-checked='true'] .group-aria-checked\:underline { - text-decoration-line: underline; - } - .group[aria-labelledby='a b'] .group-aria-\[labelledby\=a_b\]\:underline { - text-decoration-line: underline; - } - .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline { - text-decoration-line: underline; - } - .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline { - text-decoration-line: underline; - } - .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline { - text-decoration-line: underline; - } - .peer[aria-checked='true'] ~ .peer-aria-checked\:underline { - text-decoration-line: underline; - } - .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=a_b\]\:underline { - text-decoration-line: underline; - } - .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline { + stable.expect(result.css).toMatchFormattedCss(css` + .underline, + .aria-checked\:underline[aria-checked='true'], + .aria-\[labelledby\=\'a_b\'\]\:underline[aria-labelledby='a b'], + .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'], + .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline, + .group[aria-checked='true'] .group-aria-checked\:underline, + .group[aria-labelledby='a b'] .group-aria-\[labelledby\=\'a_b\'\]\:underline, + .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline, + .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline, + .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline, + .peer[aria-checked='true'] ~ .peer-aria-checked\:underline, + .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=\'a_b\'\]\:underline, + .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline, + .peer[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\:underline { text-decoration-line: underline; } + `) + oxide.expect(result.css).toMatchFormattedCss(css` + .aria-checked\:underline[aria-checked='true'], + .aria-\[labelledby\=\'a_b\'\]\:underline[aria-labelledby='a b'], + .aria-\[sort\=ascending\]\:underline[aria-sort='ascending'], + .group\/foo[aria-checked='true'] .group-aria-checked\/foo\:underline, + .group[aria-checked='true'] .group-aria-checked\:underline, + .group[aria-labelledby='a b'] .group-aria-\[labelledby\=\'a_b\'\]\:underline, + .group\/foo[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\/foo\:underline, + .group[aria-sort='ascending'] .group-aria-\[sort\=ascending\]\:underline, + .peer\/foo[aria-checked='true'] ~ .peer-aria-checked\/foo\:underline, + .peer[aria-checked='true'] ~ .peer-aria-checked\:underline, + .peer[aria-labelledby='a b'] ~ .peer-aria-\[labelledby\=\'a_b\'\]\:underline, + .peer\/foo[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\/foo\:underline, .peer[aria-sort='ascending'] ~ .peer-aria-\[sort\=ascending\]\:underline { text-decoration-line: underline; } @@ -724,15 +718,15 @@ crosscheck(({ stable, oxide }) => {
-
+
-
-
+
+
@@ -747,43 +741,37 @@ crosscheck(({ stable, oxide }) => { ` return run(input, config).then((result) => { - expect(result.css).toMatchFormattedCss(css` - .data-checked\:underline[data-ui~='checked'] { - text-decoration-line: underline; - } - .data-\[foo\=bar_baz\]\:underline[data-foo='bar baz'] { - text-decoration-line: underline; - } - .data-\[position\=top\]\:underline[data-position='top'] { - text-decoration-line: underline; - } - .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline { - text-decoration-line: underline; - } - .group[data-ui~='checked'] .group-data-checked\:underline { - text-decoration-line: underline; - } - .group[data-foo='bar baz'] .group-data-\[foo\=bar_baz\]\:underline { - text-decoration-line: underline; - } - .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline { - text-decoration-line: underline; - } - .group[data-position='top'] .group-data-\[position\=top\]\:underline { - text-decoration-line: underline; - } - .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline { - text-decoration-line: underline; - } - .peer[data-ui~='checked'] ~ .peer-data-checked\:underline { - text-decoration-line: underline; - } - .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=bar_baz\]\:underline { - text-decoration-line: underline; - } - .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline { + stable.expect(result.css).toMatchFormattedCss(css` + .underline, + .data-checked\:underline[data-ui~='checked'], + .data-\[foo\=\'bar_baz\'\]\:underline[data-foo='bar baz'], + .data-\[position\=top\]\:underline[data-position='top'], + .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline, + .group[data-ui~='checked'] .group-data-checked\:underline, + .group[data-foo='bar baz'] .group-data-\[foo\=\'bar_baz\'\]\:underline, + .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline, + .group[data-position='top'] .group-data-\[position\=top\]\:underline, + .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline, + .peer[data-ui~='checked'] ~ .peer-data-checked\:underline, + .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=\'bar_baz\'\]\:underline, + .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline, + .peer[data-position='top'] ~ .peer-data-\[position\=top\]\:underline { text-decoration-line: underline; } + `) + oxide.expect(result.css).toMatchFormattedCss(css` + .data-checked\:underline[data-ui~='checked'], + .data-\[foo\=\'bar_baz\'\]\:underline[data-foo='bar baz'], + .data-\[position\=top\]\:underline[data-position='top'], + .group\/foo[data-ui~='checked'] .group-data-checked\/foo\:underline, + .group[data-ui~='checked'] .group-data-checked\:underline, + .group[data-foo='bar baz'] .group-data-\[foo\=\'bar_baz\'\]\:underline, + .group\/foo[data-position='top'] .group-data-\[position\=top\]\/foo\:underline, + .group[data-position='top'] .group-data-\[position\=top\]\:underline, + .peer\/foo[data-ui~='checked'] ~ .peer-data-checked\/foo\:underline, + .peer[data-ui~='checked'] ~ .peer-data-checked\:underline, + .peer[data-foo='bar baz'] ~ .peer-data-\[foo\=\'bar_baz\'\]\:underline, + .peer\/foo[data-position='top'] ~ .peer-data-\[position\=top\]\/foo\:underline, .peer[data-position='top'] ~ .peer-data-\[position\=top\]\:underline { text-decoration-line: underline; } @@ -807,7 +795,7 @@ crosscheck(({ stable, oxide }) => {
-
+
@@ -835,37 +823,42 @@ crosscheck(({ stable, oxide }) => { .supports-grid\:underline { text-decoration-line: underline; } - } - @supports (display: grid) { + .supports-\[display\:grid\]\:grid { display: grid; } } + @supports (foo: bar) and (bar: baz) { .supports-\[\(foo\:bar\)and\(bar\:baz\)\]\:underline { text-decoration-line: underline; } } + @supports (foo: bar) or (bar: baz) { .supports-\[\(foo\:bar\)or\(bar\:baz\)\]\:underline { text-decoration-line: underline; } } + @supports (container-type: var(--tw)) { .supports-\[container-type\]\:underline { text-decoration-line: underline; } } + @supports not (foo: bar) { .supports-\[not\(foo\:bar\)\]\:underline { text-decoration-line: underline; } } + @supports selector(A > B) { - .supports-\[selector\(A\>B\)\]\:underline { + .supports-\[selector\(A_\>_B\)\]\:underline { text-decoration-line: underline; } } + @supports (transform-origin: 5% 5%) { .supports-\[transform-origin\:5\%_5\%\]\:underline { text-decoration-line: underline; @@ -1229,22 +1222,22 @@ crosscheck(({ stable, oxide }) => { corePlugins: { preflight: false }, plugins: [ function ({ addVariant, matchVariant }) { - addVariant('foo1', '&[data-foo=1]') - addVariant('foo2', '&[data-foo=2]') + addVariant('foo1', "&[data-foo='1']") + addVariant('foo2', "&[data-foo='2']") - matchVariant('bar', (value) => `&[data-bar=${value}]`, { + matchVariant('bar', (value) => `&[data-bar='${value}']`, { sort: (a, b) => b.value - a.value, }) - addVariant('baz1', '&[data-baz=1]') - addVariant('baz2', '&[data-baz=2]') + addVariant('baz1', "&[data-baz='1']") + addVariant('baz2', "&[data-baz='2']") - matchVariant('qux', (value) => `&[data-qux=${value}]`, { + matchVariant('qux', (value) => `&[data-qux='${value}']`, { sort: (a, b) => b.value - a.value, }) - addVariant('fred1', '&[data-fred=1]') - addVariant('fred2', '&[data-fred=2]') + addVariant('fred1', "&[data-fred='1']") + addVariant('fred2', "&[data-fred='2']") }, ], } @@ -1255,99 +1248,37 @@ crosscheck(({ stable, oxide }) => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` - .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } - .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'] { - padding: 0.25rem; - } + .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred1\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='1'], + .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[2\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='2'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz1\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='1'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo1\:p-1[data-foo='1'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz2\:bar-\[2\]\:foo2\:p-1[data-foo='2'][data-bar='2'][data-baz='2'][data-qux='1'][data-fred='2'], + .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo1\:p-1[data-foo='1'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'], .fred2\:qux-\[1\]\:baz2\:bar-\[1\]\:foo2\:p-1[data-foo='2'][data-bar='1'][data-baz='2'][data-qux='1'][data-fred='2'] { padding: 0.25rem; } diff --git a/tests/basic-usage.test.js b/tests/basic-usage.test.js index 11f8c30b7..b64878032 100644 --- a/tests/basic-usage.test.js +++ b/tests/basic-usage.test.js @@ -505,7 +505,7 @@ crosscheck(({ stable, oxide }) => { content: [{ raw: html`
` }], theme: { boxShadow: { - lg: 'var(-a, 0 35px 60px -15px rgba(0, 0, 0)), 0 0 1px rgb(0, 0, 0)', + lg: 'var(--a, 0 35px 60px -15px rgba(0, 0, 0)), 0 0 1px rgb(0, 0, 0)', }, }, corePlugins: { preflight: false }, @@ -518,7 +518,7 @@ crosscheck(({ stable, oxide }) => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` .shadow-lg { - --tw-shadow: var(-a, 0 35px 60px -15px rgba(0, 0, 0)), 0 0 1px rgb(0, 0, 0); + --tw-shadow: var(--a, 0 35px 60px -15px rgba(0, 0, 0)), 0 0 1px rgb(0, 0, 0); --tw-shadow-colored: 0 35px 60px -15px var(--tw-shadow-color), 0 0 1px var(--tw-shadow-color); box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), @@ -571,12 +571,8 @@ crosscheck(({ stable, oxide }) => { return run(input, config).then((result) => { expect(result.css).toMatchFormattedCss(css` - .bg-\[url\(\'\/images\/one-two-three\.png\'\)\2c - linear-gradient\(to_right\2c - _\#eeeeee\2c - _\#000000\)\] { - background-image: url('/images/one-two-three.png'), - linear-gradient(to right, #eeeeee, #000000); + .bg-\[url\(\'\/images\/one-two-three\.png\'\)\,linear-gradient\(to_right\,_\#eeeeee\,_\#000000\)\] { + background-image: url('/images/one-two-three.png'), linear-gradient(to right, #eee, #000); } `) }) diff --git a/tests/preflight.test.js b/tests/preflight.test.js index 76b5ab93a..4f6f722c7 100644 --- a/tests/preflight.test.js +++ b/tests/preflight.test.js @@ -1,6 +1,6 @@ import { crosscheck, run, html, css } from './util/run' -crosscheck(() => { +crosscheck(({ stable, oxide }) => { it('preflight has a correct border color fallback', () => { let config = { content: [{ raw: html`
` }], @@ -17,7 +17,9 @@ crosscheck(() => { ` return run(input, config).then((result) => { - expect(result.css).toContain(`border-color: currentColor;`) + stable.expect(result.css).toContain(`border-color: currentColor;`) + // Lightning CSS optimizes this to just `border-color: 0 solid;` based on the br value. + oxide.expect(result.css).toContain(`border: 0 solid;`) }) }) }) diff --git a/tests/source-maps.test.js b/tests/source-maps.test.js index 39ce7555c..cb7d5f1af 100644 --- a/tests/source-maps.test.js +++ b/tests/source-maps.test.js @@ -2,8 +2,9 @@ import postcss from 'postcss' import { parseSourceMaps } from './util/source-maps' import { crosscheck, runWithSourceMaps as run, html, css, map } from './util/run' -crosscheck(() => { - it('apply generates source maps', async () => { +crosscheck(({ stable, oxide }) => { + oxide.test.todo('apply generates source maps') + stable.test('apply generates source maps', async () => { let config = { content: [ { @@ -65,7 +66,8 @@ crosscheck(() => { ]) }) - it('preflight + base have source maps', async () => { + oxide.test.todo('preflight + base have source maps') + stable.test('preflight + base have source maps', async () => { let config = { content: [], } @@ -386,7 +388,8 @@ crosscheck(() => { ]) }) - test('utilities have source maps', async () => { + oxide.test.todo('utilities have source maps') + stable.test('utilities have source maps', async () => { let config = { content: [{ raw: `text-red-500` }], } @@ -411,7 +414,8 @@ crosscheck(() => { ]) }) - it('components have source maps', async () => { + oxide.test.todo('components have source maps') + stable.test('components have source maps', async () => { let config = { content: [{ raw: `container` }], } @@ -460,58 +464,62 @@ crosscheck(() => { ]) }) - it('source maps for layer rules are not rewritten to point to @tailwind directives', async () => { - let config = { - content: [{ raw: `font-normal foo hover:foo` }], - } + oxide.test.todo('source maps for layer rules are not rewritten to point to @tailwind directives') + stable.test( + 'source maps for layer rules are not rewritten to point to @tailwind directives', + async () => { + let config = { + content: [{ raw: `font-normal foo hover:foo` }], + } - let utilitiesFile = postcss.parse( - css` - @tailwind utilities; - `, - { from: 'components.css', map: { prev: map } } - ) + let utilitiesFile = postcss.parse( + css` + @tailwind utilities; + `, + { from: 'components.css', map: { prev: map } } + ) - let mainCssFile = postcss.parse( - css` - @layer utilities { - .foo { - background-color: red; + let mainCssFile = postcss.parse( + css` + @layer utilities { + .foo { + background-color: red; + } } - } - `, - { from: 'input.css', map: { prev: map } } - ) + `, + { from: 'input.css', map: { prev: map } } + ) - // Just pretend that there's an @import in `mainCssFile` that imports the nodes from `utilitiesFile` - let input = postcss.root({ - nodes: [...utilitiesFile.nodes, ...mainCssFile.nodes], - source: mainCssFile.source, - }) + // Just pretend that there's an @import in `mainCssFile` that imports the nodes from `utilitiesFile` + let input = postcss.root({ + nodes: [...utilitiesFile.nodes, ...mainCssFile.nodes], + source: mainCssFile.source, + }) - let result = await run(input, config) + let result = await run(input, config) - let { sources, annotations } = parseSourceMaps(result) + let { sources, annotations } = parseSourceMaps(result) - // All CSS generated by Tailwind CSS should be annotated with source maps - // And always be able to point to the original source file - expect(sources).not.toContain('') + // All CSS generated by Tailwind CSS should be annotated with source maps + // And always be able to point to the original source file + expect(sources).not.toContain('') - // And we should see that the source map for the layer rule is not rewritten - // to point to the @tailwind directive but instead points to the original - expect(sources.length).toBe(2) - expect(sources).toEqual(['components.css', 'input.css']) + // And we should see that the source map for the layer rule is not rewritten + // to point to the @tailwind directive but instead points to the original + expect(sources.length).toBe(2) + expect(sources).toEqual(['components.css', 'input.css']) - expect(annotations).toEqual([ - '2:8 -> 1:0', - '2:8 -> 2:12', - '2:27 -> 3:0', - '3:10 -> 4:10', - '4:12-33 -> 5:12-33', - '5:10 -> 6:10', - '3:10 -> 7:10', - '4:12-33 -> 8:12-33', - '5:10 -> 9:10', - ]) - }) + expect(annotations).toEqual([ + '2:10 -> 1:0', + '2:10 -> 2:14', + '2:29 -> 3:0', + '3:12 -> 4:12', + '4:14-35 -> 5:14-35', + '5:12 -> 6:12', + '3:12 -> 7:12', + '4:14-35 -> 8:14-35', + '5:12 -> 9:12', + ]) + } + ) }) diff --git a/tests/util/source-maps.js b/tests/util/source-maps.js index 695ba91f6..4ed263126 100644 --- a/tests/util/source-maps.js +++ b/tests/util/source-maps.js @@ -6,7 +6,7 @@ import { SourceMapConsumer } from 'source-map-js' * @param {import('postcss').Result} result */ export function parseSourceMaps(result) { - const map = result.map.toJSON() + let map = result.map.toJSON() return { sources: map.sources,