Commit graph

6789 commits

Author SHA1 Message Date
Robin Malfait
ba4c23eaff
update changelog 2026-06-26 13:32:56 +02:00
Robin Malfait
0c3554499e
Improve release (#20280)
This PR improves the release script:

- The `node-linker=hoisted` was missing for the `wasm32-wasi` package
(this is because of the pnpm v11 changes where it migrated the
noide-linker setup from `.npmrc` to a flag during `pack`)
- Notify discord in case the release fails

## Test plan

1. All tests should pass because we didn't change anything related to
code
2. Unfortunately, the only way to properly testing this is to merge it
2026-06-26 13:25:38 +02:00
Robin Malfait
94f7907b55
add margin of error to the test
Sometimes this is flaky on CI, even though it doesn't make any sense
because we use `setTimeout(_, 500)` which should _at least_ result in
500ms...

But sometimes this results into:
```
AssertionError: expected 499.76 to be greater than or equal to 500
```
2026-06-25 19:23:36 +02:00
Robin Malfait
38652a4638
migrate to pnpm v11 (#20273)
This PR bumps the repo to pnpm v11 (from v9). 

I kept running into weird Windows specific issues for the integration
tests due to some shims but they all pass right now.

Bumping to pnpm v11 also meant that everything is driven by a
`pnpm-workspace.yaml` file, and changes applied via the `pnpm` field in
the `package.json` don't work anymore.

This also moved the `node-linker` setup that was defined in the `.npmrc`
file for the wasm oxide build into the `pnpm-workspace.yaml` file.
Ideally this is scoped to just this package, but I couldn't get that to
work, so it's applied to all packages right now.

[ci-all]
2026-06-25 19:14:10 +02:00
Robin Malfait
7ff413bad4
Ignore unknown at-rule warnings for @position-try (#20277)
This PR ignores Lightning CSS warnings for the unknow at-rule
`@position-try`.

This is a temporary fix/workaround until support for `@position-try`
lands in Lightning CSS:
https://github.com/parcel-bundler/lightningcss/pull/1238

Fixes: #20275

## Test plan

Ran a test based on the issue:

Before:
```shellsession
❯ tw -i x.css -o out.css --optimize
≈ tailwindcss v4.3.1

Found 1 warning while optimizing generated CSS:

│   position-try-fallbacks: --flip-above;
│ }
│ @position-try --flip-above {
┆              ^-- Unknown at rule: @position-try
┆
│   top: auto;
│   bottom: 0;

Done in 29ms
```

After:
```shellsession
❯ tw -i x.css -o out.css --optimize
≈ tailwindcss v4.3.1

Done in 14ms
```
2026-06-25 13:21:12 +02:00
Robin Malfait
bb6a10937c
use .ts instead of .css
In newer Vite versions this extension matters, without the
vite/resolvers integration tests won't work.

Since this is still a placeholder fake file where Vite will run
`dirname` on, the actual file doesn't really matter as long as it's
relative to this file.
2026-06-25 13:03:10 +02:00
Robin Malfait
d5ca0aeac9
Handle template toolkit %]…[% syntax (#20269)
This PR handles template toolkit syntax as a pre-processor step such
that `%]` and `[%` are seen as valid boundary characters.

This is handled for the `.tt`, `.tt2` and `.tx` file extensions. It's
not handled if this syntax is used in `.html` files because then
everybody pays a pre processor cost even if you don't need this syntax
in most cases.

This now ensures that a `template.tx` like this:
```html
<div class="[% IF $is_open %]bg-white/40[% ELSE %]bg-white/10[% END %]"></div>
<!--                         ^^^^^^^^^^^          ^^^^^^^^^^^              -->
```
Extracts the classes in between those conditions correctly.

This also fixes a small issue related to Maud, a template engine for
Rust where conditionals like `p.text-black[condition]` caused the
`text-black` class not to be extracted. This is fixed as part of this PR
because it was commented on the linked issue.

Fixes: #20233 

## Test plan

1. Added a new extractor
2. Added regression tests
3. All existing tests pass
2026-06-22 15:40:58 +02:00
Robin Malfait
0fee7b55f9
Restrict walking sibling folders when using a pattern (#20263)
This PR fixes an issue where a `@source` pointing to a concrete file
could result in scanning the entire parent folder instead of only
looking for the file we are actually interested in.

When we optimize a `@source`, we move all the static parts of the
pattern to the `base`. This means that a `@source` like this:

```css
@source "../../app.config.ts";
```

Resolves to:

```rs
SourceEntry::Pattern { base: "/Users", pattern: "/app.config.ts" }
```

When walking the `base`, we would only emit an `!app.config.ts` rule.
This means that _everything_ in the `/Users` folder is still walked, and
the result is then thrown away. If you take a look at the `gitignore`
equivalent (which is what we build behind the scenes), then we would
essentially create the following:
```gitignore
!app.config.ts
```

But if you know how `gitignore` files work, then you know that this does
force `app.config.ts` to _not_ be ignored, but it doesn't say anything
about all the other files/folders.

The fix is to restrict the `base` so that we ignore everything in the
folder, and then explicitly re-include only the pattern we care about.
Fixing this would result in the following:
```gitignore
*
!foo.ts
```

In the reproduction from #20255 this takes the build from appearing to
hang (~22s) down to ~20ms on my machine.

There are a few edge cases we have to be careful about:

**Multiple patterns for the same base.** If we have multiple `@source`
directives for the same folder:

```css
@source "./src/foo.ts";
@source "./src/bar.ts";
```

Then blindly emitting `*` for each one would result in this `.gitignore`
equivalent:

```gitignore
*
!foo.ts
*
!bar.ts
```

Notice that the second `*` would end up ignoring `foo.ts` again. To
avoid this, we only emit the `*` rule once per `base`.

**Dynamic parts in intermediate folders.** If the pattern still contains
a `*` in one of its folders:

```css
@source "./src/ba*/*.html";
```

This resolves to:

```rs
SourceEntry::Pattern { base: "/src", pattern: "/ba*/*.html" }
```

If we now inject the `*` rule for `/src`, then we would never walk the
`ba*` folders (e.g. `bar` or `baz`). To fix this, we add inverse rules
for each parent segment of the pattern:

```gitignore
*                     ← ignore everything
!/ba*/                ← except for the `ba*/` folders, so we walk into them
!/ba*/*.html          ← then scan the `*.html` files in them
```

**Bases already covered by a broader source.** If the `base` is already
included (or nested) under an unrestricted source (an `Auto`/`External`
source, or a `Pattern` containing `**`), then we leave it alone.
Restricting it would incorrectly hide siblings that the broader source
is supposed to pick up. For example:

```css
@source "**/*";
@source "./src/components/button.html";
```

Here the `**/*` source should keep auto-detecting every file, so we must
_not_ restrict `src/components` just because there's a more specific
`@source` pointing to it.

Source order is preserved throughout, so later `@source not …` rules can
still override an earlier restricted source.

Fixes: #20255

## Test plan

1. Added unit tests realted to this `sources` logic
2. Added integration like tests for the scanner itself
3. All other tests should pass as-s
4. Should work on each OS [ci-all]
2026-06-19 22:39:57 +02:00
depfu[bot]
c33b85d012
Update all pnpm dependencies (2026-06-19) (#20262)
This is your weekly update of **all** pnpm dependencies. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?

✳️ postcss-selector-parser (7.1.1 → 7.1.4, patch) ·
[Repo](https://github.com/postcss/postcss-selector-parser) ·
[Changelog](https://github.com/postcss/postcss-selector-parser/blob/master/CHANGELOG.md)
·
[Release](https://github.com/postcss/postcss-selector-parser/releases/tag/7.1.4)
·
[Diff](cf6637ed6a...4a7e4e3685)

✳️ semver (7.8.1 → 7.8.4, patch) ·
[Repo](https://github.com/npm/node-semver) ·
[Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md) ·
[Release](https://github.com/npm/node-semver/releases/tag/v7.8.4) ·
[Diff](76416081a8...8640bd68f1)

✳️ turbo (2.9.14 → 2.9.18, patch) ·
[Repo](https://github.com/vercel/turborepo) ·
[Changelog](https://github.com/vercel/turborepo/blob/main/RELEASE.md) ·
[Release](https://github.com/vercel/turborepo/releases/tag/v2.9.18) ·
[Diff](fc62fe0d9c...3bdce3277d)




---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[Depfu](https://depfu.com) will only send you the next scheduled PR once
you merge or close this one.

<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@​depfu refresh</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@​depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@​depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@​depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@​depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@​depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
</dl></blockquote>
</details>

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2026-06-19 13:11:16 +02:00
Robin Malfait
c46f654fa0
Ensure --alpha(…) is seen as a color, and --spacing(…) is seen as a length (#20260)
This PR fixes an issue where intellisense recommends this
canonicalization:

```diff
- text-[calc(var(--spacing)*4)]
+ text-[--spacing(4)]
```

Which is correct, but the issue is that the result is different due to
ambiguity of the `text-*` utilities.

```css
.text-\[calc\(var\(--spacing\)\*4\)\] {
  font-size: calc(var(--spacing) * 4);
}
.text-\[--spacing\(4\)\] {
  color: calc(var(--spacing, 0.25rem) * 4);
}
```

Notice that we're using `color` all of a sudden? This is because that's
the default and we infer the data type based on the arbitrary value. The
`calc(…)` infers that the type is `length`, but we don't know what the
type of `--spacing(…)` is so we fallback to the default type which would
be `color`.

This PR makes sure that built in functions like `--alpha(…)` and
`--spacing(…)` are resolved as `color` and `length` respectively.

With this fix in place, this is the result:
```css
.text-\[calc\(var\(--spacing\)\*4\)\] {
  font-size: calc(var(--spacing) * 4);
}
.text-\[--spacing\(4\)\] {
  font-size: calc(var(--spacing, 0.25rem) * 4);
}
```


Fixes: #20256
Fixes: #20258

## Test plan

1. Added a regression test to make sure this doesn't happen anymore
2026-06-18 15:02:18 +02:00
Robin Malfait
34c2b04a35
Deleting files should not crash Vite HMR (#20259)
This PR fixes an issue where Vite crashes when a file is deleted.

This is because we don't cleanup files/folders in the scanner when they
are being deleted. This results in an issue where we would use Vite's
`addWatchFile` API with deleted files.

This PR fixes that by making sure that when we re-scan the filesystem
that we clear out files/folders (and recompute globs).

Note: `candidates` themselve are still append-only until a fresh
compiler instance is created since the TypeScript based compiler relies
on that behavior.

Fixes: #17532 
Closes: #20111

## Test plan

1. Added an integration test based on the issue
2. Tested it directly in the reproduction repo

Before deleting:
<img width="988" height="1376" alt="image"
src="https://github.com/user-attachments/assets/0f104ff3-0f88-415a-8311-1cf54ab75d21"
/>


Before:
<img width="1389" height="1376"
alt="file-1b446568bfad50e6a8105d7be33f15f0"
src="https://github.com/user-attachments/assets/d2fbf03e-ca22-4bc4-9bb4-f3de10b90b99"
/>
Which results in:
<img width="988" height="1376" alt="image"
src="https://github.com/user-attachments/assets/8320d61c-4210-4aa8-aa8d-98c5327e9604"
/>

After:
<img width="1389" height="1376"
alt="file-c36ab0265856da5bb7c7a1e77427b821"
src="https://github.com/user-attachments/assets/2ec4c4ea-aa79-4a51-900a-e247dcdb976c"
/>
Which results in:
<img width="988" height="1376" alt="image"
src="https://github.com/user-attachments/assets/8e8dec0d-40eb-4e54-b5fd-8d22abd55973"
/>


[ci-all]
2026-06-18 14:29:04 +02:00
Robin Malfait
5e9f66e428
Ensure @variant can be used in JS based APIs (#20252)
This PR doesn't fix any issues, but it does add an integration test (as
a regression test) to make sure that `@variant` with default variants
and custom variants can be used inside of JS based plugin APIs.

In Tailwind CSS v4.3.1 we introduced a PR that handles `@variant` in the
`addBase` Plugin API
(https://github.com/tailwindlabs/tailwindcss/pull/19480). This was a bit
of an older PR, but the tests made sense, so it was merged.

However, by introducing that PR, we introduced a bug that the `@variant`
was handled too early. If you added custom variants later _and_ used it
in the `addBase`, then you would get an error since the variant isn't
available (yet).

That issue was fixed by
https://github.com/tailwindlabs/tailwindcss/pull/20247

Now the question remains, why did we even have the original PR when it
already worked?

The use case we had was using `@variant` as part of the
`@tailwindcss/typography` plugin configuration for one of our templates.
I was indeed able to reproduce the issue where `@variant lg` was seen in
the output CSS file.

Turns out that this template was using `@tailwindcss/typography` +
`@variant` in the configuration, but it was also using Tailwind CSS
v4.1.15.

Upgrading to the latest version automagically fixed the issue we had.
This is also the behavior you can see in the integration test.

The correct behavior was introduced in an even older PR
https://github.com/tailwindlabs/tailwindcss/pull/19263

All that said, everything should work in the next release related to
`@variant` usages inside JS based APIs.


**Tiny improvement**

While debugging what's going on, I noticed that we looped over the AST
to get some nodes out and we did that twice. This PR also improves that
by re-using the same list of nodes instead of computing it twice. This
won't have a huge impact, but it happened while compiling every single
utility which is not ideal.

## Test plan

1. All tests should pass
2. I can't see `@variant` in the output CSS file

Input:
<img width="655" height="323" alt="image"
src="https://github.com/user-attachments/assets/20c8d524-3575-488c-b0c0-5c4669f37dc7"
/>

Before:
<img width="477" height="175" alt="image"
src="https://github.com/user-attachments/assets/045e2086-1d4b-487c-96ff-676351412935"
/>


After:
<img width="484" height="175" alt="image"
src="https://github.com/user-attachments/assets/24d950b1-7c28-40f8-96bc-81b38be0e7a3"
/>
2026-06-17 15:23:20 +02:00
Robin Malfait
707c23b955
Ensure custom variants can be used via @variant in addBase (#20247)
This PR fixes an issue where `@variant` inside `addBase` is being used
with a custom variant.

The issue is that we substitute the `@variant` calls immediately when we
call the `addBase` function. That means that variants that aren't
processed yet will error out.

This is a regression, because this used to work in Tailwind CSS v4.3.0
and started failing in Tailwind CSS v4.3.1.

## Test plan

1. Added a regression test
2026-06-16 23:05:50 +02:00
Robin Malfait
cc3b634fe0
Ensure @tailwindcss/cli watches the input file (#20246)
This PR fixes an issue where if the `--input` file (when using
`@tailwindcss/cli`) lives in an ignored folder, then changes to the
input file won't trigger a rebuild.

This happened in #17632 where the input file lives in the `assets/`
folder which is ignored by default.

However, if you make a change to the input file, then the CLI doesn't
restart which means that you can't update the `@source` directives
without quitting the program and re-starting it manually.

This PR solves that by automatically injecting an `@source` with the
path to the input file. This way the CLI will see changes, even if the
input file is in an ignored directory.

Fixes: #17632

## Test plan

1. Added an integration test
2. Existing tests pass
3. Reproduced it on the 


Before:
<img width="1122" height="1376"
alt="file-295ce4696b6b3199a3915c63f8bb50cd"
src="https://github.com/user-attachments/assets/09e5e965-38d1-474d-bfc6-3b5e9bca73b6"
/>

After:
<img width="1122" height="1376"
alt="file-301b2f2e06aee045957355014ae5de87"
src="https://github.com/user-attachments/assets/2b6d4cba-f456-4138-98bd-2259876fe70a"
/>
2026-06-16 18:38:29 +02:00
Robin Malfait
51f0760d29
Fix crash when using @tailwindcss/vite using deno v2.8.x (#20245)
This PR might fix an issue where resolvers don't work on deno v2.8.x
(they do work on deno v2.7.x) when using `@tailwindcss/vite`.

This happens when the `context.parentURL` is not actually a URL at all,
and therefore the `new URL(…)` just crashes. This PR will fallback to
the incoming result for files that are not passed proper URLs.

Fixes: #20232

## Test plan

1. Existing tests pass
2. Can't seem to figure out how to test this in the reproduction issue
without publishing first. So going to merge this and test the insiders
version instead.
2026-06-16 16:03:27 +02:00
Robin Malfait
127d17033a
Add bare value support for auto-rows-* and auto-cols-* (#20229)
This PR adds bare value support for `auto-rows-*` and `auto-cols-*`.

We first introduced `auto-rows-auto`, `auto-rows-min`, `auto-rows-max`
and `auto-rows-fr` (same for `auto-cols-*`) back in Tailwind CSS v1.9
(https://v1.tailwindcss.com/docs/grid-auto-rows#app) but we haven't
touched it since.

This PR now adds support for bare values that use the spacing scale.
That means that you can now use `auto-rows-12` or `auto-cols-12` which
will result in the following CSS:

```css
.auto-cols-12 {
  grid-auto-columns: calc(var(--spacing) * 12);
}
.auto-rows-12 {
  grid-auto-rows: calc(var(--spacing) * 12);
}
```

We could also add support for percentage based values. The only question
is what the syntax should be. This can either be `auto-rows-3/4` or
`auto-rows-75%`.

For the fraction case, we already have `w-3/4` and `aspect-3/4` even
though they both have a different value as a result:
```css
.aspect-3\/4 {
  aspect-ratio: 3/4;
}
.w-3\/4 {
  width: calc(3 / 4 * 100%);
}
```

We also have some precedence for the `%` value as well, e.g. `via-10%`
for gradient color stops.

I think I would personally towards the `auto-rows-75%` value instead of
`auto-rows-3/4`. The fraction syntax works great for aspect ratio
because that's literally what it is (`aspect-16/9`). The fraction also
works great for widths, because you typically have 2 elements next to
each other:
```html
<div>
  <div class="w-1/3"></div>
  <div class="w-2/3"></div>
</div>
```

Since you only use the `auto-rows-*` once on a parent element, I think
the `auto-rows-75%` makes a bit more sense than `auto-rows-3/4` since
there is no _other_ element (at least not that I can think of).


## TODO

- [ ] Support percentages?
   - [ ] Syntax A: `auto-rows-3/4`
   - [ ] Syntax B: `auto-rows-75%`
   - [ ] Syntax C: support both, but that seems silly

Percentage support isn't a blocker for this PR, since you can always use
`auto-rows-[75%]` if you want

## Test plan

1. Added a test to prove that this works
1. Existing tests still pass

Requested by:
https://github.com/tailwindlabs/tailwindcss/discussions/20225
2026-06-16 11:50:35 +02:00
Robin Malfait
abc4670a9a
Prevent crash when using @tailwindcss/cli using --watch on Windows (#20242)
This PR fixes an issue on Windows where the `@tailwindcss/cli` with the
`--watch` flag crashes when using a `@source` with a base path that
doesn't exist on disk.

This happens when setting up the `@parce/watcher` for directories that
don't exist. This PR essentially filters out these directories that
don't exist on disk to prevent the crash.

It might be that if you add the folder _later_, while the watcher is
already watching, that you have to restart the `@tailwindcss/cli` (or
save the `index.css` (the file that contains the `@source` directives),
this also recreates watchers from scratch).

If this issue causes problems for `@tailwindcss/postcss` and
`@tailwindcss/vite` in the future as well, then we can move this logic
back to Oxide. We do maintain the incoming `@source` files as best as
possible without resolving to absolute paths. Back when we did resolve
them, if that process error'd we just never returned the glob.

The root cause is still referencing folders that don't exist.

Fixes: #20231

## Test plan

1. Added a failing test, that did fail on Windows
2. Once fixed, all tests should pass

[ci-all]

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-15 17:20:31 +02:00
depfu[bot]
d1f9f53bf9
Update all pnpm dependencies (2026-06-15) (#20241)
This is your weekly update of **all** pnpm dependencies. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?

✳️ @emnapi/core (1.10.0 → 1.11.1, minor) ·
[Repo](https://github.com/toyobayashi/emnapi) ·
[Release](https://github.com/toyobayashi/emnapi/releases/tag/v1.11.1) ·
[Diff](ba84999164...6c3668c9af)

✳️ @emnapi/runtime (1.10.0 → 1.11.1, minor) ·
[Repo](https://github.com/toyobayashi/emnapi) ·
[Release](https://github.com/toyobayashi/emnapi/releases/tag/v1.11.1) ·
[Diff](ba84999164...6c3668c9af)

✳️ @tailwindcss/typography (0.5.19 → 0.5.20, minor) ·
[Repo](https://github.com/tailwindlabs/tailwindcss-typography) ·
[Changelog](https://github.com/tailwindlabs/tailwindcss-typography/blob/main/CHANGELOG.md)
·
[Release](https://github.com/tailwindlabs/tailwindcss-typography/releases/tag/v0.5.20)
·
[Diff](e002ab89ad...e3714a3fe5)

✳️ @emnapi/wasi-threads (1.2.1 → 1.2.2, patch) ·
[Repo](https://github.com/toyobayashi/emnapi)




---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[Depfu](https://depfu.com) will only send you the next scheduled PR once
you merge or close this one.

<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@​depfu refresh</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@​depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@​depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@​depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@​depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@​depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
</dl></blockquote>
</details>

---------

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-06-15 13:13:32 +00:00
Robin Malfait
8a14a71010
4.3.1 (#20226) 2026-06-12 19:33:35 +02:00
Truffle
522288ca08
Serve ESM type declarations to ESM importers of @tailwindcss/postcss (#20228)
## Summary

Fixes #20219.

The `@tailwindcss/postcss` exports map serves the CJS-shaped
`dist/index.d.ts` (`export = _default`) for both the `import` and
`require` conditions, while the correct ESM declaration file
`dist/index.d.mts` (`export { _default as default }`) is built and
published but never referenced. Type checkers that treat the import
condition as ESM reject the default import with TS1192 — concretely,
`deno check` on Deno 2.8.3+ (which ships TypeScript 6.0) fails on
`import tailwindcss from "@tailwindcss/postcss"`.

This splits the export entry into per-condition `types` blocks so ESM
importers resolve `index.d.mts` and CJS consumers keep `index.d.ts` —
the same shape `@tailwindcss/vite` already uses (`"types":
"./dist/index.d.mts"`).

Two notes on the issue as filed:

- Stock `tsc` with NodeNext does **not** reproduce the error
(declaration file format follows the file extension and package `type`,
not the matched condition), so I didn't take the suggested `export =` →
`export default` change in `index.d.ts` — that would break CJS
consumers, and the correct ESM declarations already ship.
- `@tailwindcss/node` has the same single-`types` exports shape; happy
to follow up there if you want parity.

## Test plan

Against `@tailwindcss/postcss@4.3.0` with the published exports map,
then again with only this `package.json` change applied to the installed
package:

```sh
# Deno 2.8.3 (TypeScript 6.0), nodeModulesDir: auto
deno check main.ts   # before: TS1192 "Module ... index.d.ts has no default export" — after: passes
```

```sh
# typescript@6.0.0-dev (NodeNext), one ESM importer + one .cts require importer
tsc --noEmit         # passes both before and after (no regression for npm consumers)
```

`@arethetypeswrong/cli`:

| | published 4.3.0 | with this change |
|---|---|---|
| node16 (from ESM) | 🎭 Masquerading as CJS | 🟢 (ESM) |
| node16 (from CJS) | 🟢 (CJS) | 🟢 (CJS) |
| bundler | 🟢 | 🟢 |
2026-06-12 14:33:16 +00:00
Robin Malfait
12833aa4b3
Fix canonicalization bug where we end up with a high precision number (#20221)
This PR fixes a bug where we suggest a canonicalization with a high
precision number.

E.g.:

`w-[calc(100%/3.5)]` → `w-[28.571428571428573%]`

While this is technically correct, it's also not as user friendly. This
PR solves this issue by checking whether the result has a precision of
`.xx` at most. If that's not the case, then we keep the original
expression.

Fixes:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1591

## Test plan

1. Added an a regression test for this situation
2. Existing tests pass
2026-06-12 00:23:11 +02:00
Rohan Santhosh Kumar
97a5b3abfb
docs: fix double word 'to to' in test comment (#20216)
## Summary

Fixes a double word typo in a test comment: 'No need to to wrap' → 'No
need to wrap'.

## Test plan

This is a comment-only change in a test file. No functional changes.

Co-authored-by: Codex <codex@openai.com>
2026-06-11 12:07:00 +02:00
Robin Malfait
6b0eb5193e
Ensure @source globs ending in **/* preserve dynamic path segments to avoid scanning too many files (#20217)
This PR fixes an issue where we were over-scanning because we lost
pattern information when a `@source` ended in `**/*` and contained other
dynamic parts in the glob pattern.

Noticed this while debugging and working on #20214. Let's say you have
the following:
```css
@source './blog/*/foo/bar/baz/**/*';
```

We make sure that folders or patterns ending in `**/*` are converted to
"auto sources", meaning that auto content detection should be used in
these folders.

However, when doing so, we would only take the `base` path of the glob.
And since we have "dynamic" parts (the `*`) in the pattern, that should
not be the case.

So the `@source` from above, would be turned into:
```rs
SourceEntry::Auto { base = "/Users/projects/project/blog" }
```

Notice that we lose all the information related to `/*/foo/bar/baz/`.
While this would technically still work, it also means that we are
scanning **too many files and folders** because we're only interested in
folders in the `blog` folder that also contain `foo/bar/baz`.

If the `*` wasn't there, then this would be correct, because then we
would've moved the `/blog/foo/bar/baz` part to the `base` ahead of time,
and the pattern would just be `/**/*`. That would result in:
```rs
SourceEntry::Auto { base = "/Users/projects/project/blog/foo/bar/baz" }
```

This PR fixes that, by _not_ converting it to an auto-source, and
instead convert it to a pattern:
```rs
SourceEntry::Pattern {
  base: "/Users/projects/project/blog",
  pattern: "/*/foo/bar/baz/**/*"
}
```

Notice that the static part `blog` is still moved to the base path. But
the rest stays in the `pattern` part as expected.

## Test plan

1. Added a failing test to make sure this doesn't happen anymore
2. Existing tests pass 
3. Tested this on the tailwindcss.com codebase using `@source
"../blog/tailwindcss*/**/*";`

```diff
 diff --git a/./tailwindcss-55526.log b/./tailwindcss-55527.log
index ce79c5da..03e97598 100644
--- a/./tailwindcss-55526.log
+++ b/./tailwindcss-55527.log
@@ -2,50 +2,9 @@ INFO tailwindcss_oxide::scanner: Provided sources:
 INFO tailwindcss_oxide::scanner: Source: PublicSourceEntry { base: "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/app", pattern: "../blog/tailwindcss*/**/*", negated: false }
 INFO tailwindcss_oxide::scanner: Source: PublicSourceEntry { base: "/Users/robin/.bun/bin", pattern: "bun", negated: true }
 INFO tailwindcss_oxide::scanner: Optimized sources:
-INFO tailwindcss_oxide::scanner: Source: Auto { base: "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog" }
+INFO tailwindcss_oxide::scanner: Source: Pattern { base: "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog", pattern: "/tailwindcss*/**/*" }
 INFO tailwindcss_oxide::scanner: Source: Ignored { base: "/Users/robin/.bun/bin", pattern: "/bun" }
 INFO discover_sources: tailwindcss_oxide::scanner: enter
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2022-05-23-headless-ui-v1-6-tailwind-ui-team-management/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2022-06-23-tailwind-templates-and-all-access/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2022-08-17-tailwind-framer-motion-template-and-tailwind-jobs/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2022-09-09-new-personal-website-heroicons-2-headless-ui-v17/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2022-12-15-protocol-api-documentation-template/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2023-04-24-new-changelog-template-and-the-biggest-tailwind-ui-update-ever/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2023-07-18-tailwind-connect-2023-recap/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2023-08-07-meet-studio-our-new-agency-template/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2024-05-24-catalyst-application-layouts/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2024-05-30-prettier-plugin-collapse-whitespace/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2024-06-21-headless-ui-v2-1/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2024-09-12-radiant-a-beautiful-new-marketing-site-template/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/2025-05-14-compass-course-starter-kit/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/automatic-class-sorting-with-prettier/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/building-react-and-vue-support-for-tailwind-ui/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/building-the-tailwind-blog/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/designing-tailwind-ui-ecommerce/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/from-900-to-1-how-we-hired-robin-malfait/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-unstyled-accessible-ui-components/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v1-4/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v1-5/demo.tsx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v1-5/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v1/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v2/examples/HeadlessUIV2Examples.tsx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v2/examples/StateAttributesExample.tsx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v2/examples/anchor-positioning.tsx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/headless-ui-v2/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/heroicons-micro/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/heroicons-v1/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/hiring-a-design-engineer-and-staff-engineer/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/introducing-catalyst/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/introducing-heroicons/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/introducing-linting-for-tailwindcss-intellisense/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/introducing-tailwind-play/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/just-in-time-the-next-generation-of-tailwind-css/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/multi-line-truncation-with-tailwindcss-line-clamp/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/simon-vrachliotis-joins-tailwind-labs/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/standalone-cli/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwind-plus/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwind-ui-ecommerce/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwind-ui-now-with-react-and-vue-support/index.mdx"
 INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwindcss-1-5/index.mdx"
 INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwindcss-1-6/index.mdx"
 INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwindcss-1-7/index.mdx"
@@ -68,10 +27,4 @@ INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/t
 INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwindcss-v4-beta/index.mdx"
 INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwindcss-v4/color-palette.tsx"
 INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/tailwindcss-v4/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/utility-friendly-transitions-with-tailwindui-react/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/vanilla-js-support-for-tailwind-plus/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/welcoming-brad-cornes-to-the-tailwind-team/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/welcoming-david-luhr-to-tailwind-labs/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/welcoming-james-mcdonald-to-tailwind-labs/index.mdx"
-INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/tailwindlabs/tailwindcss.com/src/blog/whats-new-in-tailwindcss-on-youtube/index.mdx"
 INFO discover_sources: tailwindcss_oxide::scanner: exit
```

Notice now that a lot of files are skipped as expected since we-re not
accidentally over-scanning now.

[ci-all]
2026-06-11 11:59:58 +02:00
Robin Malfait
1bf4291e85
Fix @source with folders that are ignored (#20214)
This PR fixes an issue where a `@source` that's pointing to a folder
that is git ignored, is also ignored by the `@source` even if it's
explicitly added.

Internally, we convert `@source` directives from `PublicSourceEntry`s to
`SourceEntry`s where we have dedicated enum branches for `Auto`,
`Pattern`, `Ignored` and `External`.

The `Auto` one accepts a `base` path, and will be used for auto content
detection. However, these paths will make use of all the default auto
content detection rules, which includes git ignore rules.
We also have `External` where we link to something that's "external" to
the current repo. We can probably improve this name, but it's external
in the sense that it won't show up on GitHub for example, aka ignored.
We have some content dirs that we ignore by default, such as the
`node_modules` folder. When you do use `@source` with `node_modules` in
the path, then we will mark it as an `external` resource which does not
look at the `gitignore` related rules and allowing it to be included
this way.

The idea with this is that, even though the folder is ignored by
default, you can still include files from the folder by explicitly using
the `@source` directive.

The issue as seen in #19844 is using `vendor/` instead of
`node_modules/` which is _not_ ignored by default. While we can add
`vendor/` to this same ignored dirs list, it will result in a breaking
change because this folder is often used by the Laravel community to
store some resources in.

This PR fixes this problem by not only looking at the content dirs we
ignore by default, but also looking at the actual git ignore state of
this folder. If it turns out that this is ignored, then we promote the
`Auto` source to an `External` source.

Fixes: #19844
Closes: #20057


## Test plan

1. Added integration tests for this situation
2. Ran the fix on the reproduction from #19844. If we run the CLI with
the `DEBUG=*` environment variable, the log file produces these results:

```diff
diff --git a/./tailwindcss-29207.log b/./tailwindcss-30381.log
index bc3017c..921af0e 100644
--- a/./tailwindcss-29207.log
+++ b/./tailwindcss-30381.log
@@ -6,8 +6,9 @@ INFO tailwindcss_oxide::scanner: Source: PublicSourceEntry { base: "/Users/robin
 INFO tailwindcss_oxide::scanner: Optimized sources:
 INFO tailwindcss_oxide::scanner: Source: Pattern { base: "/Users/robin/github.com/GrimLink/tailwind-gitignore-bug/app/design/frontend/theme", pattern: "/**/*.phtml" }
 INFO tailwindcss_oxide::scanner: Source: Pattern { base: "/Users/robin/github.com/GrimLink/tailwind-gitignore-bug/app/design/frontend/theme", pattern: "/**/*.xml" }
-INFO tailwindcss_oxide::scanner: Source: Auto { base: "/Users/robin/github.com/GrimLink/tailwind-gitignore-bug/vendor/acme/theme" }
+INFO tailwindcss_oxide::scanner: Source: External { base: "/Users/robin/github.com/GrimLink/tailwind-gitignore-bug/vendor/acme/theme" }
 INFO tailwindcss_oxide::scanner: Source: Ignored { base: "/Users/robin/.fnm/node-versions/v26.1.0/installation/bin", pattern: "/node" }
 INFO discover_sources: tailwindcss_oxide::scanner: enter
-INFO discover_sources: tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/GrimLink/tailwind-gitignore-bug/app/design/frontend/theme/index.phtml"
+INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/GrimLink/tailwind-gitignore-bug/app/design/frontend/theme/index.phtml"
+INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/GrimLink/tailwind-gitignore-bug/vendor/acme/theme/module/templates/component.phtml"
 INFO discover_sources: tailwindcss_oxide::scanner: exit
```

We're checking some `.gitignore` related files, so let's check on each
OS [ci-all]
2026-06-10 18:44:14 +02:00
depfu[bot]
1d5e15e9d8
Update all of react 19.2.6 → 19.2.7 (patch) (#20206)
<!--depfu-start-->
> 👉 **This PR is queued up to get rebased by Depfu**
<!--depfu-end-->





Here is everything you need to know about this upgrade. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ react (19.2.6 → 19.2.7) ·
[Repo](https://github.com/facebook/react) ·
[Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/facebook/react/releases/tag/v19.2.7">19.2.7</a></h4>

<blockquote><h2 dir="auto">React Server Components</h2>
<ul dir="auto">
<li>Fixed missing <code class="notranslate">FormData</code> entries in
Server Actions which regressed in 19.2.6<br>
(<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/36566">#36566</a>
by <a
href="https://bounce.depfu.com/github.com/unstubbable">@unstubbable</a>)</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please let us
know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="eaf3e95ca9...6117d7cca4">See
the full diff on Github</a>. The new version differs by 5 commits:</p>
<ul>
<li><a
href="6117d7cca4"><code>Version
19.2.7 (#36591)</code></a></li>
<li><a
href="53a07de1a7"><code>[19.2.x]
Unified release process (#36550)</code></a></li>
<li><a
href="02c444c3f0"><code>[19.2.x][FlightReply]
Don&#39;t drop FormData entries in `decodeReplyFromBusboy`
(#36566)</code></a></li>
<li><a
href="750848e565"><code>[19.2.x][ci]
Create artifact attestations for builds on backport branches
(#36559)</code></a></li>
<li><a
href="e1cfa7b6de"><code>[19.2.x]
Enable CI (#36552)</code></a></li>
</ul>
</details>












---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[Depfu](https://depfu.com) will automatically keep this PR
conflict-free, as long as you don't add any commits to this branch
yourself. You can also trigger a rebase manually by commenting with
`@depfu rebase`.

<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@​depfu rebase</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@​depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@​depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@​depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@​depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@​depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
<dt>@​depfu pause</dt><dd>Ignores all future updates for this dependency
and closes this PR</dd>
<dt>@​depfu pause [minor|major]</dt><dd>Ignores all future minor/major
updates for this dependency and closes this PR</dd>
<dt>@​depfu resume</dt><dd>Future versions of this dependency will
create PRs again (leaves this PR as is)</dd>
</dl></blockquote>
</details>

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2026-06-09 10:44:01 +00:00
depfu[bot]
e7d64e7648
Update next 16.2.6 → 16.2.7 (patch) (#20207)
Here is everything you need to know about this upgrade. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ next (16.2.6 → 16.2.7) ·
[Repo](https://github.com/vercel/next.js)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v16.2.7">16.2.7</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg data-component="Octicon"
class="octicon octicon-info mr-2" viewbox="0 0 16 16" version="1.1"
width="16" height="16" aria-hidden="true"><path d="M0 8a8 8 0 1 1 16 0A8
8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5
7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0
1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1
1 0-2 1 1 0 0 1 0 2Z"></path></svg>Note</p>
<p dir="auto">This release is backporting bug fixes. It does
<strong>not</strong> include all pending features/changes on canary.</p>
</div>
<h3 dir="auto">Core Changes</h3>
<ul dir="auto">
<li>Backport documentation fixes for v16.2 (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/93804">#93804</a>)</li>
<li>[backport] Patch <code class="notranslate">playwright-core</code> to
resolve <code class="notranslate">_finishedPromise</code> on <code
class="notranslate">requestFailed</code> (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/93920">#93920</a>)</li>
<li>[backport] Fix dev mode hydration failure when page is served from
HTTP cache (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/93492">#93492</a>)</li>
<li>[backport] Fix catch-all <code
class="notranslate">router.query</code> corruption with <code
class="notranslate">basePath</code> + <code
class="notranslate">rewrites</code> (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/93917">#93917</a>)</li>
<li>[backport] Encode non-ASCII characters in cache tags at construction
(<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/93918">#93918</a>)</li>
<li>[backport] Fix server action forwarding loop with middleware
rewrites (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/93919">#93919</a>)</li>
<li>[backport] Turbopack: switch from base40 to base38 hash encoding (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/93932">#93932</a>)</li>
<li>[ci] Disable hanging node 24 typescript tests on 16.2 backport
branch (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/94164">#94164</a>)</li>
<li>[backport] Fix "type: module" in project dir when using standalone
or adapters (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/94050">#94050</a>)</li>
<li>[backport] Propagate adapter preferred regions (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/94200">#94200</a>)</li>
<li>[16.2.x] Don't drop <code class="notranslate">FormData</code>
entries (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/94240">#94240</a>)</li>
<li>[backport] feat(turbopack): add LocalPathOrProjectPath PostCSS
config resolution (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/94284">#94284</a>)</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a>, <a
href="https://bounce.depfu.com/github.com/icyJoseph">@icyJoseph</a>, <a
href="https://bounce.depfu.com/github.com/unstubbable">@unstubbable</a>,
<a href="https://bounce.depfu.com/github.com/mischnic">@mischnic</a>, <a
href="https://bounce.depfu.com/github.com/bgw">@bgw</a>, <a
href="https://bounce.depfu.com/github.com/timneutkens">@timneutkens</a>,
and <a
href="https://bounce.depfu.com/github.com/lukesandberg">@lukesandberg</a>
for helping!</p></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please let us
know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="ee6e79b179...9bd3c26a73">See
the full diff on Github</a>. The new version differs by 13 commits:</p>
<ul>
<li><a
href="9bd3c26a73"><code>v16.2.7</code></a></li>
<li><a
href="c63224f3d8"><code>[backport]
feat(turbopack): add LocalPathOrProjectPath PostCSS config resolution
(#94284)</code></a></li>
<li><a
href="63115c7987"><code>[16.2.x]
Don&#39;t drop `FormData` entries (#94240)</code></a></li>
<li><a
href="aef22fdc82"><code>[backport]
Propagate adapter preferred regions (#94200)</code></a></li>
<li><a
href="f126e72271"><code>[backport]
Fix &quot;type: module&quot; in project dir when using standalone or
adapters (#94050)</code></a></li>
<li><a
href="bda3e2aabe"><code>[ci]
Disable hanging node 24 typescript tests on 16.2 backport branch
(#94164)</code></a></li>
<li><a
href="7e16e07c02"><code>[backport]
Turbopack: switch from base40 to base38 hash encoding
(#93932)</code></a></li>
<li><a
href="6139f4b885"><code>[backport]
Fix server action forwarding loop with middleware rewrites
(#93919)</code></a></li>
<li><a
href="c021d10fe9"><code>[backport]
Encode non-ASCII characters in cache tags at construction
(#93918)</code></a></li>
<li><a
href="9184ddb1ae"><code>[backport]
Fix catch-all `router.query` corruption with `basePath` + `rewrites`
(#93917)</code></a></li>
<li><a
href="3279f01607"><code>[backport]
Fix dev mode hydration failure when page is served from HTTP cache
(#93492)</code></a></li>
<li><a
href="ffc4f29682"><code>[backport]
Patch `playwright-core` to resolve `_finishedPromise` on `requestFailed`
(#93920)</code></a></li>
<li><a
href="bc1791dd7e"><code>Backport
documentation fixes for v16.2 (#93804)</code></a></li>
</ul>
</details>












---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[Depfu](https://depfu.com) will automatically keep this PR
conflict-free, as long as you don't add any commits to this branch
yourself. You can also trigger a rebase manually by commenting with
`@depfu rebase`.

<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@​depfu rebase</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@​depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@​depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@​depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@​depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@​depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
<dt>@​depfu pause</dt><dd>Ignores all future updates for this dependency
and closes this PR</dd>
<dt>@​depfu pause [minor|major]</dt><dd>Ignores all future minor/major
updates for this dependency and closes this PR</dd>
<dt>@​depfu resume</dt><dd>Future versions of this dependency will
create PRs again (leaves this PR as is)</dd>
</dl></blockquote>
</details>

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2026-06-09 12:38:33 +02:00
Robin Malfait
d01e103cc4
Add missing inset keyword for inset-shadow-none (#20208)
This PR fixes an issue where the `inset-shadow-none` class did not use
the `inset` keyword. This PR fixes that by introducing the `inset`
keyword:

```diff
  .inset-shadow-none {
-   --tw-inset-shadow: 0 0 #0000;
+   --tw-inset-shadow: inset 0 0 #0000;
    box-shadow: var(--tw-inset-shadow), var
  }
```

While the end effect is the same (there will be no shadow), it also
means that we switch between the types of shadows. This results in the
fact that things like transitions don't behave the way they should.

A minimal reproduction looks like this:
https://play.tailwindcss.com/iOrnKWP49a (depending on when you visit
this link, it might be fixed)

## Test plan

1. Updated tests to reflect
2. Verified in the Vite playground that this is the case. In the video
you will notice that the transition is smooth in the fixed case, but
there is no transition in the current state:



https://github.com/user-attachments/assets/15d8c78e-6be5-4c6c-8df9-1044c14e8f39
2026-06-08 23:08:52 +02:00
Robin Malfait
68fcdf1f05
Do not crash when migrating empty classes (#20205)
This PR fixes a crash while using `npx @tailwindcss/upgrade` when
migrating classes with no body inside of an `@layer utilities`.

When running `npx @tailwindcss/upgrade`, one thing we do is migrate the
CSS from:
```css
@layer utilities {
  .foo {
    color: red;
  }
}
```
To:
```css
@utility foo {
  color: red;
}
```

We already have some logic that leaves non-classe (IDs, attribute
selectors, ...) alone. But if we are migrating a class that has no body,
then we will migrate that as well:
```css
@layer utilities {
  .empty {
  }
}
```
Is turned into:
```css
@utility empty {
}
```

But later in the migration process this will result in an error because
a `@utility` has to have at least _some_ nodes.

Ideally, you don't even have CSS that has empty rules since it doesn't
have any effect in the browser (except of making your CSS file bigger),
but it could be that you don't have control over this file, so a fix is
still valid.

This PR solves that by leaving those rules alone, and keep them in an
`@layer utilities`.

Fixes: #20204

## Test plan

1. Added a dedicated test for this usecase
2026-06-07 21:50:35 +02:00
Robin Malfait
3f58e52e36
Ensure @source globs with symlinks are preserved (#20203)
This PR fixes an issue when working with `@source` and `@source not`
that involves symlinks.

Internally our sources are mapped to a source entry where we have a
`base` path and a `pattern`. You can think about this where we insert a
`.gitignore` file in the `base` path for the given pattern.

However, we optimize these entries to move as many "static" parts into
the base path. For example:
```css
@source "./some/folder/here/*.html";
```

Is mapped to something like:
```ts
{ base: "/projects/my-project", pattern: "./some/folder/here/*.html" }
```
We then optimize it by turning it into:
```ts
{ base: "/projects/my-project/some/folder/here", pattern: "*.html" }
```

While doing this, we also use `dunce::canonicalize` to resolve the
actual paths on disk. This means that a symlink is resolved to their
real paths. This can cause issues because the "real" path is not what
you wrote in the `@source` directives.

So before, it could be that you have this:
```css
@source "./some/symlinked-folder/here/*.html";
```
Which was mapped to this on the Rust side:
```ts
{ base: "/projects/my-project", pattern: "./some/symlinked-folder/here/*.html" }
```
But was then optimized to:
```ts
{ base: "/projects/my-project/some/actual-folder/here", pattern: "*.html" }
```

...and we lost the `symlinked-folder` information. This causes issues as
seen in #17985.

With this PR, we keep the symlinked information in those globs since
that's what you wrote in those `@source` directives.

While setting up integration tests, I stumbled upon an issue because I
wanted to test that ignoring a symlinked folder, but including a single
particular file of that ignored folder resulted in that file being
ignored as well. Let's look at an example:

```css
@source     '../lib';
@source not '../lib/ignored';
@source     '../lib/ignored/except.html';
```

Earlier I mentioned that we create `.gitignore` files based on these
`@source` directives. In this case, when we're dealing with a folder, we
use `**/*` as the contents.

Looking at the example above, we should essentially have something like
this:
```gitignore
# lib/.gitignore
# @source '../lib'
!**/*

# lib/ignored/.gitignore
# @source '../lib/ignored'
**/*

# @source '../lib/ignored/except.html'
!except.html
```

Since it's a `.gitignore` file, we have to invert the globs. But the bug
I noticed is that in reality the result of those gitignores didn't look
like the above, it looked like:
```gitignore
# lib/.gitignore
# @source '../lib'
!**/*

# lib/ignored/.gitignore
# @source '../lib/ignored/except.html'
!except.html

# @source '../lib/ignored'
**/*
```

Notice how the `!except.html` and `**/*` are flipped. When dealing with
`.gitignore` files, the order is important.

This was caused because internally we kept a `BTreeMap` of `BTreeSet`s
where the map was the base path and a set of patterns. The patterns were
sorted because of the `BTreeSet`... which is not what we want.

Fixes: #17985
Closes: #20091

## Test plan

1. Added new tests in the scanner tests (on the Rust side)
2. Added integration tests with a symlink to another folder, outside of
the current folder
2. Added integration tests with a symlink to another folder, inside of
the current folder
2. Added integration tests to ensure that the order of `@source` files
with a folder + file is sorted correctly.
3. Since we're dealing with symlinks in these tests, let's test all OSes
[ci-all]
2026-06-07 20:44:22 +02:00
Jordan Pittman
ad6693906a
Allow @variant to be used inside addBase (#19480)
This seems like an unintentional omission

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-06-04 10:46:40 +00:00
Chris Hasiński
d42b34abbf
fix: handle invalid UTF-8 in Ruby and Vue preprocessors (#19588)
## Summary

This PR fixes a panic that occurs when the Ruby or Vue preprocessors
encounter files with invalid UTF-8 bytes.

**The issue:**
- `ruby.rs:37` and `vue.rs:18` used
`std::str::from_utf8(content).unwrap()`
- This panics when processing files containing invalid UTF-8 bytes

**Error message:**
```
thread panicked at crates/oxide/src/extractor/pre_processors/ruby.rs:37:59:
called `Result::unwrap()` on an `Err` value: Utf8Error { valid_up_to: 45, error_len: Some(1) }
```

**The fix:**
- Wrap UTF-8 conversion in `if let Ok(...)` to gracefully handle invalid
UTF-8
- Skip regex-based template extraction when UTF-8 conversion fails
- Allow byte-level processing to continue (in Ruby's case)

This can happen in Rails projects when:
- Binary files are inadvertently scanned
- Files contain non-UTF-8 encodings  
- Files are truncated at multi-byte character boundaries during parallel
processing

## Test plan

- [x] Added `test_invalid_utf8_does_not_panic` test for Ruby
preprocessor
- [x] Added `test_valid_utf8_with_multibyte_chars` test for Ruby
preprocessor
- [x] Added `test_invalid_utf8_does_not_panic` test for Vue preprocessor
- [x] All existing tests pass (`cargo test pre_processors` - 43 tests)

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-06-04 10:35:47 +00:00
Robin Malfait
0612ddc7dc
Add Twig pre-processor (#20198)
This PR fixes an issue where `addClass(opacity-50)` and
`removeClass(opacity-50)` in Twig templates isn't extracted properly.

This fixes that by adding a pre-processor for `.twig` files. This is a
simple initial implementation where we drop the `(` and `)` from the
`addClass` and `removeClass` functions. We don't do any special handling
around escaped characters or parenthesis inside of strings. We will add
them when there is a use case for it. Until then, we'll keep it simple.

If the real API would've been `addClass('opacity-50')` then it would've
worked out of the box.

A workaround you can use today is by using spaces `addClass( opacity-50
)` that works as well.

Fixes: #19458
Closes: #20110

## Test plan

1. Added new tests for `twig` extraction
2. Added tests with nested `(` and `)` e.g. `addClass(p-(--value))`
which should properly extarct `p-(--value)`
2026-06-04 11:44:13 +02:00
depfu[bot]
0b58dd69fd
Update @napi-rs/cli 3.6.2 → 3.7.0 (minor) (#20197)
Here is everything you need to know about this upgrade. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/cli (3.6.2 → 3.7.0) ·
[Repo](https://github.com/napi-rs/napi-rs)





Sorry, we couldn't find anything useful about this release.











---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[Depfu](https://depfu.com) will automatically keep this PR
conflict-free, as long as you don't add any commits to this branch
yourself. You can also trigger a rebase manually by commenting with
`@depfu rebase`.

<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@​depfu rebase</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@​depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@​depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@​depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@​depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@​depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
<dt>@​depfu pause</dt><dd>Ignores all future updates for this dependency
and closes this PR</dd>
<dt>@​depfu pause [minor|major]</dt><dd>Ignores all future minor/major
updates for this dependency and closes this PR</dd>
<dt>@​depfu resume</dt><dd>Future versions of this dependency will
create PRs again (leaves this PR as is)</dd>
</dl></blockquote>
</details>

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2026-06-04 10:31:08 +02:00
Aarni Koskela
efae52c3af
Simplify CSS when using utilities that use a *-0 or *-1 value (#20196)
## Why?


While inspecting a Tailwind-powered site, I noticed selectors like

```css
.inset-0{inset:calc(var(--spacing) * 0)}
.inset-x-0{inset-inline:calc(var(--spacing) * 0)}
.top-0{top:calc(var(--spacing) * 0)}
```

which seem a bit silly, not to mention more complex for the end-user
device parsing the CSS.

## Summary

This PR adjusts helpers to not generate `calc(... * 0)` expressions.

https://github.com/tailwindlabs/tailwindcss/pull/19095 does a similar
thing on CSS AST, but that doesn't run during the build proper.

## Test plan

Ran `pnpm build && pnpm test && pnpm test:integration`. (Some unrelated
tests failed on my machine, hopefully less in CI.)

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-06-03 16:44:09 +00:00
Robin Malfait
0c5f8bfd88
Ignore the standalone CLI (#20139)
This PR fixes an issue where if you use the standalone CLI, and you move
the standalone CLI into the current project, then we would scan that
standalone CLI as-if it contains Tailwind CSS classes. Since the CLI
contains actual Tailwind CSS classes, and is in fact readable text, this
binary would've been used as a source.

There are a few ways of fixing this, we could hardcode all the known
names, but that would result in an issue if you rename the CLI. We could
check whether it's a binary format and look for magic numbers at the
top. We could also check for a shebang at the top of the file and skip
it that way.

While some of these solutions might still be useful for the future. For
now I fixed it by essentially always ignoring `process.execPath`. That
way we never ever scan the actual executable regardless of whether you
renamed it or not.

Fixes: #20134

## Test plan

- Added an integration tests
- Works on every OS [ci-all]
2026-06-02 12:49:53 +02:00
Robin Malfait
44818a6cf6
Ensure @tailwindcss/cli recovers from a deleted transitive dependency (#20137)
This PR fixes an issue where the `@tailwindcss/cli` can get into a
non-recoverable state when any of the transitive dependencies break.

Tailwind CSS has 2 kinds of dependencies:

1. All your templates
2. All dependencies that contribute to your configuration such as the
`input.css`, any plugins, any `tailwind.config.js` files and so on.

When a template changes, we just have to scan for new Tailwind CSS
classes and emit a new CSS file. But when the `input.css` file, or any
of its dependencies changes, then we want to perform a full rebuild.

The idea is that your `@theme` might have changed, or new plugins have
been added, or old plugins have been removed.

If you have an `input.css` file:
```css
@import "tailwindcss";
@config "./tailwind.config.js";
```

That relies on a custom config: `tailwind.config.js`:
```js
const theme = require('./my-custom-theme.js');
module.exports = {
  theme
}
```

If that file relies on yet another file: `./my-custom-theme.js`, then
changes there should also trigger a full rebuild.

Since we're dealing with JavaScript here, we want to clear the require
cache and rebuild the dependency tree such that another change to any of
these files triggers a full fresh build.

However, if any of those (transitive) dependencies are deleted, then we
will end up in an invalid state. Creating a new compiler will result in
a build error. The compiler won't be able to figure out the entire
dependency tree, and we're stuck.

Once the user fixes the potentially missing dependency, the watchers
will not be watching any of those files because we created a fresh
compiler.

With this PR, we fix that by keeping track of old paths and using those
while we are still in an invalid state. The moment everything is fixed,
a fresh dependency tree is created and everything starts working again
without you having to restart the `@tailwindcss/cli` command.

Fixes: #20113
Closes: #20114
Closes: #20133

## Test plan

- Added an integration test that removes the transitive dependency.
Re-adding that file later will recover the CLI state.
2026-06-01 19:15:40 +02:00
Robin Malfait
6b43b6400a
Canonicalization: limit arbitrary to bare values conversion (#20130)
This PR improves the canonicalization process by limiting the bare
values to a certain amount.

Before this PR, whenever we have an arbitrary value, e.g. `left-[6px]`,
then we prefer to use a bare value instead e.g. `left-1.5`. In most
cases, this makes sense.

However, there are places where this doesn't really make sense
(https://x.com/kettanaito/status/2059987396050268589)

- `left-[99999px]` → `left-24999.75 `

The hard part is to figure out _why_ this feels wrong. The `.75` could
feel wrong, but in the `left-[6px]` → `left-1.5`, the `.5` makes sense.
If we reduce that big number to `left-[99996px]` → `left-24999`, then
there is no floating point but it still feels wrong.

One possibility I can think of is to analyze the incoming value and see
if we find certain patterns. All repeating numbers, fun numbers like
`1337`, common numbers most programmers know such as `720px`, `1280px`,
etc.

But instead of that, I think it's more reasonable to limit the bare
value such that the `px` based value doesn't exceed a big number. We can
improve the logic if there are other cases that don't really make sense.

The biggest value we have in our default theme is `--breakpoint-2xl:
96rem`, which is equivalent to `1536px`.

So I think any bare value that results in a value `<= 1536px` should
probably be fine.

In this case, `left-[99999px]` would stay as `left-[99999px]`, but
`left-[6px]` is still converted to `left-1.5`.

Note: this is only happening for arbitrary values being converted to
bare values _if_ they use the `--spacing` variable internally.

Values such as `z-[99999999999]` will still be converted to
`z-99999999999`, since the intent is still clear.

## Test plan

1. Added new tests for these limitations
2. Other existing tests still pass
2026-05-29 21:31:47 +02:00
Robin Malfait
d4f24c5f16
Fix invalid canonicalization where 0<unit> was migrated to 0 (#20127)
This PR fixes a bug in the canonicalization process when we simplify /
fold declarations that contain `0<unit>` values.

The reason we even try to fold these in the first place is to simplify
values such as `m-[0rem]` to `m-0`. The more values we can
fold/canonicalize, the better we can suggest replacements _if_ they are
the same.

One thing we know in CSS is that if you have a `<length>` type, and that
value is `0<unit>`, then we can safely change that to just `0`.
```css
width: 0rem;
width: 0;    /* `0` is a <length> */
```
However, if this was part of a `calc(…)` (or another CSS math function),
then this could make the calc expression invalid:
- `calc(1rem + 0px)` → `calc(1rem + 0)` — this goes from _valid_ to
_invalid_
At runtime the `1rem` can be converted to a `px` based valued, then
`16px + 0px` makes sense. Adding `0` without unit does not.
- `calc(1rem * 0px)` → `calc(1rem * 0)` — this goes from _invalid_ to
_valid_
At runtime the `1rem` can be converted to a `px` based value, but `16px
* 0px` would result in `0px^2` which doesn't make sense either.

We will still normalize values such as `-0.0rem` to just `0rem`, but not
`0` if we know it's unsafe to do so.

If we end up with top-level `calc(…)` expressions that can be folded,
then we will try to do that:
- `calc(0px * -1)` → `0`
- `calc(calc(0px * -1) + 1rem)` → `calc(0px + 1rem)`
Notice that the inner `calc(…)` was folded to `0px` not `0` because that
would make the `calc(0 + 1rem)` invalid.
Additionally, we could potentially fold the `calc(0px + 1rem)` to just
`1rem`, but we have to make sure that we don't introduce valid values
from invalid values `calc(0s + 1rem)` would be invalid, but folding it
to `1rem` would make it valid which is not good.

Fixes:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1579
2026-05-29 14:43:26 +02:00
Robin Malfait
829cdc930a
duration must be at least 500ms 2026-05-26 23:34:51 +02:00
Robin Malfait
2666194d5c
Improve internal instrumentation module (#20119)
This PR improves some of the internal instrumentation tooling we have.

While working on another branch, I updated the instrumentation tooling
to have a few different ways of measuring what's going on.

Until now, we had an `I.start(label)` and corresponding `I.end(label)`.
While this works, it also means that you have to make sure that you call
`I.end(label)` before every `return` to track things properly.

With this PR, I added a `I.span(label, () => /* some callback*/{})` API
that essentially does that in one go. It also handles promises and
resturns the value that was returned from the callback. This can be
useful in situations where you have a one-liner:
```ts
let css = I.span('toCss(…)', () => toCss(ast))
```

If your callback is longer, then you end up in a situation where you
have to indent your code, and if you want to stop measuring you have to
drop code in 2 places and re-indent:
```diff
- I.span('label', () => {
    …
- })
```

For this situation, I also added a `using _ = I.track(label)` API
instead. This can also be used in any block and automatically inserts
the `I.end(label)` on every exit point. This relies on the new `using`
keyword, but we already relied on that for the instrumentation module.

Last but not least, the constructor accepts a `shouldReport` which
defaults to the `env.DEBUG`. The reason for this change is so that it's
easier to report / not report during development instead of swapping out
an environment variable. Again, this is internal so there is no public
API change happening here.

## Test plan

All tests should still pass.
2026-05-26 23:20:39 +02:00
Robin Malfait
cdfae994ed
Remove unused test (#20117)
This PR cleans up some old stale test that has been skipped from the
beginning.

While this test wants to prove that migrating from Tailwind CSS v3 to
Tailwind CSS v4 can handle the `#{!important}` SCSS notation, it doesn't
prove that we can migrate an entire SCSS project. SCSS has much more
special syntax and we never supported that.

Let's get rid of this test that's doing nothing at the moment.
Especially since we don't want to migrate SCSS projects. Enabling this
might result in the false sense that we _do_ support SCSS migrations
which is not the case.

Closes: #20106
2026-05-26 19:45:00 +02:00
Robin Malfait
749c45ef26
Expose index and siblings on walk context (#20109)
This PR is a small improvement of the current `walk` implementation
where we will expose the `index` and the `siblings` on the current
context.

During a walk, we walk over objects that contain a `nodes: []` field.
The `ctx.parent` that already exists is a reference to the parent node,
but `ctx.siblings` is a reference to the `ctx.parent.nodes`.

The `ctx.index` is the index of the current node we are walking in the
`siblings` array. This way we can prevent the awkward
`ctx.parent?.nodes.indexOf(node)` which is a bit silly because we
already know the nodes we're walking and its index...

The `ctx.parent` can be `null`, but the `ctx.siblings` will never be
`null`, this can be seen in a situation like this:

```ts
let ast: AstNode = [nodeA, nodeB]

walk(ast, (node, ctx) => {
  if (node === nodeA) {
    ctx.parent === null; // Because there is no parent
    ctx.siblings === ast; // Because that's the current list we're looping over

    // Before this PR, we would have to do something like:
    let siblings = ctx.parent?.nodes ?? ast
  }
})
```

In the above example, the `ast` is a separately variable, but if this
was inlined, we would run into some issues:
```ts
walk([nodeA, nodeB], (node, ctx) => {
  if (node === nodeA) {
    ctx.parent === null; // Because there is no parent
    ctx.siblings === ast; // Because that's the current list we're looping over

    // At this point, there is no way to get to the `[nodeA, nodeB]` list
    // without moving it to a variable first.
  }
})
```

So, this PR doesn't change much, the additional information we track is
already known information that is now exposed to the caller of the
`walk` function.

In this PR we did update some usages and got rid of some awkward
`ctx.parent?.nodes ?? []` and `ctx.parent.nodes.indexOf(…)` usages.

## Test plan

- All tests still pass as expected
2026-05-24 22:32:05 +02:00
Jiahan Chen
982e920df8
Add Rspack optional peer dependency for @tailwindcss/webpack (#20027)
## Summary

This PR makes Rspack support explicit for `@tailwindcss/webpack` by
adding `@rspack/core` as an optional peer dependency alongside
`webpack`.

The loader already works through Rspack's webpack-compatible loader API,
as shown in this runnable example:
https://github.com/rstackjs/rstack-examples/tree/main/rspack/tailwindcss.
The README and package description are updated to document that usage.

## Test plan

No Rspack-specific tests were added. The loader implementation is
unchanged, and the existing webpack loader tests exercise the same
loader API path that Rspack uses, which should cover the relevant
behavior.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-05-23 11:14:48 +02:00
Robin Malfait
73983e1cf5
Fix 'Sourcemap is likely to be incorrect' warnings when using @tailwindcss/vite (#20103)
This PR fixes an issue where a bunch of warnings would be shown related
to sourcemaps.

This happens when we are dealing with CSS files that are _not_ Tailwind
CSS roots. In that case, in the `transform` step, we return the `src` of
that module as-is because we didn't modify anything. However, when
nothing changed, you have to return a `NullValue` such as `undefined`.

So this is a stupid little fix, but it should get rid of a bunch of
annoying warnings.

Fixes: #19930

## Test plan

- Added an integration test to mimic the problem
- Other tests still pass
- Tested it against the reproduction provided in #19930


Before:
<img width="1887" height="1763" alt="8oQNG5Lqr2B"
src="https://github.com/user-attachments/assets/2d8af456-4176-4f18-92a3-5327e395ac6b"
/>


After:
<img width="1885" height="1404" alt="8oQND5kWSb4"
src="https://github.com/user-attachments/assets/7dd98ea4-126b-45d4-9411-0afbacd2c797"
/>
2026-05-22 17:57:25 +02:00
Robin Malfait
e0a0c46c85
Cleanup lockfile / dependencies (#20102)
This PR reduces the installed dependencies by cleaning up the
`pnpm-lock.yaml` file.

This also pins `@parcel/watcher` such that the lockfile is generated
properly becauase of the patched dependencies.

This is a follow-up of #19499, but up to date with the latest state of
the repo.

## Test plan

- Lockfile is simpler. Most dependencies stayed the same, and were
published _months_ ago. There are a few cases where we have more recent
published dependencies. There are 7 dependencies that were published in
the last ~24 hours: `node-releases@2.0.46` (10 hours ago),
`electron-to-chromium@1.5.361` (12 hours ago), `semver@7.8.1` (20 hours
ago), `terser@5.48.0` (20 hours ago), `webpack-sources@3.5.0` (5 hours
ago), `vite@8.0.14` (yesterday). All of these but the `terser` version
used OIDC.
- Socket.dev didn't report any issues with the changed dependencies
- All tests still pass

---------

Co-authored-by: James Garbutt <43081j@users.noreply.github.com>
2026-05-22 16:57:46 +02:00
Jordan Brough
a480043a8a
Add --silent option to suppress output in @tailwindcss/cli (#20100)
Edited by: @RobinMalfait 

This PR adds a new `--silent` option to the `@tailwindcss/cli` to
suppress output (except for errors).

## Test plan

- Existing tests pass
- A new integration test for the `--silent` option was added

---

Original:

## Summary

Small change to add a `--quiet` option.

@adamwathan previously said this type of thing [sounded like a good
idea](https://github.com/tailwindlabs/tailwindcss/issues/8050#issuecomment-1100164351):

> [I] have made a note about the --silent option idea which I think
definitely has value 👍🏻

This is helpful to keep logs high signal in some scenarios. For example,
when I want AI to sift through my foreman logs, where "Done in X"
becomes the dominant log message over time when running tailwind
alongside other things.

## Test plan

I've added an integration test.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-05-22 13:03:32 +00:00
Robin Malfait
9f9b7c5c6d
Remove pnpm-lock.yaml from wasm32-wasi (#20101)
This PR removes the `pnpm-lock.yaml` file from one of the sub-packages.
This is unnecessary and causes confusion. Since this is a monorepo, we
can make use of the `pnpm-lock.yaml` file in the root of the project
instead.

Closes #20097
2026-05-22 14:33:17 +02:00
Robin Malfait
8dcdb66e8a
Bump dependencies (#20095)
This PR bumps some of our dependencies, common dependencies were moved
to pnpm's `catalog` feature.

Closes #20092
Closes #20085
Closes #20075
Closes #20066
Closes #20062


## Test plan

- All tests still pass
- Each dependency was published some time ago. Webpack has an even newer
version that was published <15min ago. Will update that one later.

[ci-all]
2026-05-21 17:58:55 +02:00
Robin Malfait
161569a541
Improve GitHub Actions (#20093)
This PR improves GitHub workflows by making sure that:

- Uesed actions are pinned
- We don't persist credentials
- Remove caches from release workflows
- Prevent template expansion when using `env.…` in `run` blocks
- Use the `gh release` instead of `softprops/action-gh-release`
2026-05-21 12:03:39 +02:00
Robin Malfait
d03edefd19
Fix parsing bug in SelectorParser (#20090)
While working on another feature, I noticed that a selector such as
`.foo::before` was parsed as:

```ts
[
  {
    kind: 'compound',
    nodes: [
      { kind: 'selector', value: '.foo' },
      { kind: 'selector', value: ':' },
      { kind: 'selector', value: '::before' },
    ],
  },
]
```

Instead of:
```ts
[
  {
    kind: 'compound',
    nodes: [
      { kind: 'selector', value: '.foo' },
      { kind: 'selector', value: '::before' },
    ],
  },
]
```

So far this hasn't been a real issue in practice, but it is in a
follow-up PR that I'm working on. To keep things separated, I wanted to
fix this behavior in a dedicated PR instead.

## Test plan

1. Added a test case for this situation
2. Other tests still pass
2026-05-20 15:54:07 +02:00