tailwindcss/integrations/webpack/index.test.ts

111 lines
2.9 KiB
TypeScript
Raw Permalink Normal View History

Watch CSS module files for changes (#17467) This PR is a follow-up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17433 In the other PR we allow scanning CSS files for extracting usages of CSS variables. This is important for `.module.css` files that reference these variables but aren't in the same big AST of the main CSS file. This PR also makes sure to watch for changes in those registered CSS files and re-extract the variables when they change. This PR took a bit longer than expected because I was trying to make sure that writing to `./dist/out.css` works without infinite-looping (e.g.: we had issues with this in Tailwind CSS v3 with webpack). But I couldn't reproduce the issue at all. I did had some code that tried to detect if the CSS file contained license headers and skip in (because then it's very likely an output CSS file) but even without it the tests were fine. I setup integration tests with `@tailwindcss/cli` itself, and with tools that use webpack. Added a test for Next.js, and a dedicated webpack test as well. Even without tests, locally, I couldn't reproduce an infinite loop due to changes in an output CSS file... Eventually dropped the code that tries to detect output CSS files. One thing to keep in mind is that if you change any of your "main" CSS files, then we will trigger a full rebuild anyway, so this change is only required for unrelated CSS files (like CSS module files) that use CSS variables. ## Test plan 1. Added integration tests for the CLI and Next.js 2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
import { css, html, js, json, test } from '../utils'
test(
'webpack + PostCSS (watch)',
Watch CSS module files for changes (#17467) This PR is a follow-up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17433 In the other PR we allow scanning CSS files for extracting usages of CSS variables. This is important for `.module.css` files that reference these variables but aren't in the same big AST of the main CSS file. This PR also makes sure to watch for changes in those registered CSS files and re-extract the variables when they change. This PR took a bit longer than expected because I was trying to make sure that writing to `./dist/out.css` works without infinite-looping (e.g.: we had issues with this in Tailwind CSS v3 with webpack). But I couldn't reproduce the issue at all. I did had some code that tried to detect if the CSS file contained license headers and skip in (because then it's very likely an output CSS file) but even without it the tests were fine. I setup integration tests with `@tailwindcss/cli` itself, and with tools that use webpack. Added a test for Next.js, and a dedicated webpack test as well. Even without tests, locally, I couldn't reproduce an infinite loop due to changes in an output CSS file... Eventually dropped the code that tries to detect output CSS files. One thing to keep in mind is that if you change any of your "main" CSS files, then we will trigger a full rebuild anyway, so this change is only required for unrelated CSS files (like CSS module files) that use CSS variables. ## Test plan 1. Added integration tests for the CLI and Next.js 2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
{
fs: {
'package.json': json`
{
"main": "./src/index.js",
"browser": "./src/index.js",
"dependencies": {
"css-loader": "^6",
"postcss": "^8",
"postcss-loader": "^7",
"webpack": "^5",
"webpack-cli": "^5",
"mini-css-extract-plugin": "^2",
"tailwindcss": "workspace:^",
"@tailwindcss/postcss": "workspace:^"
}
}
`,
'postcss.config.js': js`
/** @type {import('postcss-load-config').Config} */
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
},
}
`,
'webpack.config.js': js`
let MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
output: {
clean: true,
},
plugins: [new MiniCssExtractPlugin()],
module: {
rules: [
{
test: /.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader'],
},
],
},
}
`,
'src/index.js': js`import './index.css'`,
'src/index.html': html`
<div class="flex"></div>
`,
'src/index.css': css`
@import 'tailwindcss/theme';
@import 'tailwindcss/utilities';
`,
'src/unrelated.module.css': css`
.module {
color: var(--color-blue-500);
}
`,
},
},
async ({ fs, spawn, exec, expect }) => {
// Generate the initial build so output CSS files exist on disk
await exec('pnpm webpack --mode=development')
Watch CSS module files for changes (#17467) This PR is a follow-up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17433 In the other PR we allow scanning CSS files for extracting usages of CSS variables. This is important for `.module.css` files that reference these variables but aren't in the same big AST of the main CSS file. This PR also makes sure to watch for changes in those registered CSS files and re-extract the variables when they change. This PR took a bit longer than expected because I was trying to make sure that writing to `./dist/out.css` works without infinite-looping (e.g.: we had issues with this in Tailwind CSS v3 with webpack). But I couldn't reproduce the issue at all. I did had some code that tried to detect if the CSS file contained license headers and skip in (because then it's very likely an output CSS file) but even without it the tests were fine. I setup integration tests with `@tailwindcss/cli` itself, and with tools that use webpack. Added a test for Next.js, and a dedicated webpack test as well. Even without tests, locally, I couldn't reproduce an infinite loop due to changes in an output CSS file... Eventually dropped the code that tries to detect output CSS files. One thing to keep in mind is that if you change any of your "main" CSS files, then we will trigger a full rebuild anyway, so this change is only required for unrelated CSS files (like CSS module files) that use CSS variables. ## Test plan 1. Added integration tests for the CLI and Next.js 2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
// NOTE: We are writing to an output CSS file which is not being ignored by
// `.gitignore` nor marked with `@source not`. This should not result in an
// infinite loop.
let process = await spawn('pnpm webpack --mode=development --watch')
Watch CSS module files for changes (#17467) This PR is a follow-up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17433 In the other PR we allow scanning CSS files for extracting usages of CSS variables. This is important for `.module.css` files that reference these variables but aren't in the same big AST of the main CSS file. This PR also makes sure to watch for changes in those registered CSS files and re-extract the variables when they change. This PR took a bit longer than expected because I was trying to make sure that writing to `./dist/out.css` works without infinite-looping (e.g.: we had issues with this in Tailwind CSS v3 with webpack). But I couldn't reproduce the issue at all. I did had some code that tried to detect if the CSS file contained license headers and skip in (because then it's very likely an output CSS file) but even without it the tests were fine. I setup integration tests with `@tailwindcss/cli` itself, and with tools that use webpack. Added a test for Next.js, and a dedicated webpack test as well. Even without tests, locally, I couldn't reproduce an infinite loop due to changes in an output CSS file... Eventually dropped the code that tries to detect output CSS files. One thing to keep in mind is that if you change any of your "main" CSS files, then we will trigger a full rebuild anyway, so this change is only required for unrelated CSS files (like CSS module files) that use CSS variables. ## Test plan 1. Added integration tests for the CLI and Next.js 2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
await process.onStdout((m) => m.includes('compiled successfully in'))
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
"
--- ./dist/main.css ---
:root, :host {
Improve compatibility with Safari 15 (#17435) This PR improves the compatibility with Tailwind CSS v4 with unsupported browsers with the goal to greatly improve compatibility with Safari 15. To make this work, this PR makes the following changes to all code - Change `oklab(…)` default theme values to use a percentage in the first place (so instead of `--color-red-500: oklch(0.637 0.237 25.331);` we now define it as `--color-red-500: oklch(63.7% 0.237 25.331);` since this syntax has much broader support on Safari). - Polyfill `@property` with a `@supports` query targeting older versions of Safari and Firefox * - Create fallbacks for the `color-mix(…)` function that use _inlined color values from your theme_ so that they can be computed a compile time by `lightningcss`. These fallbacks will convert to srgb to increase compatibility. - Create fallbacks for the _relative color_ feature used in the new shadow utilities and using `color-mix(…)` in case _relative color_ is applied on `currentcolor` (due to limited browser support) - Create fallbacks for gradient interpolation methods (e.g. to support `bg-linear-to-r/oklab`) - Polyfill `@media` queries range syntax. ## A simplified example Given this example CSS input: ```css @import 'tailwindcss'; @source inline('from-cyan-500/50 bg-linear-45'); ``` Here's the updated output CSS including the newly added polyfills and updated `oklab` values: ```css .bg-linear-45 { --tw-gradient-position: 45deg; background-image: linear-gradient(var(--tw-gradient-stops)); } @supports (background-image: linear-gradient(in lab, red, red)) { .bg-linear-45 { --tw-gradient-position: 45deg in oklab; } } .from-cyan-500\\/50 { --tw-gradient-from: oklab(71.5% -.11682 -.08247 / .5); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); } @supports (color: color-mix(in lab, red, red)) { .from-cyan-500\\/50 { --tw-gradient-from: color-mix(in oklab, var(--color-cyan-500) 50%, transparent); } } :root, :host { --color-cyan-500: oklch(71.5% .143 215.221); } @supports (((-webkit-hyphens: none)) and (not (margin-trim: 1lh))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { @layer base { *, :before, :after, ::backdrop { --tw-gradient-position: initial; --tw-gradient-from: #0000; --tw-gradient-via: #0000; --tw-gradient-to: #0000; --tw-gradient-stops: initial; --tw-gradient-via-stops: initial; --tw-gradient-from-position: 0%; --tw-gradient-via-position: 50%; --tw-gradient-to-position: 100%; } } } @property --tw-gradient-position { syntax: "*"; inherits: false } @property --tw-gradient-from { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-via { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-to { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-stops { syntax: "*"; inherits: false } @property --tw-gradient-via-stops { syntax: "*"; inherits: false } @property --tw-gradient-from-position { syntax: "<length-percentage>"; inherits: false; initial-value: 0%; } @property --tw-gradient-via-position { syntax: "<length-percentage>"; inherits: false; initial-value: 50%; } @property --tw-gradient-to-position { syntax: "<length-percentage>"; inherits: false; initial-value: 100%; } ``` ## \* A note on `@property` polyfills and CSS modules On Next.js, CSS module files are required to be _pure_, meaning that all selectors must either be scoped to a class or an ID. Fortunatnyl for us, this does not apply to `@property` rules which we've been using before to initialize CSS variables. However, since we're now bringing back the `@property` polyfills, that would cause unexpected rules to be exported from the CSS file as this: ```css @reference "tailwindcss"; .skew { @apply skew-7; } ``` Would turn to the following file: ```css .skew { /* … */ } @supports (/*…*/) { @layer base { *, :before, :after, ::backdrop { --tw-gradient-position: initial; } } } @property /* … */ ``` Notice that this adds a `*` selector which is not considered pure. Unfortunately there is no way for us to silence this warning or work around it, as the dependency causing this errors ([`postcss-modules-local-by-default`](https://github.com/css-modules/postcss-modules-local-by-default)) is bundled into Next.js. To work around crashes, these polyfills will not apply to CSS modules processed by the PostCSS extension for now. ## Testing on tailwindcss.com To see the changes in effect, take a look at this screencast that compares tailwindcss.com on iOS 15.5 with a version that has the patches of this PR applied: https://github.com/user-attachments/assets/1279d6f5-3c63-4f30-839c-198a789f4292 ## Test plan - Tested on tailwindcss.com via a preview build: https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/ - Updated tests - Ensure we also test on Chrome 111, Safari 16.4, Firefox 128 to make sure we have no regressions. Also tested on Safari 16.4, 15.5, 18.0
2025-04-01 13:33:22 +02:00
--color-blue-500: oklch(62.3% 0.214 259.815);
Watch CSS module files for changes (#17467) This PR is a follow-up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17433 In the other PR we allow scanning CSS files for extracting usages of CSS variables. This is important for `.module.css` files that reference these variables but aren't in the same big AST of the main CSS file. This PR also makes sure to watch for changes in those registered CSS files and re-extract the variables when they change. This PR took a bit longer than expected because I was trying to make sure that writing to `./dist/out.css` works without infinite-looping (e.g.: we had issues with this in Tailwind CSS v3 with webpack). But I couldn't reproduce the issue at all. I did had some code that tried to detect if the CSS file contained license headers and skip in (because then it's very likely an output CSS file) but even without it the tests were fine. I setup integration tests with `@tailwindcss/cli` itself, and with tools that use webpack. Added a test for Next.js, and a dedicated webpack test as well. Even without tests, locally, I couldn't reproduce an infinite loop due to changes in an output CSS file... Eventually dropped the code that tries to detect output CSS files. One thing to keep in mind is that if you change any of your "main" CSS files, then we will trigger a full rebuild anyway, so this change is only required for unrelated CSS files (like CSS module files) that use CSS variables. ## Test plan 1. Added integration tests for the CLI and Next.js 2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
}
.flex {
display: flex;
}
"
`)
await fs.write(
'src/unrelated.module.css',
css`
.module {
color: var(--color-blue-500);
background-color: var(--color-red-500);
}
`,
)
await process.onStdout((m) => m.includes('compiled successfully in'))
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
"
--- ./dist/main.css ---
:root, :host {
Improve compatibility with Safari 15 (#17435) This PR improves the compatibility with Tailwind CSS v4 with unsupported browsers with the goal to greatly improve compatibility with Safari 15. To make this work, this PR makes the following changes to all code - Change `oklab(…)` default theme values to use a percentage in the first place (so instead of `--color-red-500: oklch(0.637 0.237 25.331);` we now define it as `--color-red-500: oklch(63.7% 0.237 25.331);` since this syntax has much broader support on Safari). - Polyfill `@property` with a `@supports` query targeting older versions of Safari and Firefox * - Create fallbacks for the `color-mix(…)` function that use _inlined color values from your theme_ so that they can be computed a compile time by `lightningcss`. These fallbacks will convert to srgb to increase compatibility. - Create fallbacks for the _relative color_ feature used in the new shadow utilities and using `color-mix(…)` in case _relative color_ is applied on `currentcolor` (due to limited browser support) - Create fallbacks for gradient interpolation methods (e.g. to support `bg-linear-to-r/oklab`) - Polyfill `@media` queries range syntax. ## A simplified example Given this example CSS input: ```css @import 'tailwindcss'; @source inline('from-cyan-500/50 bg-linear-45'); ``` Here's the updated output CSS including the newly added polyfills and updated `oklab` values: ```css .bg-linear-45 { --tw-gradient-position: 45deg; background-image: linear-gradient(var(--tw-gradient-stops)); } @supports (background-image: linear-gradient(in lab, red, red)) { .bg-linear-45 { --tw-gradient-position: 45deg in oklab; } } .from-cyan-500\\/50 { --tw-gradient-from: oklab(71.5% -.11682 -.08247 / .5); --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); } @supports (color: color-mix(in lab, red, red)) { .from-cyan-500\\/50 { --tw-gradient-from: color-mix(in oklab, var(--color-cyan-500) 50%, transparent); } } :root, :host { --color-cyan-500: oklch(71.5% .143 215.221); } @supports (((-webkit-hyphens: none)) and (not (margin-trim: 1lh))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { @layer base { *, :before, :after, ::backdrop { --tw-gradient-position: initial; --tw-gradient-from: #0000; --tw-gradient-via: #0000; --tw-gradient-to: #0000; --tw-gradient-stops: initial; --tw-gradient-via-stops: initial; --tw-gradient-from-position: 0%; --tw-gradient-via-position: 50%; --tw-gradient-to-position: 100%; } } } @property --tw-gradient-position { syntax: "*"; inherits: false } @property --tw-gradient-from { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-via { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-to { syntax: "<color>"; inherits: false; initial-value: #0000; } @property --tw-gradient-stops { syntax: "*"; inherits: false } @property --tw-gradient-via-stops { syntax: "*"; inherits: false } @property --tw-gradient-from-position { syntax: "<length-percentage>"; inherits: false; initial-value: 0%; } @property --tw-gradient-via-position { syntax: "<length-percentage>"; inherits: false; initial-value: 50%; } @property --tw-gradient-to-position { syntax: "<length-percentage>"; inherits: false; initial-value: 100%; } ``` ## \* A note on `@property` polyfills and CSS modules On Next.js, CSS module files are required to be _pure_, meaning that all selectors must either be scoped to a class or an ID. Fortunatnyl for us, this does not apply to `@property` rules which we've been using before to initialize CSS variables. However, since we're now bringing back the `@property` polyfills, that would cause unexpected rules to be exported from the CSS file as this: ```css @reference "tailwindcss"; .skew { @apply skew-7; } ``` Would turn to the following file: ```css .skew { /* … */ } @supports (/*…*/) { @layer base { *, :before, :after, ::backdrop { --tw-gradient-position: initial; } } } @property /* … */ ``` Notice that this adds a `*` selector which is not considered pure. Unfortunately there is no way for us to silence this warning or work around it, as the dependency causing this errors ([`postcss-modules-local-by-default`](https://github.com/css-modules/postcss-modules-local-by-default)) is bundled into Next.js. To work around crashes, these polyfills will not apply to CSS modules processed by the PostCSS extension for now. ## Testing on tailwindcss.com To see the changes in effect, take a look at this screencast that compares tailwindcss.com on iOS 15.5 with a version that has the patches of this PR applied: https://github.com/user-attachments/assets/1279d6f5-3c63-4f30-839c-198a789f4292 ## Test plan - Tested on tailwindcss.com via a preview build: https://tailwindcss-com-git-legacy-browsers-tailwindlabs.vercel.app/ - Updated tests - Ensure we also test on Chrome 111, Safari 16.4, Firefox 128 to make sure we have no regressions. Also tested on Safari 16.4, 15.5, 18.0
2025-04-01 13:33:22 +02:00
--color-red-500: oklch(63.7% 0.237 25.331);
--color-blue-500: oklch(62.3% 0.214 259.815);
Watch CSS module files for changes (#17467) This PR is a follow-up PR for: https://github.com/tailwindlabs/tailwindcss/pull/17433 In the other PR we allow scanning CSS files for extracting usages of CSS variables. This is important for `.module.css` files that reference these variables but aren't in the same big AST of the main CSS file. This PR also makes sure to watch for changes in those registered CSS files and re-extract the variables when they change. This PR took a bit longer than expected because I was trying to make sure that writing to `./dist/out.css` works without infinite-looping (e.g.: we had issues with this in Tailwind CSS v3 with webpack). But I couldn't reproduce the issue at all. I did had some code that tried to detect if the CSS file contained license headers and skip in (because then it's very likely an output CSS file) but even without it the tests were fine. I setup integration tests with `@tailwindcss/cli` itself, and with tools that use webpack. Added a test for Next.js, and a dedicated webpack test as well. Even without tests, locally, I couldn't reproduce an infinite loop due to changes in an output CSS file... Eventually dropped the code that tries to detect output CSS files. One thing to keep in mind is that if you change any of your "main" CSS files, then we will trigger a full rebuild anyway, so this change is only required for unrelated CSS files (like CSS module files) that use CSS variables. ## Test plan 1. Added integration tests for the CLI and Next.js 2. Added new dedicated test for webpack
2025-03-31 18:44:06 +02:00
}
.flex {
display: flex;
}
"
`)
},
)