mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
39 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
56b22bb1d3
|
Add support for source maps (#17775)
Closes #13694 Closes #13591 # Source Maps Support for Tailwind CSS This PR adds support for source maps to Tailwind CSS v4 allowing us to track where styles come from whether that be user CSS, imported stylesheets, or generated utilities. This will improve debuggability in browser dev tools and gives us a good foundation for producing better error messages. I'll go over the details on how end users can enable source maps, any limitations in our implementation, changes to the internal `compile(…)` API, and some details and reasoning around the implementation we chose. ## Usage ### CLI Source maps can be enabled in the CLI by using the command line argument `--map` which will generate an inline source map comment at the bottom of your CSS. A separate file may be generated by passing a file name to `--map`: ```bash # Generates an inline source map npx tailwindcss -i input.css -o output.css --map # Generates a separate source map file npx tailwindcss -i input.css -o output.css --map output.css.map ``` ### PostCSS Source maps are supported when using Tailwind as a PostCSS plugin *in development mode only*. They may or may not be enabled by default depending on your build tool. If they are not you may be able to configure them within your PostCSS config: ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "@tailwindcss/postcss": {}, }, } } ``` ### Vite Source maps are supported when using the Tailwind CSS Vite plugin in *development mode only* by enabling the `css.devSourcemap` setting: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { devSourcemap: true, }, }) ``` Now when a CSS file is requested by the browser it'll have an inline source map comment that the browser can use. ## Limitations - Production build source maps are currently disabled due to a bug in Lightning CSS. See https://github.com/parcel-bundler/lightningcss/pull/971 for more details. - In Vite, minified CSS build source maps are not supported at all. See https://github.com/vitejs/vite/issues/2830 for more details. - In PostCSS, minified CSS source maps are not supported. This is due to the complexity required around re-associating every AST node with a location in the generated, optimized CSS. This complexity would also have a non-trivial performance impact. ## Testing Here's how to test the source map functionality in different environments: ### Testing the CLI 1. Setup typical project that the CLI can use and with sources to scan. ```css @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 2. Build with source maps: ```bash bun /path/to/tailwindcss/packages/@tailwindcss-cli/src/index.ts --input input.css -o output.css --map ``` 3. Open Chrome DevTools, inspect an element with utility classes, and you should see rules pointing to `input.css` or `node_modules/tailwindcss/index.css` ### Testing with Vite Testing in Vite will require building and installing necessary files under `dist/*.tgz`. 1. Create a Vite project and enable source maps in `vite.config.js`: ```js import tailwindcss from "@tailwindcss/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [tailwindcss()], css: { // This line is required for them to work devSourcemap: true, }, }) ``` 2. Add a component that uses Tailwind classes and custom CSS: ```jsx // ./src/app.jsx export default function App() { return ( <div className="bg-blue-500 my-custom-class"> Hello World </div> ) } ``` ```css /* ./src/styles.css */ @import "tailwindcss"; @utilty my-custom-utility { color: red; } /* to test `@apply` */ .card { @apply bg-white text-center shadow-md; } ``` 3. Run `npm run dev`, open DevTools, and inspect elements to verify source mapping works for both utility classes and custom CSS. ### Testing with PostCSS CLI 1. Create a test file and update your PostCSS config: ```css /* input.css */ @import "tailwindcss"; @layer components { .card { @apply p-6 rounded-lg shadow-lg; } } ``` ```jsonc // package.json { // … "postcss": { "map": { "inline": true }, "plugins": { "/path/to/tailwindcss/packages/packages/@tailwindcss-postcss/src/index.ts": {} } } } ``` 2. Run PostCSS through Bun: ```bash bunx --bun postcss ./src/index.css -o out.css ``` 3. Inspect the output CSS - it should include an inline source map comment at the bottom. ### Testing with PostCSS + Next.js Testing in Next.js will require building and installing necessary files under `dist/*.tgz`. However, I've not been able to get CSS source maps to work in Next.js without this hack: ```js const nextConfig: NextConfig = { // next.js overwrites config.devtool so we prevent it from doing so // please don't actually do this… webpack: (config) => Object.defineProperty(config, "devtool", { get: () => "inline-source-map", set: () => {}, }), }; ``` This is definitely not supported and also doesn't work with turbopack. This can be used to test them temporarily but I suspect that they just don't work there. ### Manual source map analysis You can analyze source maps using Evan Wallace's [Source Map Visualization](https://evanw.github.io/source-map-visualization/) tool which will help to verify the accuracy and quality of source maps. This is what I used extensively while developing this implementation. It'll help verify that custom, user CSS maps back to itself in the input, that generated utilities all map back to `@tailwind utilities;`, that source locations from imported files are also handled correctly, etc… It also highlights the ranges of stuff so it's easy to see if there are off-by-one errors. It's easiest to use inline source maps with this tool because you can take the CSS file and drop it on the page and it'll analyze it while showing the file content. If you're using Vite you'll want to access the CSS file with `?direct` at the end so you don't get a JS module back. ## Implementation The source map implementation follows the ECMA-426 specification and includes several key components to aid in that goal: ### Source Location Tracking Each emittable AST node in the compilation pipeline tracks two types of source locations: - `src`: Original source location - [source file, start offset, end offset] - `dst`: Generated source location - [output file, start offset, end offset] This dual tracking allows us to maintain mappings between the original source and generated output for things like user CSS, generated utilities, uses of `@apply`, and tracking theme variables. It is important to note that source locations for nodes _never overlap_ within a file which helps simplify source map generation. As such each type of node tracks a specific piece of itself rather than its entire "block": | Node | What a `SourceLocation` represents | | ----------- | ---------------------------------------------------------------- | | Style Rule | The selector | | At Rule | Rule name and params, includes the `@` | | Declaration | Property name and value, excludes the semicolon | | Comment | The entire comment, includes the start `/*` and end `*/` markers | ### Windows line endings when parsing CSS Because our AST tracks nodes through offsets we must ensure that any mutations to the file do *not* change the lenth of the string. We were previously replacing `\r\n` with `\n` (see [filter code points](https://drafts.csswg.org/css-syntax/#css-filter-code-points) from the spec) — which changes the length of the string and all offsets may end up incorrect. The CSS parser was updated to handle the CRLF token directly by skipping over the `\r` and letting remaining code handle `\n` as it did previously. Some additional tweaks were required when "peeking" the input but those changes were fairly small. ### Tracking of imports Source maps need paths to the actual imported stylesheets but the resolve step for stylesheets happens inside the call to `loadStylesheet` which make the file path unavailable to us. Because of this the `loadStylesheet` API was augmented such that it has to return a `path` property that we can then use to identify imported sources. I've also made the same change to the `loadModule` API for consistency but nothing currently uses this property. The `path` property likely makes `base` redundant but elminating that (if we even want to) is a future task. ### Optimizing the AST Our optimization pass may intoduce some nodes, for example, fallbacks we create for `@property`. These nodes are linked back to `@tailwind utilities` as ultimately that is what is responsible for creating them. ### Line Offset Tables A key component to our source map generation is the line offset table, which was inspired by some ESBuild internals. It stores a sorted list of offsets for the start of each line allowing us to translate offsets to line/column `Position`s in `O(log N)` time and from `Position`s to offsets in `O(1)` time. Creation of the table takes `O(N)` time. This means that we can store code point offsets for source locations and not have to worry about computing or tracking line/column numbers during parsing and serialization. Only when a source map is generated do these offsets need to be computed. This ensures the performance penalty when not using source maps is minimal. ### Source Map Generation The source map returned by `buildSourceMap()` is designed to follow the [ECMA-426 spec](https://tc39.es/ecma426). Because that spec is not completely finalized we consider the result of `buildSourceMap()` to be internal API that may change as the spec chamges. The produces source map is a "decoded" map such that all sources and mappings are in an object graph. A library like `source-map-js` must be used to convert this to an encoded source map of the right version where mappings are encoded with base 64 VLQs. Any specific integration (Vite, PostCSS, etc…) can then use `toSourceMap()` from `@tailwindcss/node` to convert from the internal source map to an spec-compliant encoded source map that can be understood by other tools. ### Handling minification in Lightning Since we use Lightning CSS for optimization, and it takes in an input map, we generate an encoded source map that we then pass to lightning. The output source map *from lighting itself* is then passed back in during the second optimization pass. The final map is then passed from lightning to the CLI (but not Vite or PostCSS — see the limitations section for details). In some cases we have to "fix up" the output CSS. When this happens we use `magic-string` to do the replacement in a way that is trackable and `@amppproject/remapping` to map that change back onto the original source map. Once the need for these fix ups disappear these dependencies can go away. Notes: - The accuracy of source maps run though lightning is reduced as it only tracks on a per-rule level. This is sufficient enough for browser dev tools so should be fine. - Source maps during optimization do not function properly at this time because of a bug in Lightning CSS regarding license comments. Once this bug is fixed they will start working as expected. ### How source locations flow through the system 1. During initial CSS parsing, source locations are preserved. 2. During parsing these source locations are also mapped to the destinations which supports an optimization for when no utilities are generated. 3. Throughout the compilation process, transformations maintain source location data 4. Generated utilities are explicitly pointed to `@tailwind utilities` unless generated by `@apply`. 5. When optimization is enabled, source maps are remapped through lightningcss 6. Final source maps are written in the requested format (inline or separate file) |
||
|
|
52000a30f0
|
PostCSS: Improve error recovery (#17754)
Closes #17295 This commit addresses an issue where the PostCSS plugin would get stuck in an error state when processing files with e.g. invalid @apply directives. This change prevents the PostCSS plugin from getting stuck in an error states particularly when the error happened inside an `@import`ed CSS files (as these were not registered as dependencies correctly before). ## Error overlays Some frameworks (e.g. Angular 19 or Next.js) handle errors inside PostCSS transforms to render a nice error overlay. This works well and gives immediate feedback that something went wrong. However, even when dependencies are registered before an error is thrown, these frameworks _will not consider changes to these dependencies anymore_ when an error occurs, as you can see in this Next.js example: https://github.com/user-attachments/assets/985c9dd7-daf8-4628-b4ad-6543ef220954 To avoid conditions where errors are not recoverable, this PR makes it so that these overlays will no longer show up in the app and only be logged to the output console. This will need follow-up upstream work before we can revisit this. ## Test plan - Tested with the repro in #17295. The error can now be recovered from. - Tested with a Next.js app where the issue in the screencast above is now no longer happening. - Added an integration test for errors in `@import`-ed files - Added a unit test for the changed `@apply` behavior. |
||
|
|
e085977844
|
PostCSS: Fix Turbopack 'one-revision-behind' bug (#17554)
Closes #17508 This PR fixes another issue we found that caused dev builds with Next.js and Turbopack to resolve the CSS file that was saved one revision before the latest update. When debugging this we noticed that the PostCSS entry is called twice for every one update when changing the input CSS file directly. That was caused by the input file itself being added as a _dependency_ so you would first get the callback that a _dependency_ has updated (at which point we look at the file system and figure out we need a full-rebuild because the input.css file has changed) and then another callback for when the _input file_ has updated. The problem with the second callback was that the file-system was already scanned for updates and since this includes the `mtimes` for the input file, we seemingly thought that the input file did not change. However, the issue is that the first callback actually came with an outdated PostCSS input AST... We found that this problem arises when you register the input CSS as a dependency of itself. This is not expected and we actually guard against this in the PostCSS client. However, we found that the input `from` argument is _a relative path when using Next.js with Turbopack_ so that check was not working as expected. ## Test plan Added the change to the repro from #17508 and it seems to work fine now. https://github.com/user-attachments/assets/2acb0078-f961-4498-be1a-b1c72d5ceda1 Also added a unit test to ensure we document that the input file path can be a relative path. Co-authored-by: Robin Malfait <malfait.robin@gmail.com> |
||
|
|
81a676f129
|
Fix race condition in Next.js with --turbopack (#17514)
This PR fixes an issue where if you use Next.js with `--turbopack` a race condition happens because the `@tailwindcss/postcss` plugin is called twice in rapid succession. The first call sees an update and does a partial update with the new classes. Next some internal `mtimes` are updated. The second call therefore doesn't see any changes anymore because the `mtimes` are the same, therefore it's serving its stale data. Fixes: #17508 ## Test plan - Tested with the repro provided in #17508 - Added a new unit test that calls into the PostCSS plugin directly for the same change from the same JavaScript run-loop. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> |
||
|
|
156afc6d67
|
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
|
||
|
|
53801091a0
|
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 |
||
|
|
1ef97759e3
|
Add @source not support (#17255)
This PR adds a new source detection feature: `@source not "…"`. It can
be used to exclude files specifically from your source configuration
without having to think about creating a rule that matches all but the
requested file:
```css
@import "tailwindcss";
@source not "../src/my-tailwind-js-plugin.js";
```
While working on this feature, we noticed that there are multiple places
with different heuristics we used to scan the file system. These are:
- Auto source detection (so the default configuration or an `@source
"./my-dir"`)
- Custom sources ( e.g. `@source "./**/*.bin"` — these contain file
extensions)
- The code to detect updates on the file system
Because of the different heuristics, we were able to construct failing
cases (e.g. when you create a new file into `my-dir` that would be
thrown out by auto-source detection, it'd would actually be scanned). We
were also leaving a lot of performance on the table as the file system
is traversed multiple times for certain problems.
To resolve these issues, we're now unifying all of these systems into
one `ignore` crate walker setup. We also implemented features like
auto-source-detection and the `not` flag as additional _gitignore_ rules
only, avoid the need for a lot of custom code needed to make decisions.
High level, this is what happens after the now:
- We collect all non-negative `@source` rules into a list of _roots_
(that is the source directory for this rule) and optional _globs_ (that
is the actual rules for files in this file). For custom sources (i.e
with a custom `glob`), we add an allowlist rule to the gitignore setup,
so that we can be sure these files are always included.
- For every negative `@source` rule, we create respective ignore rules.
- Furthermore we have a custom filter that ensures files are only read
if they have been changed since the last time they were read.
So, consider the following setup:
```css
/* packages/web/src/index.css */
@import "tailwindcss";
@source "../../lib/ui/**/*.bin";
@source not "../../lib/ui/expensive.bin";
```
This creates a git ignore file that (simplified) looks like this:
```gitignore
# Auto-source rules
*.{exe,node,bin,…}
*.{css,scss,sass,…}
{node_modules,git}/
# Custom sources can overwrite auto-source rules
!lib/ui/**/*.bin
# Negative rules
lib/ui/expensive.bin
```
We then use this information _on top of your existing `.gitignore`
setup_ to resolve files (i.e so if your `.gitignore` contains rules e.g.
`dist/` this line is going to be added _before_ any of the rules lined
out in the example above. This allows negative rules to allow-list your
`.gitignore` rules.
To implement this, we're rely on the `ignore` crate but we had to make
various changes, very specific, to it so we decided to fork the crate.
All changes are prefixed with a `// CHANGED:` block but here are the
most-important ones:
- We added a way to add custom ignore rules that _extend_ (rather than
overwrite) your existing `.gitignore` rules
- We updated the order in which files are resolved and made it so that
more-specific files can allow-list more generic ignore rules.
- We resolved various issues related to adding more than one base path
to the traversal and ensured it works consistent for Linux, macOS, and
Windows.
## Behavioral changes
1. Any custom glob defined via `@source` now wins over your `.gitignore`
file and the auto-content rules.
- Resolves #16920
3. The `node_modules` and `.git` folders as well as the `.gitignore`
file are now ignored by default (but can be overridden by an explicit
`@source` rule).
- Resolves #17318
- Resolves #15882
4. Source paths into ignored-by-default folders (like `node_modules`)
now also win over your `.gitignore` configuration and auto-content
rules.
- Resolves #16669
5. Introduced `@source not "…"` to negate any previous rules.
- Resolves #17058
6. Negative `content` rules in your legacy JavaScript configuration
(e.g. `content: ['!./src']`) now work with v4.
- Resolves #15943
7. The order of `@source` definitions matter now, because you can
technically include or negate previous rules. This is similar to your
`.gitingore` file.
9. Rebuilds in watch mode now take the `@source` configuration into
account
- Resolves #15684
## Combining with other features
Note that the `not` flag is also already compatible with [`@source
inline(…)`](https://github.com/tailwindlabs/tailwindcss/pull/17147)
added in an earlier commit:
```css
@import "tailwindcss";
@source not inline("container");
```
## Test plan
- We added a bunch of oxide unit tests to ensure that the right files
are scanned
- We updated the existing integration tests with new `@source not "…"`
specific examples and updated the existing tests to match the subtle
behavior changes
- We also added a new special tag `[ci-all]` that, when added to the
description of a PR, causes the PR to run unit and integration tests on
all operating systems.
[ci-all]
---------
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
|
||
|
|
225f3233b6
|
Enable URL rewriting for PostCSS (#16965)
Fixes #16636 This PR enables URL rebasing for PostCSS. Furthermore it fixes an issue where transitive imports rebased against the importer CSS file instead of the input CSS file. While fixing this we noticed that this is also broken in Vite right now and that our integration test swallowed that when testing because it did not import any Tailwind CSS code and thus was not considered a Tailwind file. ## Test plan - Added regression integration tests - Also validated it against the repro of https://github.com/tailwindlabs/tailwindcss/issues/16962: <img width="1149" alt="Screenshot 2025-03-05 at 16 41 01" src="https://github.com/user-attachments/assets/85396659-d3d0-48c0-b1c7-6125ff8e73ac" /> --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
0d5e2be312
|
Ensure we process Tailwind CSS features when using @reference (#16057)
This PR fixes an issue where if you only used `@reference` that we didn't process Tailwind CSS features. We have a 'quick bail check', in the PostCSS plugin to quickly bail if we _konw_ that we don't need to handle any Tailwind CSS features. This is useful in Next.js applications where every single CSS file will be passed to the PostCSS plugin. If you use custom font ins Next.js, each of those fonts will have a CSS file as well. Before we introduced `@reference`, we used `@import "tailwindcss" reference`, which passed the bail check because `@import` was being used. Now we have `@reference` which wasn't included in the list. This is now solved. Fixes: #16056 ### Test plan Added a failing test that is now failing after the fix. |
||
|
|
a11c80d6c6
|
Upgrade lightningcss to 1.29.0 (#15576)
Closes #15438
Closes #15560
Closes #15561
Closes #15562
This PR upgrades `lightningcss` to `1.29.0` and uses the [new feature
flag](
|
||
|
|
04dcf27de5
|
Change Chrome target to 111 (#15389)
Resolves https://github.com/tailwindlabs/tailwindcss/discussions/15387 This PR changes the Chrome target to 111. We initially picked 120 because of the unnecessary `:dir()` down-leveling but we that was maybe a bit too recent as it was causing some necessary prefixes to not be generated (e.g. `-webkit-background-clip`). This PR changes it to 111 which we require for the `color-mix()` function. To work around the `:dir()` down-leveling we also disable the `DirSelector` lightningcss feature which is used to control this behavior: https://sourcegraph.com/github.com/parcel-bundler/lightningcss/-/blob/src/selector.rs?L1964-1965 |
||
|
|
bcf70990a7
|
Improve debug logs (#15303)
This PR improves the debug logs for the `@tailwindcss/postcss` integration. It uses custom instrumentation to provide a nested but detailed overview of where time is spent during the build process. The updated logs look like this: ``` [0.15ms] [@tailwindcss/postcss] src/app/geistsans_9fc57718.module.css [0.14ms] ↳ Quick bail check [0.02ms] [@tailwindcss/postcss] src/app/geistsans_9fc57718.module.css [0.01ms] ↳ Quick bail check [0.03ms] [@tailwindcss/postcss] src/app/geistmono_b9f59162.module.css [0.02ms] ↳ Quick bail check [0.12ms] [@tailwindcss/postcss] src/app/geistmono_b9f59162.module.css [0.11ms] ↳ Quick bail check [42.09ms] [@tailwindcss/postcss] src/app/globals.css [ 0.01ms] ↳ Quick bail check [12.12ms] ↳ Setup compiler [ 0.11ms] ↳ PostCSS AST -> Tailwind CSS AST [11.99ms] ↳ Create compiler [ 0.07ms] ↳ Register full rebuild paths [ 0.06ms] ↳ Setup scanner [ 7.51ms] ↳ Scan for candidates [ 5.86ms] ↳ Register dependency messages [ 5.88ms] ↳ Build utilities [ 8.34ms] ↳ Optimization [ 0.23ms] ↳ AST -> CSS [ 4.20ms] ↳ Lightning CSS [ 3.89ms] ↳ CSS -> PostCSS AST [ 1.97ms] ↳ Update PostCSS AST ``` |
||
|
|
d444362d5e
|
Skip creating a compiler for CSS files that should not be processed (#15340)
This PR skips creating a compiler in the `@tailwindcss/postcss` implementation if we know that the CSS file we are handling is definitely not a Tailwind CSS file. This is a performance improvement for initial builds where some CSS files would've been handling by Tailwind CSS but shouldn't. E.g.: When setting up custom fonts in Next.js applications, each font will have it's own CSS file that is passed to `@tailwindcss/postcss`. Since they don't contain `@import` or any other Tailwind CSS directives, we can just skip them. |
||
|
|
94a3cff687
|
Fix PostCSS watcher warnings on Windows (#15321)
Resolves #15320 Resolves #15175 Turns out that the postcss file watcher does not like our Unix based paths and will print a warning about them. This fixes the issue by calling `path.resolve()` to convert it back to a Windows-style absolute path if necessary. ## Test Plan Tested on Windows with a new Next.js 14 project. Ensured that file reloads also still work (changes to the `tsx` file are picked up correctly). Also ensure that the CI runs on Windows. ### Before <img width="1178" alt="Screenshot 2024-12-06 at 13 12 23" src="https://github.com/user-attachments/assets/70c1fe45-6983-4fb4-9889-716a0cbef03a"> ### After <img width="1196" alt="Screenshot 2024-12-06 at 13 23 24" src="https://github.com/user-attachments/assets/0b9e3ff7-c5b6-4ccb-85a9-e7ba7aee355a"> |
||
|
|
408fa99849
|
Use AST transformations in @tailwindcss/postcss (#15297)
This PR improves the `@tailwindcss/postcss` integration by using direct AST transformations between our own AST and PostCSS's AST. This allows us to skip a step where we convert our AST into a string, then parse it back into a PostCSS AST. The only downside is that we still have to print the AST into a string if we want to optimize the CSS using Lightning CSS. Luckily this only happens in production (`NODE_ENV=production`). This also introduces a new private `compileAst` API, that allows us to accept an AST as the input. This allows us to skip the PostCSS AST -> string -> parse into our own AST step. To summarize: Instead of: - Input: `PostCSS AST` -> `.toString()` -> `CSS.parse(…)` -> `Tailwind CSS AST` - Output: `Tailwind CSS AST` -> `toCSS(ast)` -> `postcss.parse(…)` -> `PostCSS AST` We will now do this instead: - Input: `PostCSS AST` -> `transform(…)` -> `Tailwind CSS AST` - Output: `Tailwind CSS AST` -> `transform(…)` -> `PostCSS AST` --- Running this on Catalyst, the time spent in the `@tailwindcss/postcss` looks like this: - Before: median time per run: 19.407687 ms - After: median time per run: 11.8796455 ms This is tested on Catalyst which roughly generates ~208kb worth of CSS in dev mode. While it's not a lot, skipping the stringification and parsing seems to improve this step by ~40%. Note: these times exclude scanning the actual candidates and only time the work needed for parsing/stringifying the CSS from and into ASTs. The actual numbers are a bit higher because of the Oxide scanner reading files from disk. But since that part is going to be there no matter what, it's not fair to include it in this benchmark. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
545401469d
|
Postcss: Run plugin in Once hook (#15273)
Closes #15138 This PR changes the postcss client to run in the `Once` hook instead of `OnceExit`. This makes sure the postcss order in v4 matches that of v3. Conceptually this also makes more sense, since we expect tailwindcss to be run as one of the first plugins in the pipeline (where `OnceExit` would run it almost at the end). To make sure it's still possible to use `postcss-import` before and have it resolve to the right paths, we also needed to change the `postcss-fix-relative-paths` plugin to run in the `Once` order (`postcss-import` also uses `Once` order so the order). ## Test Plan This issue had many ways in which it can manifest. I added a unit test to ensure the plugin order works but here's a concrete example when using the postcss plugin in Vite. ### Before Image `url()`s were not properly handled since the postcss plugin to transform these was run before Tailwind CSS could generate the class for it: <img width="2532" alt="Screenshot 2024-12-02 at 14 55 42" src="https://github.com/user-attachments/assets/2f23b409-1576-441d-9ffe-6f24ad6e7436"> ### After <img width="2529" alt="Screenshot 2024-12-02 at 14 53 52" src="https://github.com/user-attachments/assets/b754c3d8-1af1-4aeb-87da-0bfc3ffecdb7"> --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
99b73ee368
|
Improve performance of @tailwindcss/postcss and @tailwindcss/vite (#15226)
This PR improves the performance of the `@tailwindcss/postcss` and `@tailwindcss/vite` implementations. The issue is that in some scenarios, if you have multiple `.css` files, then all of the CSS files are ran through the Tailwind CSS compiler. The issue with this is that in a lot of cases, the CSS files aren't even related to Tailwind CSS at all. E.g.: in a Next.js project, if you use the `next/font/local` tool, then every font you used will be in a separate CSS file. This means that we run Tailwind CSS in all these files as well. That said, running Tailwind CSS on these files isn't the end of the world because we still need to handle `@import` in case `@tailwind utilities` is being used. However, we also run the auto source detection logic for every CSS file in the system. This part is bad. To solve this, this PR introduces an internal `features` to collect what CSS features are used throughout the system (`@import`, `@plugin`, `@apply`, `@tailwind utilities`, etc…) The `@tailwindcss/postcss` and `@tailwindcss/vite` plugin can use that information to decide if they can take some shortcuts or not. --- Overall, this means that we don't run the slow parts of Tailwind CSS if we don't need to. --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
cb518f4623
|
Set browser targets for iOS Safari, Firefox, and Chrome (#15166)
Closes #15160 We need to set browser targets for each browser individually to see vendor prefixes created for each browser. Exact values are up for discussion, this first pass is taken from @adamwathan's comments in https://github.com/tailwindlabs/tailwindcss/issues/15160 --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
ae249c7e56
|
Fix Next.js endless loop when setting a custom distDir (#15053)
Closes #15050 In Tailwind CSS v4 Alpha 31 we changed how we scan template files. This changes included a new folder-dependency that is emitted for the `base` directory, so we can listen for new files being added as part of the postcss dependency. In our testing, this worked fine with the Next.js integration meaning a new file in the project root would be picked up by Oxide and we could update the CSS files accordingly. This change is now, however, causing an issue. With Next.js 15 **and with a custom `distDir` configured**, the postcss build, that will write into the `distDir`, will cause another postcss run to be triggered, starting an endless loop (regardless of wether or not the `distDir` was also part of your gitignore list). This PR now changes the postcss client to not emit the base directory as a dependency to revert this changes. This does mean that new files and folders created _directly in the project root_ will require a restart of the Next.js server again (just like it did in Alpha 31 and before) for now. ## Test Plan Next 15 does not seem to run in our current integration test setup (for some reason the server does not close correctly and it will fail on the cleanup step), so this change was tested manually: - First, clone the [templates repo](https://github.com/philipp-spiess/tailwindcss-playgrounds) I use for third party frameworks - Then, do a full build in the parent repo `tailwindcss` via `pnpm build` - Now, install the local tarballs in the `tailwindcss-playgrounds` repo via `pnpm install` With this setup I have tested changes to a template file (that causes new utilities to be added) and the CSS file (that will rebuild properly) across both `pnpm dev` and `pnpm dev --turbo`. Furthermore integration tests assert it still works in Next 14 like it did before: https://github.com/user-attachments/assets/b0ccb3dd-d090-4e4c-97c5-74129a2789be One thing to make sure of is to include the new `distDir` into the `.gitignore` file as well, otherwise we will scrape it for changes which inherently causes an endless loop issue again. --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
c5b6df2a27
|
Optimize generated CSS output (#14873)
This PR improves the generated CSS by running it through Lightning CSS twice.Right now Lightning CSS merges adjacent at-rules and at the end flattens the nesting. This means that after the nesting is flattened, the at-rules that are adjacent and could be merged together will not be merged. This PR improves our output by running Lightning CSS twice on the generated CSS which will make sure to merge adjacent at-rules after the nesting is flattened. Note: in the diff output you'll notice that some properties are duplicated. These need some fixes in Lightning CSS itself but they don't break anything for us right now. Related PR in Lightning CSS for the double `-webkit-backdrop-filter` can be found here: https://github.com/parcel-bundler/lightningcss/pull/850 --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> |
||
|
|
894bf9f5ef
|
Support migrating projects with multiple config files (#14863)
When migrating a project from Tailwind CSS v3 to Tailwind CSS v4, then we started the migration process in the following order: 1. Migrate the JS/TS config file 2. Migrate the source files (found via the `content` option) 3. Migrate the CSS files However, if you have a setup where you have multiple CSS root files (e.g.: `frontend` and `admin` are separated), then that typically means that you have an `@config` directive in your CSS files. These point to the Tailwind CSS config file. This PR changes the migration order to do the following: 1. Build a tree of all the CSS files 2. For each `@config` directive, migrate the JS/TS config file 3. For each JS/TS config file, migrate the source files If a CSS file does not contain any `@config` directives, then we start by filling in the `@config` directive with the default Tailwind CSS config file (if found, or the one passed in). If no default config file or passed in config file can be found, then we will error out (just like we do now) --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
d68a780f98
|
Auto source detection improvements (#14820)
This PR introduces a new `source(…)` argument and improves on the
existing `@source`. The goal of this PR is to make the automatic source
detection configurable, let's dig in.
By default, we will perform automatic source detection starting at the
current working directory. Auto source detection will find plain text
files (no binaries, images, ...) and will ignore git-ignored files.
If you want to start from a different directory, you can use the new
`source(…)` next to the `@import "tailwindcss/utilities"
layer(utilities) source(…)`.
E.g.:
```css
/* ./src/styles/index.css */
@import 'tailwindcss/utilities' layer(utilities) source('../../');
```
Most people won't split their source files, and will just use the simple
`@import "tailwindcss";`, because of this reason, you can use
`source(…)` on the import as well:
E.g.:
```css
/* ./src/styles/index.css */
@import 'tailwindcss' source('../../');
```
Sometimes, you want to rely on auto source detection, but also want to
look in another directory for source files. In this case, yuo can use
the `@source` directive:
```css
/* ./src/index.css */
@import 'tailwindcss';
/* Look for `blade.php` files in `../resources/views` */
@source '../resources/views/**/*.blade.php';
```
However, you don't need to specify the extension, instead you can just
point the directory and all the same automatic source detection rules
will apply.
```css
/* ./src/index.css */
@import 'tailwindcss';
@source '../resources/views';
```
If, for whatever reason, you want to disable the default source
detection feature entirely, and only want to rely on very specific glob
patterns you define, then you can disable it via `source(none)`.
```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);
/* Only look at .blade.php files, nothing else */
@source "../resources/views/**/*.blade.php";
```
Note: even with `source(none)`, if your `@source` points to a directory,
then auto source detection will still be performed in that directory. If
you don't want that, then you can simply add explicit files in the globs
as seen in the previous example.
```css
/* Completely disable the default auto source detection */
@import 'tailwindcss' source(none);
/* Run auto source detection in `../resources/views` */
@source "../resources/views";
```
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
|
||
|
|
35b84cc313
|
Improve @tailwindcss/postcss performance for initial builds (#14565)
This PR improves the performance of the `@tailwindcss/postcss` plugin. Before this change we created 2 compiler instances instead of a single one. On a project where a `tailwindcss.config.ts` file is used, this means that the timings look like this: ``` [@tailwindcss/postcss] Setup compiler: 137.525ms ⋮ [@tailwindcss/postcss] Setup compiler: 43.95ms ``` This means that with this small change, we can easily shave of ~50ms for initial PostCSS builds. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
ab82efab7d
|
Expose timing information in debug mode (#14553)
This PR exposes when using the the `DEBUG` environment variable. This follows the `DEBUG` conventions where: - `DEBUG=1` - `DEBUG=true` - `DEBUG=*` - `DEBUG=tailwindcss` Will enable the debug information, but when using: - `DEBUG=0` - `DEBUG=false` - `DEBUG=-tailwindcss` It will not. This currently only exposes some timings related to: 1. Scanning for candidates 2. Building the CSS 3. Optimizing the CSS We can implement a more advanced version of this where we also expose more fine grained information such as the files we scanned, the amount of candidates we found and so on. But I believe that this will be enough to start triaging performance related issues. |
||
|
|
79794744a9
|
Resolve @import in core (#14446)
This PR brings `@import` resolution into Tailwind CSS core. This means that our clients (PostCSS, Vite, and CLI) no longer need to depend on `postcss` and `postcss-import` to resolve `@import`. Furthermore this simplifies the handling of relative paths for `@source`, `@plugin`, or `@config` in transitive CSS files (where the relative root should always be relative to the CSS file that contains the directive). This PR also fixes a plugin resolution bug where non-relative imports (e.g. directly importing node modules like `@plugin '@tailwindcss/typography';`) would not work in CSS files that are based in a different npm package. ### Resolving `@import` The core of the `@import` resolution is inside `packages/tailwindcss/src/at-import.ts`. There, to keep things performant, we do a two-step process to resolve imports. Imagine the following input CSS file: ```css @import "tailwindcss/theme.css"; @import "tailwindcss/utilities.css"; ``` Since our AST walks are synchronous, we will do a first traversal where we start a loading request for each `@import` directive. Once all loads are started, we will await the promise and do a second walk where we actually replace the AST nodes with their resolved stylesheets. All of this is recursive, so that `@import`-ed files can again `@import` other files. The core `@import` resolver also includes extensive test cases for [various combinations of media query and supports conditionals as well als layered imports](https://developer.mozilla.org/en-US/docs/Web/CSS/@import). When the same file is imported multiple times, the AST nodes are duplicated but duplicate I/O is avoided on a per-file basis, so this will only load one file, but include the `@theme` rules twice: ```css @import "tailwindcss/theme.css"; @import "tailwindcss/theme.css"; ``` ### Adding a new `context` node to the AST One limitation we had when working with the `postcss-import` plugin was the need to do an additional traversal to rewrite relative `@source`, `@plugin`, and `@config` directives. This was needed because we want these paths to be relative to the CSS file that defines the directive but when flattening a CSS file, this information is no longer part of the stringifed CSS representation. We worked around this by rewriting the content of these directives to be relative to the input CSS file, which resulted in added complexity and caused a lot of issues with Windows paths in the beginning. Now that we are doing the `@import` resolution in core, we can use a different data structure to persist this information. This PR adds a new `context` node so that we can store arbitrary context like this inside the Ast directly. This allows us to share information with the sub tree _while doing the Ast walk_. Here's an example of how the new `context` node can be used to share information with subtrees: ```ts const ast = [ rule('.foo', [decl('color', 'red')]), context({ value: 'a' }, [ rule('.bar', [ decl('color', 'blue'), context({ value: 'b' }, [ rule('.baz', [decl('color', 'green')]), ]), ]), ]), ] walk(ast, (node, { context }) => { if (node.kind !== 'declaration') return switch (node.value) { case 'red': assert(context.value === undefined) case 'blue': assert(context.value === 'a') case 'green': assert(context.value === 'b') } }) ``` In core, we use this new Ast node specifically to persist the `base` path of the current CSS file. We put the input CSS file `base` at the root of the Ast and then overwrite the `base` on every `@import` substitution. ### Removing the dependency on `postcss-import` Now that we support `@import` resolution in core, our clients no longer need a dependency on `postcss-import`. Furthermore, most dependencies also don't need to know about `postcss` at all anymore (except the PostCSS client, of course!). This also means that our workaround for rewriting `@source`, the `postcss-fix-relative-paths` plugin, can now go away as a shared dependency between all of our clients. Note that we still have it for the PostCSS plugin only, where it's possible that users already have `postcss-import` running _before_ the `@tailwindcss/postcss` plugin. Here's an example of the changes to the dependencies for our Vite client ✨ : <img width="854" alt="Screenshot 2024-09-19 at 16 59 45" src="https://github.com/user-attachments/assets/ae1f9d5f-d93a-4de9-9244-61af3aff1237"> ### Performance Since our Vite and CLI clients now no longer need to use `postcss` at all, we have also measured a significant improvement to the initial build times. For a small test setup that contains only a hand full of files (nothing super-complex), we measured an improvement in the **3.5x** range: <img width="1334" alt="Screenshot 2024-09-19 at 14 52 49" src="https://github.com/user-attachments/assets/06071fb0-7f2a-4de6-8ec8-f202d2cc78e5"> The code for this is in the commit history if you want to reproduce the results. The test was based on the Vite client. ### Caveats One thing to note is that we previously relied on finding specific symbols in the input CSS to _bail out of Tailwind processing completely_. E.g. if a file does not contain a `@tailwind` or `@apply` directive, it can never be a Tailwind file. Since we no longer have a string representation of the flattened CSS file, we can no longer do this check. However, the current implementation was already inconsistent with differences on the allowed symbol list between our clients. Ideally, Tailwind CSS should figure out wether a CSS file is a Tailwind CSS file. This, however, is left as an improvement for a future API since it goes hand-in-hand with our planned API changes for the core `tailwindcss` package. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
a1d56d8e24
|
Ensure content globs defined in @config files are relative to that file (#14314)
When you configure custom content globs inside an `@config` file, we want to tread these globs as being relative to that config file and not the CSS file that requires the content file. A config can be used by multiple CSS configs. --------- Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
52012d90d7
|
Support loading config files via @config (#14239)
In Tailwind v4 the CSS file is the main entry point to your project and is generally configured via `@theme`. However, given that all v3 projects were configured via a `tailwind.config.js` file we definitely need to support those. This PR adds support for loading existing Tailwind config files by adding an `@config` directive to the CSS — similar to how v3 supported multiple config files except that this is now _required_ to use a config file. You can load a config file like so: ``` @import "tailwindcss"; @config "./path/to/tailwind.config.js"; ``` A few notes: - Both CommonJS and ESM config files are supported (loaded directly via `import()` in Node) - This is not yet supported in Intellisense or Prettier — should hopefully land next week - TypeScript is **not yet** supported in the config file — this will be handled in a future PR. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Adam Wathan <adam.wathan@gmail.com> Co-authored-by: Robin Malfait <malfait.robin@gmail.com> |
||
|
|
ab8972749c
|
Postcss: Bring back proper type exports (#14256)
Closes #14253 Since we changed the export strategy for the postcss client in #14132, we accidentally no longer generated type exports for this package. This PR adds a type export back. We now use a similar pattern to the `./colors` and `./defaultTheme` exports in the tailwindcss package where we have a separate cjs entrypoint. The changes were validated manually in a playground project that were installing the updated dependencies from tarballs. Here is one example of it working as expected: <img width="750" alt="Screenshot 2024-08-26 at 14 10 07" src="https://github.com/user-attachments/assets/83de15f2-1543-4805-9231-9b8df1636c5e"> |
||
|
|
a902128640
|
Improve Oxide scanner API (#14187)
This PR updates the API for interacting with the Oxide API. Until now,
we used the name `scanDir(…)` which is fine, but we do way more work
right now.
We now have features such as:
1. Auto source detection (can be turned off, e.g.: `@tailwindcss/vite`
doesn't need it)
2. Scan based on `@source`s found in CSS files
3. Do "incremental" rebuilds (which means that the `scanDir(…)` result
was stateful).
To solve these issues, this PR introduces a new `Scanner` class where
you can pass in the `detectSources` and `sources` options. E.g.:
```ts
let scanner = new Scanner({
// Optional, omitting `detectSources` field disables automatic source detection
detectSources: { base: __dirname },
// List of glob entries to scan. These come from `@source` directives in CSS.
sources: [
{ base: __dirname, pattern: "src/**/*.css" },
// …
],
});
```
The scanner object has the following API:
```ts
export interface ChangedContent {
/** File path to the changed file */
file?: string
/** Contents of the changed file */
content?: string
/** File extension */
extension: string
}
export interface DetectSources {
/** Base path to start scanning from */
base: string
}
export interface GlobEntry {
/** Base path of the glob */
base: string
/** Glob pattern */
pattern: string
}
export interface ScannerOptions {
/** Automatically detect sources in the base path */
detectSources?: DetectSources
/** Glob sources */
sources?: Array<GlobEntry>
}
export declare class Scanner {
constructor(opts: ScannerOptions)
scan(): Array<string>
scanFiles(input: Array<ChangedContent>): Array<string>
get files(): Array<string>
get globs(): Array<GlobEntry>
}
```
The `scanFiles(…)` method is used for incremental rebuilds. It takes the
`ChangedContent` array for all the new/changes files. It returns whether
we scanned any new candidates or not.
Note that the `scanner` object is stateful, this means that we don't
have to track candidates in a `Set` anymore. We can just call
`getCandidates()` when we need it.
This PR also removed some unused code that we had in the `scanDir(…)`
function to allow for sequential or parallel `IO`, and sequential or
parallel `Parsing`. We only used the same `IO` and `Parsing` strategies
for all files, so I just got rid of it.
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
|
||
|
|
1fdf67989f | Remove errant console log calls | ||
|
|
921b4b673b
|
Use import to load plugins (#14132)
Alternative to #14110 This PR changes the way how we load plugins to be compatible with ES6 async `import`s. This allows us to load plugins even inside the browser but it comes at a downside: We now have to change the `compile` API to return a `Promise`... So most of this PR is rewriting all of the call sites of `compile` to expect a promise instead of the object. --------- Co-authored-by: Jordan Pittman <jordan@cryptica.me> |
||
|
|
541d84a3bb
|
Add @source support (#14078)
This PR is an umbrella PR where we will add support for the new `@source` directive. This will allow you to add explicit content glob patterns if you want to look for Tailwind classes in other files that are not automatically detected yet. Right now this is an addition to the existing auto content detection that is automatically enabled in the `@tailwindcss/postcss` and `@tailwindcss/cli` packages. The `@tailwindcss/vite` package doesn't use the auto content detection, but uses the module graph instead. From an API perspective there is not a lot going on. There are only a few things that you have to know when using the `@source` directive, and you probably already know the rules: 1. You can use multiple `@source` directives if you want. 2. The `@source` accepts a glob pattern so that you can match multiple files at once 3. The pattern is relative to the current file you are in 4. The pattern includes all files it is matching, even git ignored files 1. The motivation for this is so that you can explicitly point to a `node_modules` folder if you want to look at `node_modules` for whatever reason. 6. Right now we don't support negative globs (starting with a `!`) yet, that will be available in the near future. Usage example: ```css /* ./src/input.css */ @import "tailwindcss"; @source "../laravel/resources/views/**/*.blade.php"; @source "../../packages/monorepo-package/**/*.js"; ``` It looks like the PR introduced a lot of changes, but this is a side effect of all the other plumbing work we had to do to make this work. For example: 1. We added dedicated integration tests that run on Linux and Windows in CI (just to make sure that all the `path` logic is correct) 2. We Have to make sure that the glob patterns are always correct even if you are using `@import` in your CSS and use `@source` in an imported file. This is because we receive the flattened CSS contents where all `@import`s are inlined. 3. We have to make sure that we also listen for changes in the files that match any of these patterns and trigger a rebuild. PRs: - [x] https://github.com/tailwindlabs/tailwindcss/pull/14063 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14085 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14079 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14067 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14076 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14080 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14127 - [x] https://github.com/tailwindlabs/tailwindcss/pull/14135 Once all the PRs are merged, then this umbrella PR can be merged. > [!IMPORTANT] > Make sure to merge this without rebasing such that each individual PR ends up on the main branch. --------- Co-authored-by: Philipp Spiess <hello@philippspiess.com> Co-authored-by: Jordan Pittman <jordan@cryptica.me> Co-authored-by: Adam Wathan <adam.wathan@gmail.com> |
||
|
|
54474086c8
|
Add support for basic addVariant plugins with new @plugin directive (#13982)
* Add basic `addVariant` plugin support * Return early * Load plugins right away instead later * Use correct type for variant name * Preliminary support for addVariant plugins in PostCSS plugin * Add test for compounding plugin variants * Add basic `loadPlugin` support to Vite plugin * Add basic `loadPlugin` support to CLI * add `io.ts` for integrations * use shared `loadPlugin` from `tailwindcss/io` * add `tailwindcss-test-utils` to `@tailwindcss/cli` and `@tailwindcss/vite` * only add `tailwindcss-test-utils` to `tailwindcss` as a dev dependency Because `src/io.ts` is requiring the plugin. * move `tailwindcss-test-utils` to `@tailwindcss/postcss ` This is the spot where we actually need it. * use newer pnpm version * Duplicate loadPlugin implementation instead of exporting io file * Remove another io reference * update changelog --------- Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com> Co-authored-by: Robin Malfait <malfait.robin@gmail.com> |
||
|
|
0ee3508179
|
Move optimizeCss to the packages where it's used (#13230)
* add `@tailwindcss/optimize` as a separate package * remove lightningcss from `tailwindcss` * import `optimizeCss` from `@tailwindcss/optimize` * ensure we use `src/` files in development * move `devDependencies` after `dependencies` Just for consistency * inline `optimizeCss` in leaf packages Instead of introducing a custom `@tailwindcss/optimize` package * update changelog * fix changelog |
||
|
|
172bc5d822
|
Add incremental rebuilds to @tailwindcss/postcss (#13170)
* rename PostCSS plugin name from `tailwindcss-v4` to `@tailwindcss/postcss` * add incremental rebuilds to `@tailwindcss/postcss` * improve comment * update changelog * Simplify nested conditionals * Simplify more conditionals * Refactor file modification tracking --------- Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com> |
||
|
|
d230f2e13b
|
Improve incremental builds (#13168)
* ensure we don't crash on deleted files * change return type of `compile` to include a `rebuild()` function This will allow us in the future to perform incremental rebuilds after the initial rebuild. This is purely the API change so that we can prepare all the call sites to use this new API. * set `@tailwind utilities` nodes Instead of replacing the node that represents the `@tailwind utilities` with the generated AST nodes from the rawCandidates, we will set the nodes of the `@tailwind utilities` rule to the AST nodes instead. This way we dont' have to remove and replace the `@tailwind utilities` rule with `n` new nodes. This will later allow us to track the `@tailwindcss utilities` rule itself and update its `nodes` for incremental rebuilds. This also requires a small change to the printer where we now need to print the children of the `@tailwind utilities` rule. Note: we keep the same `depth` as-if the `@tailwindcss utilities` rule was not there. Otherwise additional indentation would be present. * move sorting to the `ast.sort()` call This will allow us to keep sorting AST nodes in a single spot. * move parser functions to the `DesignSystem` This allows us to put all the parsers in the `DesignSystem`, this allows us to scope the parsers to the current design system (the current theme, current utility values and variants). The implementation of these parsers are also using a `DefaultMap` implementation. This allows us to make use of caching and only parse a candidate, parse a variant or compile AST nodes for a given raw candidate once if we've already done this work in the past. Again, this is scoped to the `DesignSystem` itself. This means that if the corresponding theme changes, then we will create a new `DesignSystem` entirely and the caches will be garbage collected. This is important because a candidate like `bg-primary` can be invalid in `DesignSystem` A, but can be valid in `DesignSystem` B and vice versa. * ensure we sort variants alphabetically by root For incremental rebuilds we don't know all the used variants upfront, which means that we can't sort them upfront either (what we used to do). This code now allows us to sort the variants deterministically when sorting the variants themselves instead of relying on the fact that they used to be sorted before. The sort itself could change slightly compared to the previous implementation (especially when you used stacked variants in your candidates), but it will still be deterministic. * replace `localeCompare` comparisons Use cheaper comparisons than `localeCompare` when comparing 2 strings. We currently don't care if it is 100% correctly sorted, but we just want consistent sorting. This is currently faster compared to `localeCompare`. Another benefit is that `localeCompare` could result in non-deterministic results if the CSS was generated on 2 separate computers where the `locale` is different. We could solve that by adding a dedicated locale, but it would still be slower compared to this. * track invalid candidates When an incoming raw candidates doesn't produce any output, then we can mark it as an invalid candidate. This will allow us to reduce the amount of candidates to handle in incremental rebuilds. * add initial incremental rebuild implementation This includes a number of steps: 1. Track the `@tailwind utilities` rule, so that we can adjust its nodes later without re-parsing the full incoming CSS. 2. Add the new incoming raw candidates to the existing set of candidates. 3. Parse the merged set to `compileCandidates` (this can accept any `Iterable<string>`, which means `string[]`, `Set<string>`, ...) 4. Get the new AST nodes, update the `@tailwind utilities` rule's nodes and re-print the AST to CSS. * improvement 1: ignore known invalid candidates This will reduce the amount of candidates to handle. They would eventually be skipped anyway, but now we don't even have to re-parse (and hit a cache) at all. * improvement 2: skip work, when generated AST is the same Currently incremental rebuilds are additive, which means that we are not keeping track if we should remove CSS again in development. We can exploit this information, because now we can quickly check the amoutn of generated AST nodes. - If they are the same then nothing new is generated — this means that we can re-use the previous compiled CSS. We don't even have to re-print the AST because we already did do that work in the past. - If there are more AST nodes, something new is generated — this means that we should update the `@tailwind utilities` rule and re-print the CSS. We can store the result for future incremental rebuilds. * improvement 3: skip work if no new candidates are detected - We already know a set of candidates from previous runs. - We also already know a set of candidates that are invalid and don't produce anything. This means that an incremental rebuild could give us a new set of candidates that either already exist or are invalid. If nothing changes, then we can re-use the compiled CSS. This actually happens more often than you think, and the bigger your project is the better this optimization will be. For example: ``` // Imagine file A exists: <div class="flex items-center justify-center"></div> <button class="text-red-500">Delete</button> ``` ``` // Now you add a second file B: <div class="text-red-500 flex"></div> ``` You just created a brand new file with a bunch of HTML elements and classes, yet all of the candidates in file B already exist in file A, so nothing changes to the actual generated CSS. Now imagine the other hundreds of files that already contain hundreds of classes. The beauty of this optimization is two-fold: - On small projects, compiling is very fast even without this check. This means it is performant. - On bigger projects, we will be able to re-use existing candidates. This means it stays performant. * remove `getAstNodeSize` We can move this up the tree and move it to the `rebuild` function instead. * remove invalid candidate tracking from `DesignSystem` This isn't used anywhere but only in the `rebuild` of the compile step. This allows us to remove it entirely from core logic, and move it up the chain where it is needed. * replace `throwOnInvalidCandidate` with `onInvalidCanidate` This was only needed for working with `@apply`, now this logic _only_ exists in the code path where we are handling `@apply`. * update `compile` API signature * update callsite of `compile()` function * fix typo |
||
|
|
b7dd979735
|
when using { optimize: true }, also minify the CSS (#13201)
If you don't want to minify, but you do want to optimize, then you can
use `{ optimize: { minify: false} }` instead.
|
||
|
|
c550a62be4
|
[v4] Make CSS optimization and minification configurable (#13130)
* only run Lightning CSS when passing `--minify` to the CLI
* only optimize the CSS when creating a production build
* add `optimize` option to PostCSS plugin
- The optimize option can be set to `true`, which will optimize
(unnesting, adding browser prefixes, lowering values) and minify
- The optimize option can also be set to `{ minify: false }`, which will
optimize but not minify.
* default `optimize` option to the true for `NODE_ENV=production`
* add `--optimize` flag to CLI
This will only optimize the CSS output without minification.
* update `--minify` description
* update changelog
|
||
|
|
a68de1df27
|
introduce v4 codebase |