Compare commits

...

665 commits
v4.1.3 ... main

Author SHA1 Message Date
Robin Malfait
fa81d697fe
Improve style invalidation performance of group-* and peer-* variants (#20513)
# For humans, by @RobinMalfait

## TL;DR

Some people ran into performance issues with the current `group-*`
variant because of the `*` inside of the selector. Take the
`group-focus-visible:flex` class for example, this generates:
```css
.group-focus-visible\:flex:is(:where(.group):focus-visible *) {
  display: flex;
}
```

This slightly rewrites the selector by maintaining the same
functionality, but increasing performance because the browser has to do
fewer style recalculations.

That same class now produces:
```css
:is(:where(.group):focus-visible .group-focus-visible\\:flex) {
  display: flex;
}
```

# For AI, generated by AI

This PR changes the selectors generated for `group-*` and `peer-*`
variants so browsers do less style recalculation when a group or peer
changes state (e.g. on focus or hover). Which elements match, and with
what specificity, stays the same, except for one deliberately accepted
edge case involving `@namespace` (see below).

```css
/* Before */
.group-focus\:flex:is(:where(.group):focus *) { display: flex; }
.peer-focus\:flex:is(:where(.peer):focus ~ *) { display: flex; }

/* After */
:is(:where(.group):focus .group-focus\:flex) { display: flex; }
:is(:where(.peer):focus ~ .peer-focus\:flex) { display: flex; }
```

## Why

In the old form, the subject inside `:is(…)` is `*`. When `.group`
changes state, Chromium has to recalculate styles for **every**
descendant of the group (or every following sibling of the peer), not
just the elements that use the utility. With the target itself in that
position, the browser can narrow invalidation down to elements matching
the target.

## Performance

Synthetic benchmark: toggle focus on a group/peer, flush style after
each change, and measure only a non-layout property (`outline-color`) to
isolate invalidation. Apple M5 Pro, macOS arm64.

**Elements recalculated per focus/blur** (Chromium 151,
`UpdateLayoutTree` `elementCount`, including the focused element):

| Scenario                                    | Before |   After |
| ------------------------------------------- | -----: | ------: |
| Group: 100 targets among 10,000 descendants | 10,001 | **101** |
| Peer: 20 targets among 2,000 siblings       |  2,001 |  **21** |

**Median time per focus/blur** (sparse: 1% of elements carry the
utility):

| Engine       | Group: before → after     | Peer: before → after      |
| ------------ | ------------------------- | ------------------------- |
| Chromium 151 | 2.169 → **0.130 ms** (~17×) | 0.635 → **0.192 ms**
(~3.3×) |
| Firefox 153  | 0.500 → 0.350 ms          | 0.650 → 0.600 ms          |
| WebKit 26.5  | 0.750 → 0.750 ms          | 0.450 → 0.450 ms          |

**Dense case** (every element carries the utility): no meaningful
difference in any engine, because every element needs recalculation
anyway. Firefox's dense group case was ~5% slower (2.775 → 2.925 ms).
Everything else was within noise.

| Engine | Group dense: before → after | Peer dense: before → after |
| ------------ | --------------------------- |
-------------------------- |
| Chromium 151 | 4.027 → 3.971 ms | 18.095 → 18.121 ms |
| Firefox 153 | 2.775 → 2.925 ms | 43.900 → 44.025 ms |
| WebKit 26.5 | 7.550 → 7.425 ms | 35.325 → 35.100 ms |

These are micro-benchmarks of style updates, not page-load or frame-rate
numbers. The real-world gain depends on DOM size and how many elements
inside a group/peer use the variant. The biggest win is the common case:
a large group containing only a handful of `group-*` targets.

## Do the selectors behave the same?

Yes. For a group condition `G` and a target `&`:

- **Before:** matches `&` and has an ancestor matching `G`
- **After:** has an ancestor matching `G` and matches `&`

`peer-*` follows the same reasoning with `~`. Details:

- **Specificity is unchanged.** `:is()` takes the specificity of its
argument, so before was `spec(&) + spec(G)` and after is `spec(G) +
spec(&)`.
- **`&` is used, not the utility class**, so `@apply`, `@variant`,
`*:group-*`, `[&_p]:group-*`, and other variants that change the target
keep working. Complex parents such as `.foo .bar { @apply
peer-focus:flex }` keep `:is(…)` semantics during nesting: `:is(P ~
:is(.foo .bar))`.
- **`&` appears only once**, so stacked variants grow the selector
linearly. A unit test with 12 stacked variants guards against
exponential growth.
- **The selectors are also shorter:** 6 bytes of wrapping instead of 7.
- **The outer `:is(…)`** keeps compound variants such as `has-group-*`,
`not-group-*`, and `in-group-*` equivalent. For example, `has-group-*`
can still match when the group sits outside the element carrying the
utility.

**Accepted edge case:** if a stylesheet declares a default `@namespace`,
the old trailing `*` limited matches to elements in that namespace.
Inside compound variants such as `group-group-*` or `has-group-*`, the
new selector no longer does, so an SVG element (e.g. inside
`foreignObject`) can now count as the inner group. Appending `:is(*)` to
the target would restore the old behavior with no performance cost, but
it makes every selector longer for a combination (`@namespace` + mixed
namespaces + compound group variants) that is very unlikely in practice.
We can add it back if anyone runs into this.

There's one known browser quirk this PR doesn't change: Chromium doesn't
invalidate `has-group-*` when the focused group is an ancestor *outside*
the element. That happens with both the old and new selectors.

## Test plan

- [x] Updated unit test snapshots for the new selector shape
- [x] New unit test that bounds selector size with 12 stacked variants
- [x] New browser tests in `packages/tailwindcss/tests/ui.spec.ts`, run
in Chromium, Firefox, and WebKit. They cover `group-*`/`peer-*` focus
and blur, `@apply` inside a complex selector, specificity, stacked
groups in either order, and compound `group-peer-*`/`peer-group-*`. The
pre-PR selectors pass all of them too, so behavior is unchanged
- [x] `pnpm run test` and `pnpm run test:ui` pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 21:32:41 +02:00
Dex
9798a8ab5b
fix: correct two comment typos (#20507)
Fixes two typos in code comments (no behavior affected):

- `packages/@tailwindcss-cli/src/commands/build/index.ts`: `Succesfully`
-> `Successfully`
- `packages/tailwindcss/src/candidate.ts`: `preceeded` -> `preceded`

Co-authored-by: Dextheking1 <4175097+Dextheking1@users.noreply.github.com>
2026-09-25 11:28:27 +00:00
한국
722acdfebf
Don't treat \ as an escape inside CSS comments (#20508)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

The CSS parser treated `\` inside comments as an escape and skipped the
character after it. Because of that, a comment ending in `\*/` was never
closed, and the CSS that followed was swallowed or turned into a broken
rule:

```css
/* C:\temp\*/
.a { color: red }
```

Before this change the `.a` rule disappeared entirely, and `/* \*/ .a {
color: red }` produced the selector `* \*/ .a`. The same thing happened
to comments inside declaration values, where the comment ran on until
the next `*/` it could find, pulling following declarations into the
value.

Per [CSS Syntax Level 3
§4.3.2](https://www.w3.org/TR/css-syntax-3/#consume-comment), a comment
ends at the first `*/` and escapes aren't processed inside comments.
This removes the backslash handling from both comment-scanning loops in
`css-parser.ts` (top-level and inside declaration values).

The existing test `/*Hello, \*\/ world!*/` keeps passing, since that
input contains no `*/` before the final one.

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

Added two tests to `packages/tailwindcss/src/css-parser.test.ts` (both
run with Unix and Windows line endings):

- a top-level comment ending in `\*/` followed by a rule
- a comment ending in `\*/` inside a custom property value, followed by
another declaration

Both fail without the change to `css-parser.ts` and pass with it.

```sh
pnpm vitest run packages/tailwindcss/src/css-parser.test.ts
pnpm vitest run --project tailwindcss
pnpm run lint
```

---------

Co-authored-by: Robin Malfait <robin.malfait@shopify.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-09-25 11:16:58 +00:00
Alexandre Kohler
86f3daf912
Sort breakpoint variants with decimal values numerically (#20512)
## Summary

`compareBreakpoints` compared values in the same unit with `parseInt`,
which drops the fractional part, so `40.25rem` and `40.5rem` were
treated as equal. As a result `min-[40.5rem]:*` could be emitted before
`min-[40.25rem]:*` (the same happened for `@min-*` container queries),
which means the smaller breakpoint wins in the cascade. This switches
the comparison to `parseFloat` so decimal values are ordered
numerically; values that were already sorted correctly are unaffected.

## Test plan

- Added a test to `packages/tailwindcss/src/variants.test.ts` that sorts
`min-[40.25rem]`, `min-[40.5rem]`, `max-[40.25rem]` and `max-[40.5rem]`.
It fails without the change (the `min-[40.5rem]` rule is emitted before
`min-[40.25rem]`) and passes with it.
- `vitest run src/variants.test.ts -t "decimal values"` (in
`packages/tailwindcss`)
- `vitest run` (in `packages/tailwindcss`): 42 files, 5009 tests passed
- `prettier --check` on the changed files
2026-09-25 11:07:37 +00:00
cui fliter
41d9cae8e5
Reject candidates with multiple modifiers (#20466)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

`segment()` preserves empty top-level segments, but the candidate and
variant parsers currently use the truthiness of the third segment to
detect additional modifiers.

As a result, inputs such as `bg-red-500/50/`, `bg-red-500/50//foo`,
`group-hover/foo/:flex`, and `group-hover/foo//bar:flex` can be parsed
as valid candidates even though they contain multiple slash modifier
segments.

This change checks the number of segments instead of the value of the
third segment. Single modifiers such as `bg-red-500/50` and
`group-hover/foo:flex` continue to parse normally, while all additional
top-level `/` segments are rejected.


## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

- `pnpm exec vitest run packages/tailwindcss/src/candidate.test.ts
--hideSkippedTests`
- `pnpm exec vitest run packages/tailwindcss/src --hideSkippedTests`
- `pnpm exec prettier --check packages/tailwindcss/src/candidate.ts
packages/tailwindcss/src/candidate.test.ts`

`pnpm --filter=tailwindcss lint` was also attempted, but currently fails
on existing cross-package dependency and Bun type errors unrelated to
this change.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-09-08 13:46:59 +00:00
Wei
f723e834ad
Don't warn about Angular's ::ng-deep and :host-context() when optimizing CSS (#20434)
Fixes #20433.

The warning filter in `optimize.ts` already ignores `:deep()`,
`:slotted()` and `:global()`. Angular's two deep selectors are the same
kind of thing — non-standard pseudo-selectors that the framework's
compiler resolves before the CSS reaches a browser — but they aren't
covered, so every Angular component stylesheet using them prints a
warning block per occurrence.

```
Found 2 warnings while optimizing generated CSS:

Issue #1:
│ :host ::ng-deep .some-child, :host
┆        ^-- 'ng-deep' is not recognized as a valid pseudo-element. Did you mean ':ng-deep' (pseudo-class) or is this a typo?
```

Angular strips both during view-encapsulation shimming — `::ng-deep` via
`_shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g`, and
`:host-context()` in the same pass — so neither ever reaches a browser.

Worth noting that `/deep/` and `>>>`, Angular's two other spellings of
the deep selector, already pass silently because
`nonStandard.deepSelectorCombinator` is enabled. `::ng-deep` is the only
one that warns, and it's the spelling the Angular docs use — so in
practice every Angular codebase hits this. On the workspace where I ran
into it (7 Angular apps), a production build printed 425 `ng-deep`
warnings and 2 `host-context` ones.

## Test plan

There's no automated coverage here because the warning path is behind
`process.env.NODE_ENV !== 'test'`, so a spy sees nothing under Vitest
regardless of the filter — the same reason #20277 shipped without one.
Instead I ran the file before and after the change against Lightning CSS
1.33.0 directly, counting emitted warning blocks:

| Input | Before | After |
| --- | --- | --- |
| `:host ::ng-deep .a, :host ::ng-deep .b { … }` | 1 | 0 |
| `:host-context(.dark) .a { … }` | 1 | 0 |
| `:deep(.a) { … }` | 0 | 0 |
| `.a::totally-not-real { … }` — genuine typo | 1 | **1** |

Generated CSS is byte-identical before and after; only the warning is
suppressed. Genuine unknown pseudo-selectors still warn, so the typo
hint the message exists for is preserved.

Happy to restructure this if you'd prefer the predicate extracted so it
can be unit-tested, or to split `ng-deep` and `host-context` into their
own block rather than extending the existing regex.

A minimal reproduction of the original issue is at
https://gist.github.com/weilinzung/ace42ceb95c2f47b6747cc1f34f162bd.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-31 10:14:54 +00:00
Simon Bobrov
9f451eead8
Cold start performance improvements (#20427)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

Hi guys, thanks for you great work!

I work on performance improvements on
https://github.com/schoero/eslint-plugin-better-tailwindcss project.
Part of issues can be fixed on the tailwind side only.

This is a first fix, I have a bigger one in mind, it would require a
small additional public API method - out of scope of this PR.

Please let me know what you think.

## Finding

`getVariantOrder()` re-sorts all parsed variants with the (expensive)
variant comparator on every call, and it is called by every
compileCandidates() invocation — per build pass, per @apply
substitution, and hundreds of times during candidate canonicalization
via the variant signature caches. Since parsedVariants is append-only,
the computed order only changes when a new variant is parsed, so we can
cache the result and invalidate on parsedVariants.size. This makes cold
canonicalization (IntelliSense, lint plugins) faster and removes
repeated sorting from @apply substitution and incremental rebuilds.

## Test plan

Tested at https://github.com/smnbbrv/better-tailwindcss-bench . The
relevant part is the `epbt-now / tw-patched`

**plugin cost** (ms, cold run net of parse baseline)

| codebase | epbt-now / tw-now | epbt-patched / tw-now | epbt-now /
tw-patched | epbt-patched / tw-patched |
| ---------- | ----------------: | --------------------: |
--------------------: | ------------------------: |
| mixed | 6292 | 3965 (-37%) | 5395 (-14%) | 3595 (-43%) |
| repetitive | 5279 | 2425 (-54%) | 4819 (-9%) | 2124 (-60%) |
| unique | 6096 | 5458 (-10%) | 5507 (-10%) | 5295 (-13%) |
2026-08-31 10:05:03 +00:00
Srinu desetti
90f8ff41c8
Don't space out and/or/not inside function calls in supports-[…] variants (#20420)
## Summary

The `supports-[…]` variant works around a Chrome bug where `@supports
(a)or(b)`
is invalid by spacing out the `and`, `or`, and `not` keywords. However,
the
replacement is applied to the entire value, including the inside of
function
calls, where these words can be part of a selector.

For example, `supports-[selector(a:not(.foo))]:flex` generates:

```css
@supports selector(a: not (.foo))
```

The selector `a: not (.foo)` is unparsable, so a condition that is true
in
every browser silently becomes false and the utility never applies. The
same
happens to class names like `.and` or `.or` inside `selector(…)`.

This PR only spaces out the keywords at the condition level: parens
preceded by
an identifier (other than the keywords themselves) start a function
call, and
everything inside is left as-is. The Chrome workaround still applies to
the
condition itself, e.g. `supports-[(display:grid)or(display:flex)]` still
becomes `@supports (display: grid) or (display: flex)`.

## Test plan

- Added a test covering `selector(a:not(.foo))`, class names
`.and`/`.or`
inside `selector(…)`, the Chrome `(a)or(b)` workaround, and a top-level
  `not(…)` condition.
- `pnpm vitest run packages/tailwindcss/src/variants.test.ts` — 102
passed.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-14 19:43:33 +00:00
Srinu desetti
7a7f386648
Don't emit utilities that resolve a theme value when an unsupported modifier is used (#20419)
## Summary

Modifiers on functional utilities that resolve a named theme value are
silently
dropped instead of invalidating the candidate. For example, with the
default theme:

- `rounded-sm/[5]` emits the same CSS as `rounded-sm` (the `[5]` is
ignored)
- `shadow-sm/foo`, `inset-shadow-sm/foo`, `text-shadow/foo`, and
`drop-shadow/foo`
  emit the full shadow CSS with the invalid `foo` modifier ignored

This is inconsistent with how every sibling code path behaves:
`rounded/foo`,
`rounded-[4px]/foo`, `rounded-sm/5`, and `drop-shadow-xl/foo` all
correctly
produce no output, because those paths check `candidate.modifier`.

This PR adds the missing guards:

- In the generic `functionalUtility` handler, a candidate whose named
value
resolves from the theme now rejects modifiers (except fractions like
`w-1/2`,
  where the modifier is part of the resolved value).
- The `shadow`, `inset-shadow`, and `text-shadow` utilities now apply
the same
`if (candidate.modifier && !alpha) return` guard in their default-value,
arbitrary-value, and named-size branches that `drop-shadow` already
applies,
  and `drop-shadow` gets it in its default-value branch too.

Existing tests asserting candidates like `drop-shadow/foo` produce no
output were
passing for the wrong reason: they run without a theme, so the theme
lookup fails
before the modifier is ever considered. The new tests provide a theme so
the
invalid modifier is what invalidates the candidate.

## Test plan

- Added assertions to the `rounded`, `filter`, `shadow`, `inset-shadow`,
and
`text-shadow` tests that compile candidates with invalid modifiers
against a
theme that defines the relevant values, and expect no output. All of
them fail
  without the fix.
- `pnpm vitest run packages/tailwindcss/src/utilities.test.ts` — 398
passed.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-14 16:47:34 +00:00
Robin Malfait
021b7fe6f2
Canonicalization: prevent inlining CSS-wide keywords (#20417)
This PR fixes an issue where canonicalization suggestions in
intellisense result in 'weird' suggestions.

```
The class text-foreground/60 can be written as text-default-soft-hover
```

If we look at the CSS provided by the issue, this doesn't immediately
make sense:
```css
@theme {
  --color-foreground: var(--foreground);
  --color-default-soft-hover: color-mix(in oklab, var(--default) 60%, transparent);
}

:root {
  --foreground: oklch(0.2103 0.0059 285.89); /* near-black */
  --default: oklch(94% 0.001 286.375); /* light gray */
}
```

But it turns out that when you use Uniwind (React Native) with HeroUI,
that the setup looks more like this:
```css
@import 'tailwindcss';

@theme {
  --foreground: unset;
  --default: unset;
}

@theme inline {
  --color-foreground: var(--foreground);
  --color-default-soft-hover: color-mix(in oklab, var(--default) 60%, transparent);
}
```

During the canonicalization step, we inline all the `@theme` values, the
reason for this is that `text-[#fff]` can be turned into `text-white`
even though they look slightly different:
```css
.text-\[\#fff\] {
  color: #fff;
}
.text-white {
  color: var(--color-white, #fff);
}
```

But when we inline them, it looks like this:
```css
.text-\[\#fff\] {
  color: #fff;
}
.text-white {
  color: #fff;
}
```

Internally, we use signatures to make sure that they are safe to be
subtituted with eachother. In this case, the signatures will look like
this:
```css
.x {
  color: #fff;
}
.x {
  color: #fff;
}
```

If we now look at the signatures of the original issue, you would see:
```css
/* text-foreground/60 */
.x {
  color: color-mix(in_oklab,unset_60%,transparent);
}

/* text-default-soft-hover */
.x {
  color: color-mix(in_oklab,unset_60%,transparent);
}
```

That's because the `@theme` variables were inlined, resulting in the
exact same signature, thus we consider them the same.

This PR makes sure to never inline [CSS-wide
keywords](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/Data_types#css-wide_keywords)
(such as `unset`) and therefore keeping the CSS variable reference:
```css
/* text-foreground/60 */
.x {
  color: color-mix(in_oklab,var(--foreground)_60%,transparent);
}

/* text-default-soft-hover */
.x {
  color: color-mix(in_oklab,var(--default)_60%,transparent);
}
```

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

## Test plan

1. Existing tests pass
2. Added a regression test

Checked manually in the regression repo

Before:
<img width="1358" height="421" alt="image"
src="https://github.com/user-attachments/assets/8ab3de97-17e4-4e11-b335-bb37218ff2ee"
/>

After:
<img width="1177" height="247" alt="image"
src="https://github.com/user-attachments/assets/cbbfcf04-3ed3-4fa7-a3eb-ef607239f4ad"
/>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-08-14 12:16:00 +02:00
Robin Malfait
f7f58f0806
Move debug logs in .tailwindcss folder (#20416)
Instead of writing debug logs (triggered by using `DEBUG=*`) in the root
of the project as `tailwindcss-<pid>.log`, we will write them to
`.tailwindcss/logs/scanner-<timestamp>-<pid>.log` instead.

The reasoning for this is that we won't pollute the root of the project.
Another reason is that if you don't ignore `.log` files via
`.gitignore`, it could be that you end up in a loop if you use `DEBUG=*
vite` because a new `.log` file might trigger a new build. We noticed
this while looking at
https://github.com/tailwindlabs/tailwindcss/discussions/20382.

The `.tailwindcss` folder will come with a `.gitignore` file that
ignores everything (`*`).

## Test plan

1. Nothing really changed here, all tests should pass


Testing this in a local project, it looks like this:
<img width="483" height="165"
alt="file-35bfc43606c556f2380a161c7595d103"
src="https://github.com/user-attachments/assets/629519c8-8f2b-46ee-8046-9ba3749f319a"
/>
2026-08-13 23:16:30 +02:00
Robin Malfait
de9e71c599
fix referenced link 2026-08-13 21:31:51 +02:00
Robin Malfait
00ef99df3d
Do not force full page reloads when using @tailwindcss/vite (#20414)
This PR removes all of the custom HMR handling we had in the
`@tailwindcss/vite` plugin.

When Vite 7.1 was introduced, Vite stopped performing a full page reload
for unknown files and instead started performing normal `hmr` updates.
This resulted in this issue:
https://github.com/tailwindlabs/tailwindcss/issues/19637

At the time, it felt like something we could easily re-add: if a file is
not covered by Vite, we can perform a `full-reload`. This meant that a
`.php` file would trigger a full page reload as expected.

The reason the `.php` file triggered Vite in the first place is because
those files were scanned by us (`@tailwindcss/vite`) so it made sense.

However, this then resulted in a plethora of issues, and it feels a bit
like a game of whac-a-mole.

- https://github.com/tailwindlabs/tailwindcss/issues/19744
- https://github.com/tailwindlabs/tailwindcss/issues/19903
- https://github.com/tailwindlabs/tailwindcss/issues/20320
- https://github.com/tailwindlabs/tailwindcss/issues/20378
- https://github.com/tailwindlabs/tailwindcss/issues/20411

Fixes: #19744
Fixes: #19903
Fixes: #20320
Fixes: #20378
Fixes: #20411

We kept updating the logic by safelisting certain extensions, checking
different servers and/or environments, handling the fact that `server`
in the callback could be absent in `experimental.bundledDev` mode, etc.
etc.

Now, when investigating the last issue
(https://github.com/tailwindlabs/tailwindcss/issues/20411), I can
trigger full reloads by changing `.json`, `.yaml` or `.svg` files. This
makes sense since they aren't handled by default.

So thinking about this more, I think it's just not Tailwind's
responsibility to tell Vite to reload the browser or not. Yes, we use
the `addWatchFile` API, so files are being watched because of us.
However, our only goal is to update the `.css` file (and HMR that).

This means that we can just drop all the custom HMR handling we have in
`@tailwindcss/vite`.

This also means that
https://github.com/tailwindlabs/tailwindcss/issues/19637 would regress
and won't trigger full page reloads. But this can be easily handled by a
plugin responsible for this behavior:

- https://github.com/ElMassimo/vite-plugin-full-reload

## Test plan

1. All tests pass
2. Manually tested and changing unknown files don't result in a full
page reload
2026-08-13 17:00:08 +02:00
Matt Van Horn
b9286a7346
fix: Drop invalid UTF-8 scanner candidates (#20389)
## Summary

Replace the three unchecked scanner conversions in
`crates/oxide/src/scanner/mod.rs` with checked conversions that omit
only extracted slices that are not valid UTF-8. Apply the check in the
shared `extract` pipeline so initial scans, incremental `scan_content`
calls, and CSS-variable extraction cannot insert invalid strings into
scanner state, and apply the same policy to both branches of
`get_candidates_with_positions` while preserving byte offsets and the
legacy `-[]` restoration. Keep the extractor's byte-oriented CSS
identifier classification unchanged: accepting non-ASCII bytes during
extraction is useful for valid multibyte code points, while the
conversion boundary is the authoritative place to enforce the `String`
contract.

The scanner currently converts extracted byte slices with unchecked
UTF-8 constructors at the shared extraction boundary and both
candidate-with-position branches. A source file containing a stray
continuation byte can therefore produce an invalid `String`, violating
Rust's string invariant and allowing corrupted candidates to persist in
a long-lived scanner. The thread provides a deterministic reproduction
using invalid bytes inside an arbitrary value, so the problem no longer
depends on reproducing the originally reported Turbopack race. Valid
candidates found alongside malformed byte sequences must continue to be
returned normally.

Fixes #20368

## Test plan

Not applicable to this change.

AI was used for assistance.

---------

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-13 13:01:13 +02:00
Robin Malfait
84bea42b6c
Visualize scanner related tests (#20412)
This PR improves the scanner related tests a bit. Right now all the
scanner tests (a lot of them) are somewhat hard to reason about and to
figure out whether these are actually correct or not.

With this PR, the existing are extended with a visualization of the file
system tree, but with indicators to know whether or not a file was
scanned or not. If a `.gitignore` is present, it's content is also shown
to make it easier to see why some files are included or not included.

One of the outputs look like this:
```
.
├── ✗ .gitignore
│       src/ignored-by-gitignore.html
└── ✓ src
    ├── ✗ ignore-by-extension.bin
    ├── ✓ ignored-by-gitignore.html
    ├── ✗ ignored-by-source-not.html
    ├── ✓ keep-me.html
    └── ✓ new-file.html
```

I'm sure we can cleanup a lot of these tests, simplify, deduplicate, but
I didn't want to do that as part of this PR. This PR is purely adding
this visualization.

This uses the [insta.rs](https://insta.rs) library for (inline) snapshot
tests. If at some point the snapshots change, then you get a nice UI:
<img width="958" height="670" alt="image"
src="https://github.com/user-attachments/assets/652cf48f-e1b1-4fe5-a42a-29c5a888063a"
/>


## Test plan

1. All tests should pass [ci-all]
2. Nothing in the code itself changed, so nothing should break

---

<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
2026-08-13 12:45:40 +02:00
Robin Malfait
606438c6c3
rebase ignore crate from 0.4.24 to 0.4.33 (#20409)
This PR updates the vendored `ignore/` crate to make use of the upstream
improvements.

We can't fully use the native `ignore` crate yet because of the missing
functionality that we added ourselves. Our own changes are still marked
as `CHANGED:`

## Test plan

1. All tests should still pass [ci-all]

---

<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
2026-08-13 12:45:40 +02:00
Syed Sohail Hussain
8ac18c7e72
test: fix 'with with' typo in css parser tests (#20410)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2026-08-13 11:05:30 +02:00
Marick van Tuil
b86a6e0a0d
Fix slow Vite rebuilds in projects with large gitignored directories (#20408)
Preface: For full transparency, I used AI (Claude) to help dig into this
issue we're having and write some of the explanation below.

Our local Laravel application (with Docker) takes between 2-6s seconds
(re)building `app.css` after each Blade file update. After checking with
Claude, there seems to be some potential unnecessary traversing of big
(gitignored) directories, specifically `vendor` and `storage`.

As far as I understand it, two scans happen in tailwindcss:

- a first scan, the content scan. This one reads the project files
(auto-detect). It respects .gitignore, so it correctly skips vendor/ and
storage/.

- a second scan, the glob resolution (resolve_globs() in the oxide
crate). It builds watch patterns that the Vite plugin registers, so a
file save can trigger a CSS rebuild. It walks the project again, but
this walk does not respect .gitignore. It only skips a hardcoded list of
directory names from `ignored-content-dirs.txt` (node_modules, .git,
venv, ...). vendor/ and storage/ are not on that list, so it descends
into them and stats every file. These directories can never produce a
watch pattern, because the content scan never visited them. That work is
perhaps wasted?

Maybe why this went unnoticed: on a native filesystem a stat takes about
1µs, so 57k of them is around 0.06s. A Docker bind mount multiplies that
by roughly 100. And the big directory in a typical JS project is
`node_modules`, which the name list already catches; `vendor/` is the
PHP ecosystem's `node_modules`, and it is not on that list. (#19616 /
#19632 fixed the same problem for `scanner.files`; the CLI never reads
`scanner.globs`, so it was never affected.)

The fix: the second walk now skips any directory the scan never visited,
the same pruning the first walk (line 73) already does.

Measured on our local project: `get_globs()` after `scan()` went from
9.9s to somewhere between 0.2 and 1.0s across runs, with identical
output (35 globs). A template save now reloads in about 0.3s instead of
4.

## Test plan

- `cargo test` in `crates/oxide`: 66 tests pass, including the glob
assertions for nested and gitignored directories in `tests/scanner.rs`.
- Compared the sorted `get_globs()` output before and after on the
project above: identical.

I've also set up a repo that reproduces the issue in a fresh Laravel
app:

https://github.com/marickvantuil/tailwind-scan-reproduce

The results:

| Environment | vendor/ | scan | get_globs | syscalls under vendor/ |
| --- | --- | --- | --- | --- |
| GitHub Actions (native FS) | present | 5.0 ms | 48.8 ms | 11,410 |
| GitHub Actions (native FS) | absent | 4.5 ms | 0.9 ms | n/a |
| macOS, Docker bind mount | present | 15.8 ms | 599.4 ms | 11,410 |
| macOS, Docker bind mount | absent | 16.5 ms | 6.5 ms | n/a |

---------

Co-authored-by: Marick van Tuil <marick@dolphiq.nl>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-12 14:31:47 +00:00
Ridwan Olanrewaju
16e94cbf7f
Make createCompatConfig theme mappings safe to spread when a namespace returns a string (#20399)
When building the legacy config compat layer, namespace mappings like
`fontSize` → `text`, `boxShadow` → `shadow`, `animation` → `animate`,
etc. spread the result of `theme(namespace, {})` directly:

```ts
...(theme('text', {}) ?? {})
```

Some of these namespaces can return a **string** (e.g. `theme('text',
{})` → `'1rem'`). Spreading a string produces char-indexed keys (`{ '0':
'1', '1': 'r', ... }`) instead of a `{ DEFAULT: ... }` entry, silently
corrupting the compat config output.

This PR adds a small `spreadTheme` helper that normalizes the return
value before spreading:

- `string` → `{ DEFAULT: value }`
- plain object → `{ ...value }`
- anything else → `{}`

and updates all namespace mappings to use it. Includes unit tests for
the helper plus integration tests for the affected namespaces and the
string-return regression cases.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-10 15:22:33 +00:00
Robin Malfait
46df7ee2fc
Don't scan ignored folders using .gitignore safelist setup (#20397)
This PR fixes an issue where a folder containing a `.gitignore` that
uses a whitelist setup results in too many folders being scanned:

```gitignore
# .gitignore
/*
!/app
```

We want to make sure that we respect the `.gitignore` file, but we also
need to make sure that `@source` directives 'win' from `.gitignore`
files. For example:

```css
@import 'tailwindcss';
@source "./node_modules/my-package/*.js";
```

In this case, `node_modules` is likely ignored via `.gitignore`, but you
_do_ want to include the files from the `my-package` folder, essentially
overriding the `.gitignore` rules.

To do this, we internally mark this folder as an `External` folder such
that it gets read, because that's what we want.

The current bug has a pattern like `/*` and then safelist/whitelist
patterns to include certain folders. The problem with the `/*` pattern
is that this convert the _current_ folder to an `External` folder. This
is wrong. The current folder cannot be ignored by the current
`.gitignore` file itself. You can ignore files and folders _inside_ the
current folder, but not the folder itself. The current folder can only
be ignored by parent `.gitignore` rules.

Originally discussed in:
https://github.com/tailwindlabs/tailwindcss/discussions/20382
Fixes: #20385

## Test plan

1. Added regression tests
2. Added integration tests

Manually tested, and logging the scanned files does result in some
interesting results:


<details>

<summary>Before</summary>

```
Reading ".git/COMMIT_EDITMSG"
Reading ".git/HEAD"
Reading ".git/MERGE_RR"
Reading ".git/config"
Reading ".git/description"
Reading ".git/hooks/applypatch-msg.sample"
Reading ".git/hooks/commit-msg.sample"
Reading ".git/hooks/fsmonitor-watchman.sample"
Reading ".git/hooks/post-update.sample"
Reading ".git/hooks/pre-applypatch.sample"
Reading ".git/hooks/pre-commit.sample"
Reading ".git/hooks/pre-merge-commit.sample"
Reading ".git/hooks/pre-push.sample"
Reading ".git/hooks/pre-rebase.sample"
Reading ".git/hooks/pre-receive.sample"
Reading ".git/hooks/prepare-commit-msg.sample"
Reading ".git/hooks/push-to-checkout.sample"
Reading ".git/hooks/sendemail-validate.sample"
Reading ".git/hooks/update.sample"
Reading ".git/index"
Reading ".git/info/exclude"
Reading ".git/logs/HEAD"
Reading ".git/logs/refs/heads/main"
Reading ".git/objects/02/2fe3aa1b28300c0146acdff4eb3240cd2a861b"
Reading ".git/objects/17/45509b3ec34de9d8ddd6708c9d73f191c63c48"
Reading ".git/objects/1e/d83f044a80cfec0843a0d68fa859c101e4e02f"
Reading ".git/objects/60/76888f3ae7ecd2712c236933314e1c420e52f2"
Reading ".git/objects/76/e5d55dc7d51e47bff78b27e39a13110e17c072"
Reading ".git/objects/89/7b4529b9a3db489cc00e2cff4bbd1a2f09fb50"
Reading ".git/objects/8c/781ce3374b53455c284bef51bffbee5ca88969"
Reading ".git/objects/b2/ceaa157eef0b2a8d0231a1396b17684da71de7"
Reading ".git/objects/da/7ef6439d0779bae07668b42d77dcbce2f84111"
Reading ".git/refs/heads/main"
Reading ".gitattributes"
Reading ".gitignore"
Reading "app/index.html"
Reading "build/index.html"
Reading "node_modules/.modules.yaml"
Reading "node_modules/.package-map.json"
Reading "node_modules/.pnpm-workspace-state-v1.json"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/README.md"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/package.json"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/src/strings.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/README.md"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/package.json"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/binary-search.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/by-source.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/flatten-map.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/resolve.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/sort.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/sourcemap-segment.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/strip-filename.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/trace-mapping.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/src/types.ts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/binary-search.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/binary-search.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/binary-search.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/binary-search.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/by-source.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/by-source.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/by-source.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/by-source.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/resolve.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/resolve.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/resolve.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/resolve.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sort.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sort.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sort.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sort.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/types.d.cts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/types.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/types.d.mts"
Reading "node_modules/.pnpm/@jridgewell+gen-mapping@0.3.13/node_modules/@jridgewell/trace-mapping/types/types.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/README.md"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.mjs"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.umd.js"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/types/gen-mapping.d.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/types/set-array.d.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/types/sourcemap-segment.d.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/dist/types/types.d.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/package.json"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/src/gen-mapping.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/src/set-array.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/src/sourcemap-segment.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/src/types.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/set-array.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/set-array.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/set-array.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/set-array.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/types.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/types.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/types.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/gen-mapping/types/types.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/README.md"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/dist/remapping.mjs"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/dist/remapping.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/dist/remapping.umd.js"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/dist/remapping.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/package.json"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/src/build-source-map-tree.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/src/remapping.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/src/source-map-tree.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/src/source-map.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/src/types.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/remapping.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/remapping.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/remapping.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/remapping.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map-tree.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map-tree.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map-tree.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map-tree.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/source-map.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/types.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/types.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/types.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/remapping/types/types.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/README.md"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/package.json"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/binary-search.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/by-source.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/flatten-map.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/resolve.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/sort.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/sourcemap-segment.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/strip-filename.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/trace-mapping.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/src/types.ts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/binary-search.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/binary-search.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/binary-search.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/binary-search.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/by-source.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/by-source.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/by-source.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/by-source.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/resolve.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/resolve.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/resolve.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/resolve.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sort.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sort.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sort.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sort.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/types.d.cts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/types.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/types.d.mts"
Reading "node_modules/.pnpm/@jridgewell+remapping@2.3.5/node_modules/@jridgewell/trace-mapping/types/types.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/README.md"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/dist/types/resolve-uri.d.ts"
Reading "node_modules/.pnpm/@jridgewell+resolve-uri@3.1.2/node_modules/@jridgewell/resolve-uri/package.json"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/README.md"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/dist/types/resolve-uri.d.ts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/resolve-uri/package.json"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/LICENSE"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/README.md"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/package.json"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/src/strings.ts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts"
Reading "node_modules/.pnpm/@jridgewell+trace-mapping@0.3.31/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map"
Reading "node_modules/.pnpm/@parcel+watcher-darwin-arm64@2.6.0/node_modules/@parcel/watcher-darwin-arm64/LICENSE"
Reading "node_modules/.pnpm/@parcel+watcher-darwin-arm64@2.6.0/node_modules/@parcel/watcher-darwin-arm64/README.md"
Reading "node_modules/.pnpm/@parcel+watcher-darwin-arm64@2.6.0/node_modules/@parcel/watcher-darwin-arm64/package.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher-darwin-arm64/LICENSE"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher-darwin-arm64/README.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher-darwin-arm64/package.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/LICENSE"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/README.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/binding.gyp"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/index.d.ts"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/index.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/index.js.flow"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/package.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/scripts/build-from-source.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Backend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Backend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Debounce.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Debounce.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/DirTree.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/DirTree.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Event.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Glob.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Glob.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/PromiseRunner.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Signal.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Watcher.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/Watcher.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/binding.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/linux/InotifyBackend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/linux/InotifyBackend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/macos/FSEventsBackend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/macos/FSEventsBackend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/shared/BruteForceBackend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/shared/BruteForceBackend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/unix/fts.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/unix/legacy.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/wasm/WasmBackend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/wasm/WasmBackend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/wasm/include.h"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/watchman/BSER.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/watchman/BSER.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/watchman/IPC.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/windows/WindowsBackend.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/windows/WindowsBackend.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/windows/win_utils.cc"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/src/windows/win_utils.hh"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/@parcel/watcher/wrapper.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/LICENSE"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/README.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/index.d.ts"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/lib/detect-libc.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/lib/elf.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/lib/filesystem.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/lib/process.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/detect-libc/package.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/is-glob/LICENSE"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/is-glob/README.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/is-glob/index.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/is-glob/package.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/LICENSE.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/README.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/common.gypi"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/except.gypi"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/index.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/napi-inl.deprecated.h"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/napi-inl.h"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/napi.h"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/node_addon_api.gyp"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/node_api.gyp"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/noexcept.gypi"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/nothing.c"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/package-support.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/package.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/tools/README.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/tools/check-napi.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/tools/clang-format.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/tools/conversion.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/node-addon-api/tools/eslint-format.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/LICENSE"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/README.md"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/index.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/lib/constants.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/lib/parse.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/lib/picomatch.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/lib/scan.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/lib/utils.js"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/package.json"
Reading "node_modules/.pnpm/@parcel+watcher@2.6.0/node_modules/picomatch/posix.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/graceful-fs/LICENSE"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/graceful-fs/README.md"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/graceful-fs/clone.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/graceful-fs/graceful-fs.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/graceful-fs/legacy-streams.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/graceful-fs/package.json"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/graceful-fs/polyfills.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/LICENSE"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/README.md"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/AsyncParallelBailHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/AsyncParallelHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/AsyncSeriesBailHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/AsyncSeriesHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/AsyncSeriesLoopHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/AsyncSeriesWaterfallHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/Hook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/HookCodeFactory.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/HookMap.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/MultiHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/SyncBailHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/SyncHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/SyncLoopHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/SyncWaterfallHook.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/index.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/lib/util-browser.js"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/package.json"
Reading "node_modules/.pnpm/enhanced-resolve@5.24.5/node_modules/tapable/tapable.d.ts"
Reading "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/LICENSE"
Reading "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/README.md"
Reading "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/clone.js"
Reading "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/graceful-fs.js"
Reading "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/legacy-streams.js"
Reading "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/package.json"
Reading "node_modules/.pnpm/graceful-fs@4.2.11/node_modules/graceful-fs/polyfills.js"
Reading "node_modules/.pnpm/is-extglob@2.1.1/node_modules/is-extglob/LICENSE"
Reading "node_modules/.pnpm/is-extglob@2.1.1/node_modules/is-extglob/README.md"
Reading "node_modules/.pnpm/is-extglob@2.1.1/node_modules/is-extglob/index.js"
Reading "node_modules/.pnpm/is-extglob@2.1.1/node_modules/is-extglob/package.json"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-extglob/LICENSE"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-extglob/README.md"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-extglob/index.js"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-extglob/package.json"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-glob/LICENSE"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-glob/README.md"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-glob/index.js"
Reading "node_modules/.pnpm/is-glob@4.0.3/node_modules/is-glob/package.json"
Reading "node_modules/.pnpm/lightningcss-darwin-arm64@1.33.0/node_modules/lightningcss-darwin-arm64/LICENSE"
Reading "node_modules/.pnpm/lightningcss-darwin-arm64@1.33.0/node_modules/lightningcss-darwin-arm64/README.md"
Reading "node_modules/.pnpm/lightningcss-darwin-arm64@1.33.0/node_modules/lightningcss-darwin-arm64/package.json"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/LICENSE"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/README.md"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/index.d.ts"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/lib/detect-libc.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/lib/elf.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/lib/filesystem.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/lib/process.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/detect-libc/package.json"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss-darwin-arm64/LICENSE"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss-darwin-arm64/README.md"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss-darwin-arm64/package.json"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/LICENSE"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/README.md"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/ast.d.ts"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/ast.js.flow"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/browserslistToTargets.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/composeVisitors.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/flags.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/index.d.ts"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/index.js"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/index.js.flow"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/index.mjs"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/targets.d.ts"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/node/targets.js.flow"
Reading "node_modules/.pnpm/lightningcss@1.33.0/node_modules/lightningcss/package.json"
Reading "node_modules/.pnpm/lock.yaml"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/LICENSE"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/README.md"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/package.json"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/src/strings.ts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/magic-string/LICENSE"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/magic-string/README.md"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/magic-string/dist/index.d.mts"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/magic-string/dist/index.mjs"
Reading "node_modules/.pnpm/magic-string@1.1.0/node_modules/magic-string/package.json"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/LICENSE"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/README.md"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.mjs"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.mjs.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.umd.js"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/gen-mapping.umd.js.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/types/gen-mapping.d.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/types/set-array.d.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/types/sourcemap-segment.d.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/dist/types/types.d.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/package.json"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/src/gen-mapping.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/src/set-array.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/src/sourcemap-segment.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/src/types.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/gen-mapping.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/set-array.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/set-array.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/set-array.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/set-array.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/sourcemap-segment.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/types.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/types.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/types.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/gen-mapping/types/types.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/LICENSE"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/README.md"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/dist/remapping.mjs"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/dist/remapping.mjs.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/dist/remapping.umd.js"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/dist/remapping.umd.js.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/package.json"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/src/build-source-map-tree.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/src/remapping.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/src/source-map-tree.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/src/source-map.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/src/types.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/build-source-map-tree.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/remapping.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/remapping.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/remapping.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/remapping.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map-tree.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map-tree.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map-tree.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map-tree.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/source-map.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/types.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/types.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/types.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/remapping/types/types.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/LICENSE"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/README.md"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.mjs.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/dist/resolve-uri.umd.js.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/dist/types/resolve-uri.d.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/resolve-uri/package.json"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/LICENSE"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/README.md"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.umd.js.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/package.json"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/src/scopes.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/src/sourcemap-codec.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/src/strings.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/src/vlq.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/scopes.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/sourcemap-codec.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/strings.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/strings.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/sourcemap-codec/types/vlq.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/LICENSE"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/README.md"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.mjs.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/dist/trace-mapping.umd.js.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/package.json"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/binary-search.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/by-source.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/flatten-map.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/resolve.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/sort.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/sourcemap-segment.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/strip-filename.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/trace-mapping.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/src/types.ts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/binary-search.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/binary-search.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/binary-search.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/binary-search.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/by-source.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/by-source.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/by-source.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/by-source.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/flatten-map.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/resolve.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/resolve.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/resolve.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/resolve.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sort.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sort.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sort.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sort.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/sourcemap-segment.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/strip-filename.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/trace-mapping.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/types.d.cts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/types.d.cts.map"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/types.d.mts"
Reading "node_modules/.pnpm/node_modules/@jridgewell/trace-mapping/types/types.d.mts.map"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher-darwin-arm64/LICENSE"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher-darwin-arm64/README.md"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher-darwin-arm64/package.json"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/LICENSE"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/README.md"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/binding.gyp"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/index.d.ts"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/index.js"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/index.js.flow"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/package.json"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/scripts/build-from-source.js"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Backend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Backend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Debounce.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Debounce.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/DirTree.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/DirTree.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Event.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Glob.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Glob.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/PromiseRunner.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Signal.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Watcher.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/Watcher.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/binding.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/kqueue/KqueueBackend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/linux/InotifyBackend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/linux/InotifyBackend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/macos/FSEventsBackend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/macos/FSEventsBackend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/shared/BruteForceBackend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/shared/BruteForceBackend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/unix/fts.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/unix/legacy.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/wasm/WasmBackend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/wasm/WasmBackend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/wasm/include.h"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/watchman/BSER.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/watchman/BSER.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/watchman/IPC.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/watchman/WatchmanBackend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/windows/WindowsBackend.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/windows/WindowsBackend.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/windows/win_utils.cc"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/src/windows/win_utils.hh"
Reading "node_modules/.pnpm/node_modules/@parcel/watcher/wrapper.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/LICENSE"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/esm-cache.loader.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/esm-cache.loader.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/index.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/index.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/index.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/require-cache.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/dist/require-cache.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/node/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-darwin-arm64/LICENSE"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-darwin-arm64/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-darwin-arm64/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/LICENSE"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.cjs.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.cjs.min.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.cjs.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.esm-bundler.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.min.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.min.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/dist/emnapi-core.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/LICENSE"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.cjs.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.cjs.min.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.cjs.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.esm-bundler.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.iife.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.iife.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.min.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.min.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/dist/emnapi.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/LICENSE"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.cjs.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.cjs.min.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.cjs.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.esm-bundler.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.min.d.mts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.min.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/dist/wasi-threads.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/dist/emnapi-plugins.cjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/dist/emnapi-plugins.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/dist/fs-proxy.cjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/dist/fs.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/fs-proxy.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/runtime.cjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime/runtime.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/dist/tsdoc-metadata.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/dist/wasm-util.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/dist/wasm-util.esm-bundler.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/dist/wasm-util.esm.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/dist/wasm-util.esm.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/dist/wasm-util.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/dist/wasm-util.min.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/asyncify.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/jspi.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/load.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/memory.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/error.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/fd.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/fs.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/path.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/preview1.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/rights.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/types.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/wasi/util.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/cjs/webassembly.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/asyncify.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/index.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/jspi.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/load.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/memory.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/error.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/fd.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/fs.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/index.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/path.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/preview1.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/rights.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/types.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/wasi/util.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/lib/mjs/webassembly.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/CopyrightNotice.txt"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/LICENSE.txt"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/README.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/SECURITY.md"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/modules/index.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/modules/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/modules/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/tslib.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/tslib.es6.html"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/tslib.es6.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/tslib.es6.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/tslib.html"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib/tslib.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/package.json"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/tailwindcss-oxide.wasi-browser.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/tailwindcss-oxide.wasi.cjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/wasi-worker-browser.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide-wasm32-wasi/wasi-worker.mjs"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide/LICENSE"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide/index.d.ts"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide/index.js"
Reading "node_modules/.pnpm/node_modules/@tailwindcss/oxide/package.json"
Reading "node_modules/.pnpm/node_modules/detect-libc/LICENSE"
Reading "node_modules/.pnpm/node_modules/detect-libc/README.md"
Reading "node_modules/.pnpm/node_modules/detect-libc/index.d.ts"
Reading "node_modules/.pnpm/node_modules/detect-libc/lib/detect-libc.js"
Reading "node_modules/.pnpm/node_modules/detect-libc/lib/elf.js"
Reading "node_modules/.pnpm/node_modules/detect-libc/lib/filesystem.js"
Reading "node_modules/.pnpm/node_modules/detect-libc/lib/process.js"
Reading "node_modules/.pnpm/node_modules/detect-libc/package.json"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/LICENSE"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/README.md"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/AliasFieldPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/AliasPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/AliasUtils.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/AppendPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/CloneBasenamePlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ConditionalPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/DescriptionFilePlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/DescriptionFileUtils.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/DirectoryExistsPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ExportsFieldPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ExtensionAliasPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/FileExistsPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ImportsFieldPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/JoinRequestPartPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/JoinRequestPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/LogInfoPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/MainFieldPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ModulesInHierachicDirectoriesPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ModulesInHierarchicalDirectoriesPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ModulesInRootPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ModulesUtils.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/NextPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ParsePlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/PnpPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/Resolver.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ResolverFactory.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/RestrictionsPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/ResultPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/RootsPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/SelfReferencePlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/SymlinkPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/SyncAsyncFileSystemDecorator.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/TryNextPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/TsconfigPathsPlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/UnsafeCachePlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/UseFilePlugin.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/createInnerContext.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/forEachBail.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/getInnerRequest.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/getPaths.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/index.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/entrypoints.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/fileURLToPath.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/fs.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/graceful-fs-browser.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/identifier.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/memoize.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/module-browser.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/path-browser.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/path.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/process-browser.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/strip-json-comments.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/lib/util/url-browser.js"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/package.json"
Reading "node_modules/.pnpm/node_modules/enhanced-resolve/types.d.ts"
Reading "node_modules/.pnpm/node_modules/graceful-fs/LICENSE"
Reading "node_modules/.pnpm/node_modules/graceful-fs/README.md"
Reading "node_modules/.pnpm/node_modules/graceful-fs/clone.js"
Reading "node_modules/.pnpm/node_modules/graceful-fs/graceful-fs.js"
Reading "node_modules/.pnpm/node_modules/graceful-fs/legacy-streams.js"
Reading "node_modules/.pnpm/node_modules/graceful-fs/package.json"
Reading "node_modules/.pnpm/node_modules/graceful-fs/polyfills.js"
Reading "node_modules/.pnpm/node_modules/is-extglob/LICENSE"
Reading "node_modules/.pnpm/node_modules/is-extglob/README.md"
Reading "node_modules/.pnpm/node_modules/is-extglob/index.js"
Reading "node_modules/.pnpm/node_modules/is-extglob/package.json"
Reading "node_modules/.pnpm/node_modules/is-glob/LICENSE"
Reading "node_modules/.pnpm/node_modules/is-glob/README.md"
Reading "node_modules/.pnpm/node_modules/is-glob/index.js"
Reading "node_modules/.pnpm/node_modules/is-glob/package.json"
Reading "node_modules/.pnpm/node_modules/jiti/LICENSE"
Reading "node_modules/.pnpm/node_modules/jiti/README.md"
Reading "node_modules/.pnpm/node_modules/jiti/dist/babel.cjs"
Reading "node_modules/.pnpm/node_modules/jiti/dist/jiti.cjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti-cli.mjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti-hooks.mjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti-native.mjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti-register.d.mts"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti-register.mjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti-static.mjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti.cjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti.d.cts"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti.d.mts"
Reading "node_modules/.pnpm/node_modules/jiti/lib/jiti.mjs"
Reading "node_modules/.pnpm/node_modules/jiti/lib/types.d.ts"
Reading "node_modules/.pnpm/node_modules/jiti/package.json"
Reading "node_modules/.pnpm/node_modules/lightningcss-darwin-arm64/LICENSE"
Reading "node_modules/.pnpm/node_modules/lightningcss-darwin-arm64/README.md"
Reading "node_modules/.pnpm/node_modules/lightningcss-darwin-arm64/package.json"
Reading "node_modules/.pnpm/node_modules/lightningcss/LICENSE"
Reading "node_modules/.pnpm/node_modules/lightningcss/README.md"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/ast.d.ts"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/ast.js.flow"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/browserslistToTargets.js"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/composeVisitors.js"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/flags.js"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/index.d.ts"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/index.js"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/index.js.flow"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/index.mjs"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/targets.d.ts"
Reading "node_modules/.pnpm/node_modules/lightningcss/node/targets.js.flow"
Reading "node_modules/.pnpm/node_modules/lightningcss/package.json"
Reading "node_modules/.pnpm/node_modules/magic-string/LICENSE"
Reading "node_modules/.pnpm/node_modules/magic-string/README.md"
Reading "node_modules/.pnpm/node_modules/magic-string/dist/index.d.mts"
Reading "node_modules/.pnpm/node_modules/magic-string/dist/index.mjs"
Reading "node_modules/.pnpm/node_modules/magic-string/package.json"
Reading "node_modules/.pnpm/node_modules/mri/index.d.ts"
Reading "node_modules/.pnpm/node_modules/mri/lib/index.js"
Reading "node_modules/.pnpm/node_modules/mri/lib/index.mjs"
Reading "node_modules/.pnpm/node_modules/mri/license.md"
Reading "node_modules/.pnpm/node_modules/mri/package.json"
Reading "node_modules/.pnpm/node_modules/mri/readme.md"
Reading "node_modules/.pnpm/node_modules/node-addon-api/LICENSE.md"
Reading "node_modules/.pnpm/node_modules/node-addon-api/README.md"
Reading "node_modules/.pnpm/node_modules/node-addon-api/common.gypi"
Reading "node_modules/.pnpm/node_modules/node-addon-api/except.gypi"
Reading "node_modules/.pnpm/node_modules/node-addon-api/index.js"
Reading "node_modules/.pnpm/node_modules/node-addon-api/napi-inl.deprecated.h"
Reading "node_modules/.pnpm/node_modules/node-addon-api/napi-inl.h"
Reading "node_modules/.pnpm/node_modules/node-addon-api/napi.h"
Reading "node_modules/.pnpm/node_modules/node-addon-api/node_addon_api.gyp"
Reading "node_modules/.pnpm/node_modules/node-addon-api/node_api.gyp"
Reading "node_modules/.pnpm/node_modules/node-addon-api/noexcept.gypi"
Reading "node_modules/.pnpm/node_modules/node-addon-api/nothing.c"
Reading "node_modules/.pnpm/node_modules/node-addon-api/package-support.json"
Reading "node_modules/.pnpm/node_modules/node-addon-api/package.json"
Reading "node_modules/.pnpm/node_modules/node-addon-api/tools/README.md"
Reading "node_modules/.pnpm/node_modules/node-addon-api/tools/check-napi.js"
Reading "node_modules/.pnpm/node_modules/node-addon-api/tools/clang-format.js"
Reading "node_modules/.pnpm/node_modules/node-addon-api/tools/conversion.js"
Reading "node_modules/.pnpm/node_modules/node-addon-api/tools/eslint-format.js"
Reading "node_modules/.pnpm/node_modules/picocolors/LICENSE"
Reading "node_modules/.pnpm/node_modules/picocolors/README.md"
Reading "node_modules/.pnpm/node_modules/picocolors/package.json"
Reading "node_modules/.pnpm/node_modules/picocolors/picocolors.browser.js"
Reading "node_modules/.pnpm/node_modules/picocolors/picocolors.d.ts"
Reading "node_modules/.pnpm/node_modules/picocolors/picocolors.js"
Reading "node_modules/.pnpm/node_modules/picocolors/types.d.ts"
Reading "node_modules/.pnpm/node_modules/picomatch/LICENSE"
Reading "node_modules/.pnpm/node_modules/picomatch/README.md"
Reading "node_modules/.pnpm/node_modules/picomatch/index.js"
Reading "node_modules/.pnpm/node_modules/picomatch/lib/constants.js"
Reading "node_modules/.pnpm/node_modules/picomatch/lib/parse.js"
Reading "node_modules/.pnpm/node_modules/picomatch/lib/picomatch.js"
Reading "node_modules/.pnpm/node_modules/picomatch/lib/scan.js"
Reading "node_modules/.pnpm/node_modules/picomatch/lib/utils.js"
Reading "node_modules/.pnpm/node_modules/picomatch/package.json"
Reading "node_modules/.pnpm/node_modules/picomatch/posix.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/LICENSE"
Reading "node_modules/.pnpm/node_modules/source-map-js/README.md"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/array-set.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/base64-vlq.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/base64.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/binary-search.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/mapping-list.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/quick-sort.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/source-map-consumer.d.ts"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/source-map-consumer.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/source-map-generator.d.ts"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/source-map-generator.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/source-node.d.ts"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/source-node.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/lib/util.js"
Reading "node_modules/.pnpm/node_modules/source-map-js/package.json"
Reading "node_modules/.pnpm/node_modules/source-map-js/source-map.d.ts"
Reading "node_modules/.pnpm/node_modules/source-map-js/source-map.js"
Reading "node_modules/.pnpm/node_modules/tapable/LICENSE"
Reading "node_modules/.pnpm/node_modules/tapable/README.md"
Reading "node_modules/.pnpm/node_modules/tapable/lib/AsyncParallelBailHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/AsyncParallelHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/AsyncSeriesBailHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/AsyncSeriesHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/AsyncSeriesLoopHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/AsyncSeriesWaterfallHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/Hook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/HookCodeFactory.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/HookMap.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/MultiHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/SyncBailHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/SyncHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/SyncLoopHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/SyncWaterfallHook.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/index.js"
Reading "node_modules/.pnpm/node_modules/tapable/lib/util-browser.js"
Reading "node_modules/.pnpm/node_modules/tapable/package.json"
Reading "node_modules/.pnpm/node_modules/tapable/tapable.d.ts"
Reading "node_modules/@tailwindcss/cli/LICENSE"
Reading "node_modules/@tailwindcss/cli/README.md"
Reading "node_modules/@tailwindcss/cli/dist/index.mjs"
Reading "node_modules/@tailwindcss/cli/package.json"
Reading "node_modules/my-ui-lib/index.html"
Reading "node_modules/tailwindcss/LICENSE"
Reading "node_modules/tailwindcss/README.md"
Reading "node_modules/tailwindcss/dist/chunk-5JIJA4QV.mjs"
Reading "node_modules/tailwindcss/dist/chunk-C2OYBFIH.mjs"
Reading "node_modules/tailwindcss/dist/chunk-DCG7AFIE.mjs"
Reading "node_modules/tailwindcss/dist/colors-C__qRT83.d.ts"
Reading "node_modules/tailwindcss/dist/colors.d.mts"
Reading "node_modules/tailwindcss/dist/colors.d.ts"
Reading "node_modules/tailwindcss/dist/colors.js"
Reading "node_modules/tailwindcss/dist/colors.mjs"
Reading "node_modules/tailwindcss/dist/default-theme.d.mts"
Reading "node_modules/tailwindcss/dist/default-theme.d.ts"
Reading "node_modules/tailwindcss/dist/default-theme.js"
Reading "node_modules/tailwindcss/dist/default-theme.mjs"
Reading "node_modules/tailwindcss/dist/flatten-color-palette.d.mts"
Reading "node_modules/tailwindcss/dist/flatten-color-palette.d.ts"
Reading "node_modules/tailwindcss/dist/flatten-color-palette.js"
Reading "node_modules/tailwindcss/dist/flatten-color-palette.mjs"
Reading "node_modules/tailwindcss/dist/lib.d.mts"
Reading "node_modules/tailwindcss/dist/lib.d.ts"
Reading "node_modules/tailwindcss/dist/lib.js"
Reading "node_modules/tailwindcss/dist/lib.mjs"
Reading "node_modules/tailwindcss/dist/plugin.d.mts"
Reading "node_modules/tailwindcss/dist/plugin.d.ts"
Reading "node_modules/tailwindcss/dist/plugin.js"
Reading "node_modules/tailwindcss/dist/plugin.mjs"
Reading "node_modules/tailwindcss/dist/resolve-config-B4yBzhca.d.ts"
Reading "node_modules/tailwindcss/dist/resolve-config-QUZ9b-Gn.d.mts"
Reading "node_modules/tailwindcss/dist/types-DWdTiksJ.d.mts"
Reading "node_modules/tailwindcss/package.json"
Reading "package.json"
Reading "pnpm-lock.yaml"
Reading "pnpm-workspace.yaml"
Reading "public/index.html"
Reading "tailwindcss-10742.log"
```

</details>

<details>

<summary>After</summary>

```
Reading "./app/index.html"
Reading "./package.json"
Reading "./public/index.html"
```

</details>
2026-08-07 17:44:33 +02:00
Robin Malfait
e566a926e7
--default(…) should emit values as-is (#20392)
This PR fixes an issue where the `--default(…)` function that can be
used as part of `@utility` definitions goes through the same
pre-processing treatment as `--value(…)` and `--modifier(…)`.

Instead the `--default(…)` value should be emitted as-is.

The pre-processing treatment that's in place today is just to normalize
the value(s) that could be formatted in a strange way if you are dealing
with older Prettier / Biome versions.

Some of the transformations we do ahead of time:

- `--value(--spacing)`                 -> `--value(--spacing-*)`
- `--value(--spacing- *)`              -> `--value(--spacing-*)`
- `--value(--text- * --line-height)` -> `--value(--text-*--line-height)`
- `--value(--text --line-height)` -> `--value(--text-*--line-height)`
- `--value(--text-\\* --line-height)` ->
`--value(--text-*--line-height)`
- `--value([ *])`                      -> `--value([*])`


One of these steps is getting rid of the spaces, that means that if your
`--default(…)` uses spaces, then we will get rid of it.

```css
@theme inline {
  --text-box-trim-start: trim-start;
  --text-box-trim-end: trim-end;
  --text-box-trim-both: trim-both;

  --text-box-edge-cap: cap alphabetic;
  --text-box-edge-cap-text: cap text;
  --text-box-edge-ex: ex alphabetic;
  --text-box-edge-ex-text: ex text;
  --text-box-edge-text: text alphabetic;
  --text-box-edge-text-text: text text;
}
@utility trim-* {
  text-box: --value('normal', 'inherit', 'initial', 'revert', 'revert-layer', 'unset');
  text-box: --value(--text-box-trim-*) --modifier(--text-box-edge-*, --default(box alphabetic));
}
```

When you try to use this:

```html
<div className="trim-both" />
```

Then we get the following CSS out:
```css
.trim-both {
 text-box: trim-both boxalphabetic;
}
```

This PR fixes that, and now the proper CSS will be emitted:
```css
.trim-both {
 text-box: trim-both box alphabetic;
}
```

Fixes: #20391

## Test plan

1. Added an regression test to make sure that `--default(…)` values are
emitted as-is.
2026-08-06 15:25:48 +02:00
Lazizbek Ergashev
b8a5f85889
Detect classes in Ruby percent literals using angle brackets or custom delimiters (#20387)
## Summary

Fixes #20386.

Ruby lets you close a `%w`/`%W` literal with any non-alphanumeric
character, but the pre-processor only recognized `[`, `(`, `{`, `#` and
a space. So `%w<bg-green-400>` and `%w|bg-blue-400|` were skipped and
the classes inside them never made it into the output.

The boundary lookup now maps `<` to `>` alongside the other paired
delimiters, and falls back to treating any other non-alphanumeric,
non-whitespace character as its own closing delimiter.

## Test plan

Added pre-processor and extraction cases for `%w<…>`, `%w|…|`, `%w:…:`
and `%w!…!` in `crates/oxide/src/extractor/pre_processors/ruby.rs`. They
fail on `main` and pass with this change.

```
cargo test -p tailwindcss-oxide
```

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-06 14:52:04 +02:00
Robin Malfait
3524b45310
Attempt to fix flaky integration test (#20384)
One of the integration tests is flaky on CI. This is an attempt to "fix"
it.

It has probably something to do with the availability of the resources
provided by GitHub because locally this works flawlessly for 100 runs
straight.

## Test plan

- Once we get 5 consecutive positive runs we van merge it.
- No actual code was changed, only the flaky integration test itself.

<img width="336" height="445" alt="image"
src="https://github.com/user-attachments/assets/bec81687-62f5-4df3-8109-d0aaa4ffbd8a"
/>
2026-08-05 12:39:27 +02:00
Robin Malfait
b5ab20473b
improve flaky integration test 2026-08-04 19:27:26 +02:00
Robin Malfait
9dae5163db
update changelog 2026-08-04 18:42:00 +02:00
Robin Malfait
d190343594
Use wasm as a fallback for @tailwindcss/oxide (#20383)
Right now, we use Rust for `@tailwindcss/oxide` which has 2
responsibilities:

1. Traverse the file system and figure out which files need to be
scanned based on auto source detection and `@source` directives.
2. Given those files, extract possible Tailwind CSS classes which we
call candidates.

Since this is using native code, we use napi-rs to get native `.node`
files on a per platform / arch basis.

So far so good, however, if you are on an OS that doesn't have a
prebuilt binary, you will receive an error that might look like this:

```
Error: Cannot find native binding. npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). Please try `npm i` again after removing both package-lock.json and node_modules directory.
    at Object.<anonymous> (/private/var/folders/1k/bdv8blv93xq7qgwjdwc9z88h0000gn/T/tailwind-integrationspYHIVP/node_modules/.pnpm/@tailwindcss+oxide@file+..+..+..+..+..+..+..+Users+robin+github.com+tailwindlabs+tailwi_47ae1688f61c719f66c73e2ff35e430f/node_modules/@tailwindcss/oxide/index.js:573:19)
    at Module._compile (node:internal/modules/cjs/loader:1829:14)
    at Object..js (node:internal/modules/cjs/loader:1969:10)
    at Module.load (node:internal/modules/cjs/loader:1552:32)
    at Module._load (node:internal/modules/cjs/loader:1354:12)
    at wrapModuleLoad (node:internal/modules/cjs/loader:255:19)
    at Module.require (node:internal/modules/cjs/loader:1575:12)
    at require (node:internal/modules/helpers:191:16)
    at file:///private/var/folders/1k/bdv8blv93xq7qgwjdwc9z88h0000gn/T/tailwind-integrationspYHIVP/index.mjs:5:19
    at ModuleJob.run (node:internal/modules/esm/module_job:437:25) {
  cause: Error: Cannot find module '@tailwindcss/oxide-darwin-arm64'
```

This means that we have to add support for these platforms, and there
are some open PRs related to this, which could be closed by this PR:

- #20327
- #20276
- #20201

Today we already have support for the big platforms out there:

- Windows arm64
- Windows x64
- macOS arm64
- macOS x64

But then it starts to get a bit out of hand once we start looking at
Linux based versions:

- Android arm eabi
- Android arm64
- Linux arm64 gnu
- Linux arm64 gnueabihf
- Linux arm64 musl
- Linux x64 gnu
- Linux x64 musl
- freebsd x64

... and then we have the pending list from the 3 PRs linked above.
Adding support for all of these is not the end of the world, but it gets
complex if we need to keep supporting more and more. Right now we rely
on a bunch of non-default napi-rs setup in CI just to support these
other platforms.

This PR solves that by using the `wasm32-wasi` build as a universal
fallback. The napi-rs generated loader already knows how to fall back to
`@tailwindcss/oxide-wasm32-wasi`, but that package declared `"cpu":
["wasm32"]`, so npm/pnpm never installed it on real hardware. Removing
that restriction means the package is installed everywhere, and the
loader picks it up whenever no native binding exists.

This PR also fixes a `UVWASI_EACCES` crash on sandboxed platforms
(OpenHarmony, Android): the generated wasm loader preopens `/`, which
those sandboxes deny, so the fallback failed to load on exactly the
platforms that need it (see [this
comment](https://github.com/tailwindlabs/tailwindcss/pull/20276#issuecomment-4950167198)).
We patch `@napi-rs/cli`'s codegen templates via `pnpm patch` to retry
with narrower preopens (`/` → cwd → none). On such platforms, scanning
is limited to files under the current working directory.

## Test plan

Added two integration tests:

1. Trick pnpm (via `supportedArchitectures`) into installing for a
platform we explicitly don't support, and assert `@tailwindcss/oxide`
loads the wasm binding and scans files from disk.
2. Simulate a sandbox that denies preopening `/`, and assert the wasm
binding still loads and scans.

[ci-all]
2026-08-04 18:41:43 +02:00
Robin Malfait
05c3a87627
Bump dependencies (#20381)
This PR bumps dependencies and dev dependencies and applies required
changes due to version bumps.

- Moved `@parcel/watcher` related dependencies to the
`pnpm-workflow.yaml` catalog
- Bumped Lightning CSS + updated tests with improvements
- Bumped napi related dependencies
- Tackled a Vitest warning

## Test plan

All tests should still pass on all platforms [ci-all]
2026-08-04 13:55:03 +02:00
Lazizbek Ergashev
50daebded0
Prevent @tailwindcss/vite crash under Vite's experimental bundledDev (#20379)
## Summary

Under Vite's experimental `bundledDev` mode, the `hotUpdate` hook in
`@tailwindcss/vite` gets called without a `server`. Vite only passes `{
type, file, modules }` here, but the hook loops over
`Object.values(server.environments)`, so editing any file (JS, CSS, or
HTML) throws `TypeError: Cannot read properties of undefined (reading
'environments')` and the dev server build fails.

The fix returns early when `server` is missing. Those environment loops
only look at environments other than the current one, and the
server-level `hot`/`ws` reload channels don't exist in this mode, so
bailing out leaves the classic (non-`bundledDev`) dev path untouched.

Fixes #20378

## Test plan

- Added a unit test that calls `hotUpdate` without a `server` and checks
it doesn't throw. It fails on the current code and passes with the
guard.
- Reproduced with a Vite 8 project using `experimental.bundledDev:
true`: before the change, editing any JS/CSS/HTML file crashed the dev
server; after it, edits work.
- `pnpm run test` and the `@tailwindcss/vite` integration suite both
pass.


[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-08-03 15:10:06 +02:00
Robin Malfait
6def8205b5 update changelog 2026-08-03 13:11:43 +02:00
Robin Malfait
a4e0f75633 try converting variants with arbitrary values to builtin variants 2026-08-03 13:11:43 +02:00
Robin Malfait
489c22b4be add failing tests 2026-08-03 13:11:43 +02:00
Robin Malfait
4be6110aff update changelog 2026-07-31 15:13:38 +02:00
Tim Neutkens
25fa72c1ec Use default Turbopack mode in integration test 2026-07-31 15:13:38 +02:00
Tim Neutkens
56565b68f9 Add Turbopack loader integration coverage 2026-07-31 15:13:38 +02:00
Tim Neutkens
c9689a44a6 Add @tailwindcss/turbopack loader 2026-07-31 15:13:38 +02:00
Robin Malfait
d494a78532 update changelog 2026-07-31 14:53:47 +02:00
Robin Malfait
78b4864d30 always emit , ) instead of ,) for fallback values in CSS variables
This will bring the development version and production version closer
together. It also means that the development version works in older
supported browsers like  Chrome v112 where the space is required (even
though the spec allows both)

There are no changes in tests just becaues we already run each test
through Lightning CSS which normalizes these values:

Input:
```css
var(--tw-blur,)
var(--tw-blur, )
```

Lightning CSS output:
```css
var(--tw-blur, )
var(--tw-blur, )
```
2026-07-31 14:53:47 +02:00
Robin Malfait
423aee5309
Improve handling of @scope related at-rules (#20369)
Continuation of #20344, original commits of that PR are present in this
PR as well.


Before we talk about the issues, there is some jargon I'll be using that
might be unfamiliar:
```css
@scope (.from) to (.to) {
  /*   ^^^^^^^^^^^^^^^^    This is the prelude */
  /*   ^^^^^^^             This is the `scope-start` */
  /*              ^^^^^    This is the `scope-end` */
}

/* The `scope-start` is optional, if you only want `scope-end`: */
@scope to (.to) {
}

/* The `scope-end` is optional, if you only want `scope-start`: */
@scope (.from) {
}
```

This PR contains multiple fixes, but they are all related to the
`@scope` at-rule. The `@scope` at-rule is a beast, it's an at-rule where
we do have multiple selectors in the prelude, which in turn can contain
`&` rules. If you use `&` inside of an `@scope` rule, its meaning
changes compared to what `&` typically means and it is even different
whether it exists in `scope-start` or `scope-end`.

Let's start with the original issue, if you define a custom variant
like:
```css
@custom-variant blue {
  @scope ([data-theme='blue']) to ([data-theme]) {
    @slot;
  }
}
```

Then using `blue:bg-blue-500` used to generate:

```css
.blue\:bg-blue-500 {
  @scope ([data-theme='blue']) to ([data-theme]) {
    background-color: var(--color-blue-500);
  }
}
```

This is because internally we start with the AST node representing
`bg-blue-500`, then we wrap all the `variant` related nodes around it
(`blue`), and then wrap everything in the final rule with a selector
representing the class (`.blue\:bg-blue-500`).

This is incorrect because you want that sandwich effect where any
`.blue\:bg-blue-500` class between an element with the
`[data-theme="blue"]` attribute and another nested `[data-theme]`
attribute.

The other [PR](#20344) solved this by simply making sure that the
`@scope` rule gets hoisted to the top. For this particular example that
was enough.

However, that would result in a ton more issues. This hoisting or
swapping with the rule is only "safe" in a `custom-variant`.

So similar to the other PR, this PR wraps this properly:

```css
@scope ([data-theme='blue']) to ([data-theme]) {
  .blue\:bg-blue-500 {
    background-color: var(--color-blue-500);
  }
}
```

The tricky part is that the nesting pass runs on _all_ CSS, custom
variants _and_ your own CSS. Hoisting an `@scope` rule out of the rule
it is nested in changes its meaning.

Take a look at this example:

```css
.parent {
  @scope (.from) to (.to) {
    * {
      border: 1px solid black;
    }
  }
}
```

This says that you need a structure roughly like this:
```html
<div class="parent">
  <div class="from">
    <div>This element will get a border</div>

    <div class="to">
      <div>This element won't get a border, it's beyond the scope</div>
    </div>
  </div>
</div>
```

If we had switched it:
```css
@scope (.from) to (.to) {
  .parent {
    * {
      border: 1px solid black;
    }
  }
}
```

Then this requires that an element with a class of `.parent` lives
between the
`.from` scope-start and `.to` scope-end:
```html
<div class="from">
  <div class="parent">
    <div>This element will get a border</div>

    <div class="to">
      <div>This element won't get a border, it's beyond the scope</div>
    </div>
  </div>
</div>
```

Subtle, but it's definitely wrong.

Instead, we will apply our flattening rules, such that the `@scope` gets
hoisted, but we have to bring that `.parent` selector requirement inside
of the `@scope` prelude, more specifically inside the `scope-start`:

```css
@scope (.parent .from) to (.to) {
  * {
    border: 1px solid black;
  }
}
```

We _could_ have left this as-is, but there is a Lightning CSS bug where
the original CSS:
```css
.parent {
  @scope (.from) to (.to) {
    * {
      border: 1px solid black;
    }
  }
}
```

gets turned into:
```css
@scope (.from) to (.to) {
  :scope * {
    border: 1px solid #000;
  }
}
```

Notice how the `.parent` class is not found at all? (I will open some
issues on the Lightning CSS repo (and PRs to fix this). But it's a good
fix to have in Tailwind CSS as well, just because a lot of tooling is
already using Lightning CSS on top of Tailwind CSS, so we need an entire
chain to be updated for this to work.)

Side note: the version we generate is untouched by Lightning CSS.

## Resolving `&` in the prelude

`&` resolves differently depending on which scope selector you're
looking at
([spec](https://drafts.csswg.org/css-nesting-1/#nesting-at-scope)):

- In the `<scope-start>` selector, `&` refers to the elements matched by
the nearest ancestor style rule (the utility, for variants).
- In the `<scope-end>` selector, `&` refers to the scoping root and
behaves like `:where(:scope)`. `:scope` might work, but that has a
specificity of `0,1,0` instead of `0,0,0`.

So this input:

```css
.parent {
  @scope (& > .scope) to (& .limit) {
    .content {
      color: red;
    }
  }
}
```

compiles to:

```css
@scope (.parent > .scope) to (:where(:scope) .limit) {
  .content {
    color: red;
  }
}
```

The substitution uses the exact same logic as `&` in nested style rule
selectors: `:is(…)` semantics by default, dropping the `:is(…)` whenever
the substitution is provably equivalent.

So a complex parent compiles to `@scope (.a .b > .from)` rather than
`@scope (:is(.a .b) > .from)`, while `.card&` inside a `main` rule keeps
it (`@scope (.card:is(main))`) because substituting the type selector
as-is would produce invalid CSS.

For user CSS, every selector in a `<scope-start>` selector list is
relative to the parent rule, whether it uses `&` or not, just like
nested style rule selectors:

```css
.parent {
  @scope (.a, & > .b) {
    .inside {
      color: red;
    }
  }
}
```

compiles to:

```css
@scope (.parent .a, .parent > .b) {
  .inside {
    color: red;
  }
}
```

## Controlling the composition with `&`

Because `&` in the `<scope-start>` selector refers to the utility,
variant authors can control where the utility ends up:

```css
/* Default: the utility applies to elements inside the scope */
@custom-variant in-blue {
  @scope ([data-theme='blue']) to ([data-theme]) {
    @slot;
  }
}

/* Trailing `&`: the utility itself becomes the scoping root */
@custom-variant blue-self {
  @scope ([data-theme='blue'] &) to ([data-theme]) {
    @slot;
  }
}

/* Leading `&`: the scoping roots are found inside the utility */
@custom-variant scoped-panel {
  @scope (& .panel) {
    @slot;
  }
}
```

generates:

```css
@scope ([data-theme='blue']) to ([data-theme]) {
  .in-blue\:flex {
    display: flex;
  }
}

@scope ([data-theme='blue'] .blue-self\:flex) to ([data-theme]) {
  :where(:scope) {
    display: flex;
  }
}

@scope (.scoped-panel\:flex .panel) {
  :where(:scope) {
    display: flex;
  }
}
```

Note that when the utility becomes the scoping root, the `<scope-end>`
limit no longer affects the utility itself (a scoping root is never
beyond its own limits), and the declarations apply to the root with zero
specificity. The default composition is usually what you want to get the
sandwich effect, but now you _can_ get the other behavior if you want.

## Bare declarations

There is another small Lightning CSS bug that we want to fix. If you
have the following CSS:
```css
@scope (.from) to (.to) {
  color: red;
}
```

Then Lightning CSS crashes with an unexpected input. The [spec
says](https://drafts.csswg.org/css-cascade-6/#scoped-declarations) that:

> Declarations may be used directly with the body of a `@scope` rule.
Contiguous runs of declarations are wrapped in nested declarations
rules, which match the scoping root with zero specificity.
>
> ```css
> @scope (.foo) {
>   border: 1px solid black;
> }
> ```
> is equivalent to:
> ```css
> scope (.foo) {
>   :where(:scope) {
>     border: 1px solid black;
>   }
> }
> ```

So this is what we will do as well:
```css
@scope (.from) to (.to) {
  :where(:scope) {
    color: red;
  }
}
```

---

One more small detail about the implementation: we do have to track
which CSS is coming from the custom variant, and what is user defined
CSS. To do this, we insert some internal `context` nodes with a `{
source: 'user' }` or `{ source: 'variant' }` so we know when we can just
swap this `@scope` around, or if we just have to handle the nesting and
`&` substitutions.

Fixes: #18961
Closes: #20344

## Test plan

1. Added loads of new tests related to `@scope` in user-land
1. Added loads of new tests related to `@scope` in custom variants
   - When using the `@custom-variant` shorthand syntax
   - When using the `@custom-variant` syntax with `@slot`
   - When using arbitrary variants like `[@scope_(.from)_to_(.to)]:flex`
   - When using `addVariant()` and `matchVariant()` APIs
1. Added tests for the hoisting logic, prelude resolution for `&`
selectors
1. Added browser based UI tests to make sure that the non-flattened
version and flattened version behave the same.


---

Sorry about all the sandwiches, I'm hungry myself.

---------

Co-authored-by: uditDewan <udit.dewan21@gmail.com>
2026-07-31 12:36:44 +02:00
Robin Malfait
f6b7c5818e
Update integration tests: PostCSS re-emits the UTF-8 BOM character (#20372)
This PR updates the integration tests that started failing because of a
recent PostCSS update.

PostCSS v8.5.24 started re-emitting the BOM character in the output,
see: https://github.com/postcss/postcss/releases/tag/8.5.24 therefore
the tests started failing.

This PR just re-adds that back.

It might not be clear from the diff on GitHub, but this is the change: 
<img width="878" height="84" alt="image"
src="https://github.com/user-attachments/assets/5e2fbb6e-2ee4-46c5-9a2c-9c3a1a806bb0"
/>


## Test plan

1. All tests pass again [ci-all]
2026-07-31 12:19:43 +02:00
Robin Malfait
6de87c62c4
Fix changes to symlinked files outside of the project works (#20356)
This PR fixes an issue where changes to a symlinked file wouldn't result
in hot-reload when using `@tailwincdss/vite`. This issue also exists in
the other packages such as `@tailwincdss/postcss`,
`@tailwincdss/webpack` and `@tailwincdss/cli`.

The issue is that we watch the symlinked file, but not the "real" file
for changes. If the source of the symlinked file lives in another folder
that is not covered by auto-source detection or by any of the `@source`
directives, then changes to that file won't trigger a change.

To solve this, if a file is symlinked or lives in a symlinked folder,
then we will make sure that the `scanner.files` contains the real path /
canonicalized path to the real file as well just so we can detect
changes in that file.

Fixes: #20346
Closes: #20347

## Test plan

1. Added a regression test for `@tailwindcss/vite` 
2. Added tests in the scanner code itself
3. Manually tested on the reproduction:

| &nbsp; | Initial state | After change |
| ---: | --- | --- |
| **Before** | <img width="3200" height="1800"
alt="file-f8616573eec3150ae484fd279204c6d7"
src="https://github.com/user-attachments/assets/2734ecc3-b5b6-420e-820d-8a4a8fcdd7f5"
/> | <img width="3200" height="1800"
alt="file-0e93fd3c2c9694c844b098616a3208d2"
src="https://github.com/user-attachments/assets/fd86859b-420b-476b-80f6-e94d02e8b07b"
/> |
| **After** | <img width="3200" height="1800"
alt="file-f8616573eec3150ae484fd279204c6d7"
src="https://github.com/user-attachments/assets/2734ecc3-b5b6-420e-820d-8a4a8fcdd7f5"
/> | <img width="3200" height="1800"
alt="file-eb7b37430ea1363dddeac7226007afe4"
src="https://github.com/user-attachments/assets/0a95b1a4-a357-491d-b57f-78460c0fc9db"
/> |


[ci-all]

---------

Co-authored-by: Nic <162764842+Nic-Polumeyv@users.noreply.github.com>
2026-07-28 12:40:02 +02:00
Robin Malfait
4c128662c2
Fix failing upgrade test in CI (#20357)
The last CI run on `main` was on July 16, and the last `globby` release
(16.2.2) was released on July 15 (~21 hours earlier). Due to the default
`minimumReleaseAge` in pnpm of 24 hours, it meant that we were receiving
the previous globby version.

In the new version, they slightly changed some of the `.gitignore`
parsing, but in one of our upgrade tests we wrote an invalid
`.gitignore` file (which had indented lines).

This PR fixes that by using the `txt` tagged template literal, which
behind the scenes uses `dedent` which in turn makes sure that we ship a
proper `.gitignore` file.


## Test plan

All tests should pass [ci-all]
2026-07-28 00:21:06 +02:00
Robin Malfait
094bf62605
update release date 2026-07-16 19:15:10 +02:00
Robin Malfait
c2b24dd15f
4.3.3 (#20334)
Some checks failed
Release / Build aarch64-apple-darwin (oxide) (release) Has been cancelled
Release / Build x86_64-apple-darwin (oxide) (release) Has been cancelled
Release / Build x86_64-unknown-freebsd (OXIDE) (release) Has been cancelled
Release / Build aarch64-unknown-linux-gnu (oxide) (release) Has been cancelled
Release / Build armv7-unknown-linux-gnueabihf (oxide) (release) Has been cancelled
Release / Build x86_64-unknown-linux-gnu (oxide) (release) Has been cancelled
Release / Build x86_64-unknown-linux-musl (oxide) (release) Has been cancelled
Release / Build aarch64-unknown-linux-musl (oxide) (release) Has been cancelled
Release / Build aarch64-linux-android (oxide) (release) Has been cancelled
Release / Build armv7-linux-androideabi (oxide) (release) Has been cancelled
Release / Build aarch64-pc-windows-msvc (oxide) (release) Has been cancelled
Release / Build x86_64-pc-windows-msvc (oxide) (release) Has been cancelled
Release / Build and publish Tailwind CSS (release) Has been cancelled
Release / notify (release) Has been cancelled
2026-07-16 07:21:50 -04:00
Robin Malfait
bdcd7087b3
Don't trigger a full page reload for scanned files that Vite processes as modules (#20336)
This PR fixes an issue where editing a scanned file that Vite (or one of
its plugins) can process as a module, but that isn't currently loaded,
caused `@tailwindcss/vite` to force a full page reload, throwing away
all client state.

The `hotUpdate` hook has a fallback that sends a `full-reload` for files
that Tailwind scans but that Vite knows nothing about (e.g. `.php` or
`.blade.php` templates rendered by a backend). Without it, edits to
those files wouldn't refresh the page at all. To detect those files we
check whether every module for the changed file is an `asset` and/or has
no id, because the scanner's `addWatchFile` calls create exactly such
placeholder nodes for every scanned file.

The problem is that a source file that Vite _can_ process, but that
isn't loaded yet, looks exactly the same. The realistic way to get into
that state is code splitting: with route-level splitting (e.g.
`React.lazy`, TanStack Router's `autoCodeSplitting`, lazy routes in
`vue-router`), every component behind an un-visited split boundary only
exists as a scan placeholder in the module graph. Editing any of them
reloaded the whole app. The same happens for component stylesheets that
a framework plugin compiles into the component (e.g. Angular via
Analog), which never show up as their own module.

A full reload is never useful for these files: if the file is loaded,
Vite's own HMR handles it, and if it isn't loaded, reloading the page
won't load it either. Any new candidates still apply through the regular
`css-update` flow because the file is registered via `addWatchFile`.

So instead, we now skip the fallback when the changed file is handled by
Vite's module pipeline:

- The file exists as a real module in another environment (e.g. an
SSR-only module). This check already existed and is folded into the same
code path.
- The file is part of the JS/TS or CSS families, which Vite transforms
natively.
- For any other file type (e.g. `.vue`, `.svelte`, or `.md` with an SSG
plugin), a file with the same extension exists as a real module in some
environment's module graph, then a plugin does handle this file type and
the changed file just isn't loaded (yet).

External templates like `.php` files still trigger a full reload exactly
like before.

Fixes: #20320
Fixes: #19903
Closes: #20323

## Test plan

1. Added integration tests to ensure extensions handled by default rely
on HMR
2. Added integration tests to make sure that unknown extensions that
have been handled already will also use HMR
3. Manually tested that changing a `.php` file still triggers a
`full-reload`
4. Manually tested the reproduction where local client state isn't
thrown away


<img width="594" height="100"
alt="file-14a86a90a1e4b810c2b80338ea688572"
src="https://github.com/user-attachments/assets/a4507502-4a5d-43ee-93b9-14c793a25891"
/>

<img width="1122" height="1376"
alt="file-a5121da2ad77b95fdd1703560ef1ff41"
src="https://github.com/user-attachments/assets/cc5c67b0-b5ad-481f-8823-a3266d75357d"
/>
2026-07-15 17:49:05 +02:00
Robin Malfait
7811d74f37
Ensure earlier @source is not accidentally ignored by later @source (#20335)
This PR fixes an issue where an `@source` pointing to a file in a nested
folder was not scanned when a later `@source` pointed to a file in a
parent folder.

E.g.:

```css
@source "./nested/index.html";
@source "./index.html";
```

When using `@source` pointing to a specific file, then we want to make
sure that we ignore _other_ files since they are not listed explicitly.
To ensure that these patterns don't read other files, we inject a `*`
ignore pattern before it. You can think of the above being expanded to:

```rs
Ignored { base: "/project/src/nested", pattern: "*" }
Pattern { base: "/project/src/nested", pattern: "/index.html" }
Ignored { base: "/project/src", pattern: "*" }
Pattern { base: "/project/src", pattern: "/index.html" }
```

The problem with this is that the `Ignored { base: "/project/src",
pattern: "*" }` pattern results in ignoring the `nested` folder as well.
This means that we never even walk into the `nested` folder, so the
earlier `@source "./nested/index.html"` never matches anything.

We could switch the order in user land, but that's going to be hard to
maintain (and order matters for undoing/redoing earlier rules, so we
can't re-order internally either). Instead, we can scope the ignore
pattern to the _current_ path only, and not deeply nested. In other
words, the pattern should become:

```diff
- *
+ /*
```

It's a very subtle difference, but the pattern from above will now
become:
```diff
  Ignored { base: "/project/src/nested", pattern: "*" }
  Pattern { base: "/project/src/nested", pattern: "/index.html" }
- Ignored { base: "/project/src", pattern: "*" }
+ Ignored { base: "/project/src", pattern: "/*" }
  Pattern { base: "/project/src", pattern: "/index.html" }
```

We already do this when an unrestricted root (e.g. `@source "./nested"`)
lives inside the base of a restricted pattern. This works because every
source base is also its own walk root, and a `/*` pattern only matches
direct children so it can't ignore anything when walking from the nested
root itself.

This PR extends that same check to restricted pattern bases: if another
`@source` pattern has its base nested inside the current base, we emit
`/*` instead of `*`.

Note that we only relax the pattern to `/*` when such a nested root
actually exists. Sibling folders that no `@source` points into (e.g. an
`ignore-me` folder next to `nested`) are still direct children, so they
still match `/*` and are never walked.

Fixes: #20333

## Test plan

1. Added a regression test with the reproduction setup
2. Added a similar test with another sibling folder that should still be
ignored
3. Tested it against the actual reproduction:

Before:
<img width="797" height="463" alt="image"
src="https://github.com/user-attachments/assets/893bb872-f5cf-4c5a-bfb5-2dc1043b5a39"
/>

After:
<img width="794" height="460" alt="image"
src="https://github.com/user-attachments/assets/a59aae45-68ad-4fe3-b21f-b568f535d24f"
/>

Notice that the `text-green-500` now appears as expected.
2026-07-15 13:35:00 +02:00
Robin Malfait
f861d5c5e6
use the same fix for template files
Forgot this as part of https://github.com/tailwindlabs/tailwindcss/pull/20329
2026-07-14 18:42:33 +02:00
Benoît Rouleau
a854b35bcf
Prevent @tailwindcss/upgrade from rewriting files inside node_modules when run from a workspace subpackage (#20329)
Fixes #20328.

## What changed

When `@tailwindcss/upgrade` is invoked from a subpackage of a workspace
(e.g. `pnpm --filter …` or `cd packages/foo && pnpm exec upgrade`), it
traverses `../../node_modules/…` and applies its v3 → v4 migration
transform (`@tailwind utilities;` → `@import 'tailwindcss/utilities'
layer(utilities);`) to files inside the installed `tailwindcss` package
itself — a self-referential import that later detonates as an infinite
CSS-resolution loop and reads like a "cache corruption" bug (matches the
report in #19726 / #20328).

Root cause: `globby`'s `isGitIgnored` only walks `.gitignore` files at
or below its `cwd`. When invoked from a subpackage, the workspace-root
`.gitignore` (which almost always lists `node_modules`) is never
consulted, and `../../node_modules/…` paths come back as **not**
ignored.

The fix anchors `isGitIgnored` at the git repository root instead of the
subpackage:

- New helper: `gitRoot(cwd)` in `src/utils/git.ts` — thin wrapper over
`git rev-parse --show-toplevel`.
- `analyze(stylesheets, { base })` in `src/codemods/css/analyze.ts` now
calls `isGitIgnored({ cwd: gitRoot(base) ?? base })`. Falls back to the
previous behavior when not inside a git repository or when `git` is
unavailable.

## Test

Added an integration test that reproduces the exact scenario: a pnpm
workspace with a subpackage that imports `tailwindcss/utilities.css`,
upgrade run from the subpackage, then asserts
`packages/css/node_modules/tailwindcss/utilities.css` still holds the
pristine `@tailwind utilities;` directive.

Verified the test fails on `main` and passes with this change.

The 415 existing upgrade unit tests still pass. Two npm-related snapshot
failures in `upgrade-errors.test.ts` (`half-upgraded v3 project to v4
(bun|npm)`) pre-exist this change — they're about newer npm's `npm
notice run` output that isn't in the snapshots, unrelated to what this
PR touches.


---

Edit by @RobinMalfait: going to run on all [ci-all] so we can make sure
it works on Windows as well.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-07-14 16:39:24 +00:00
Robin Malfait
e48c5e8047
Fix weird character rendering on Windows with Japanese locale (#20318)
This PR fixes an issue where some characters are incorrectly rendered on
Windows with the Japanese locale.

This is arguably a bug in the font that's loaded by Windows when it
encounters `system-ui`. But waiting for fixes there might ... take a
while.

Another option is to not change the defaults in Tailwind CSS and instead
let the users that support different locales implement a fallback by
overriding the `--font-sans` variable.

The biggest reason for me to _not_ change it in Tailwind CSS is that it
requires us to know what the (proper) fallback fonts need to be on a per
OS basis.

But the main reason why I did want to make the change is that MDN says
this about the `system-ui` font:

> Glyphs are taken from the default user interface font on a given
platform. Because typographic traditions vary widely across the world,
this generic is provided for typefaces that don't map cleanly into the
other generics.
>
> **Note:** As the name implies, `system-ui` is intended to make UI
elements look like native apps, and not for typesetting large paragraphs
of text. It may cause the displayed typeface to be undesirable for some
users—for example, the default Windows CJK font may render Latin scripts
poorly, and the `lang` attribute may not affect the displayed font. Some
operating systems do not allow customizing `system-ui`, while browsers
generally allow customizing the `sans-serif` font family. For large
paragraphs, use `sans-serif` or some other non-UI font family instead.
>
> —
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/font-family#system-ui

There are PRs in other big projects that made this kind of change as
well. E.g.:

- https://github.com/withastro/starlight/pull/3729
- https://github.com/vuejs/vitepress/pull/4988

The reasoning for getting rid of `ui-sans-serif` is twofold:
1. Because the starlight PR seems very well tested, and they got rid of
it
2. In the event that the browser decided to load the broken font when it
encounters `ui-sans-serif`, then we will run into the same issue again.

Fixes: #19767
Fixes: #19768


## Test plan

1. `system-ui` is not used anymore, so the bug doesn't happen
3. Everything still looks the same for the places I checked, but it's
hard to know if this created _other_ issues on other OS + Locale
combinations...
2026-07-14 17:04:20 +02:00
Robin Malfait
b03e5e7731
Lazy load @parcel/watcher (#20325)
This PR ensure that we lazy load the `@parcel/watcher` in the
`@tailwindcss/cli`. This means that we don't have to load it at all when
using a normal build or when using `--watch --poll` combination.

The bigger reason is that on some platforms `@parcel/watcher` might not
work, and therefore the build will fail even if you don't use `--watch`
at all.

This PR fixes that by lazy loading it. Then, if we can't load it when
using `--watch`, a useful workaround is shown by using `--watch --poll`
instead.

This PR also improves showing errors such that the `error.cause`
property can be rendered as well.

Maybe in the future we can make use of deferred imports
(https://github.com/tc39/proposal-defer-import-eval)

Fixes: #20322

## Test plan

1. All tests still pass [ci-all]
2. Fabricated a fake error locally to prove that we can still use a
normal build and `--watch --poll` as a workaround

<img width="1122" height="1376"
alt="file-2f91d99cfb9eb0e24a196c2dd358c1f3"
src="https://github.com/user-attachments/assets/e37658ee-0827-422e-bd50-8ea5a85e1b7c"
/>
2026-07-13 15:53:42 +02:00
Robin Malfait
35a3e9c515
Always produce <length> value when optimizing --spacing(0) (#20319)
This PR fixes a type issue when using `--spacing(0)`. This construction
doesn't really make sense, but if you use it, it produces the value `0`
instead of `calc(var(--spacing) * 0)` which is fine if you use it in a
spot where a `<length>` data type can be used, then the `0` is
interpreted as a `<length>`.

E.g.: `padding: 0` and `padding: 0px` are equivalent.

However, if you use it in a CSS variable, then the `0` will turn in a
`<number>` if you don't have an `@property` definition for that CSS
variable.

This on its own isn't the issue, but if you later use it as part of a
`calc(…)` then the `<number>` instead of `<length>` type is being used.
As seen in #20315.

We could remove the optimization, and use `calc(var(--spacing) * 0)`
again, but this is a bit silly since it only makes your CSS file larger.

We could try to be smart, and only do it _if_ we're assigning to a CSS
variable (which #20317 is doing). But if your CSS variable _does_ use a
`<length>` then it's a non-issue. We would run into the same issue if
you use `width: calc(100% --spacing(0))`. This value is a bit silly
anyway, but it would originally resolve to `calc(100% +
calc(var(--spacing) * 0))`, the optimization of `calc(100% - 0)` would
make it invalid, so the fix in #20317 would not be enough.

Instead I opted for an inbetween solution, by always using `0px`. In
most cases we can use `0`, but in the places we can't the `0px` would at
least ensure that we are dealing with `<length>` data types.

This way we don't have to try to be smart to analyze where we use the
value, and `0px` is still better than the long `calc(var(--spacing) *
0)` value.

Fixes: #20315
Closes: #20317

## Test plan

1. Manually tested that now `--spacing(0)` does produce `0px` which has
a length type
2026-07-09 18:34:27 +02:00
Robin Malfait
4af47fbe94
Fix hues in achromatic theme colors to be none (#20314) 2026-07-09 13:36:34 +02:00
Robin Malfait
5835691d21
Handle CSS nesting natively (#20124)
This PR introduces a new feature where we will be handling the CSS
nesting ourselves.

We currently still rely on Lightning CSS in most places. But there are
situations where we don't use Lightning CSS out of the box:

1. During development, typically optimization/minification isn't setup
2. In places where it isn't as easy to run Lightning CSS such as in
`@tailwindcss/browser` or in Tailwind Play.

We handle CSS nesting in a single pass over the AST by tracking some
information as we go. It's not the most complex code, but there are some
tricky parts to make this happen in an efficient way, especially for the
few additional optimizations we handle.

While going over the AST, we will only emit CSS the moment we see
declarations or comments. This also means that this has a fun side
effect of removing CSS that ends up with empty nodes automatically.
(Caveat: there are exceptions for body-less rules such as `@layer foo;`
or `@charset "UTF-8";)

This also allowed us to do some cleanup in `optimizeAst` that tried to
do this as well, but now this will be handled by the code that handles
nesting automatically. Which is preferred because the version in
`optimizeAst` mutated the AST.

This also contains some optimizations where we merge adjacent at-rules
(with the same name / params), and adjacent rules with the same
selector, and get rid of declarations that are duplicated in a node.
(Caveat: there are exceptions, in case of `@font-family { … }` where we
don't want to merge them)

~~To ensure that this implementation is correct, I also added an oracle
implementation in the tests. This implementation does multiple passes
over the AST, because it does each step one by one, with minimal code.
Each step contains comments with examples to see what's happening in
that step. We then test the optimized version against this.~~ Once the
implementation was in place, and all the tests were passing, then I
deleted the oracle implementation. That way we don't have to keep it in
sync all the time.

While handling the nesting, we have to make sure that `&` exists and if
we replace it with a parent selector that we do use `:is(…)` semantics.
This means that:
```css
.foo {
  &:hover {
    color: red;
  }
}
```
Becomes:
```css
:is(.foo):hover {
  color: red;
}
```

We then also make sure that we optimize the selector by removing the
unnecessary `:is(…)` wrappers, but only if they were introduced by the
nesting logic. If _you_ wrote `:is(…)` in your CSS, we won't touch it.

If you look at the commits, the first thing we did is remove the
optimization step from Lightning CSS in the tests. Then we enabled our
CSS nesting handling code. This allows us to see the effect of the
changes we are making. At the end, we re-enabled Lightning CSS.

For now, this PR will be a step that happens before Lightning CSS is
executed, while still using Lightning CSS. But now this step will also
always happen in places where we don't use Lightning CSS at all.

This should not result in any breaking changes. It could result in
changed CSS output in environments where Lightning CSS isn't used. In
environments where it is being used, then there could be some
differences related to some selectors but they should result in the same
behavior with the same specificity.

While testing things, I noticed that there are some missed opportunities
for performance related to how we extract variables from declaration
values. I want to tackle `optimizeAst` in future PRs to make it simpler,
more performant, and maybe even merge it with the CSS nesting handling.

As part of testing this, I tested it against the tailwindcss.com
codebase which contains a lot of CSS (807.67 KB, 18 174 AST nodes)
because almost every utility is being used in examples.

The oracle implementation is rather slow:
```
[131.59ms]   ↳ oracle (step by step)
[129.23ms]     ↳ handleNesting(…)
[  2.32ms]     ↳ toCss(…)
```

But the final code is much faster (`<15ms`):
```
[ 10.11ms]   ↳ hand written (single pass)
[  8.40ms]     ↳ handleNesting(…)
[  1.67ms]     ↳ toCss(…)
```

In contrast, Lightning CSS takes: `[ 37.48ms] Optimized by Lightning
CSS`

One interesting thing to notice is that in big projects, this could add
`10ms` to the build, but Lightning CSS would then take less time to
process, which results in a no-op with better output.

One thing to keep in mind here is that Lightning CSS does more things,
such as normalizing values, handling vendor prefixes, CSS nesting, etc.

<details>

<summary>Some notes on how the algorithm works:</summary>

### The basic idea

When you have CSS that looks this:
```css
.foo {
  .bar {
    color: red;
  }
}
```

Then the AST looks like this:
```
[
  {
    kind: 'rule',
    selector: '.foo',
    nodes: [
      {
        kind: 'rule',
        selector: '.bar',
        nodes: [
          {
            kind: 'declaration',
            property: 'color',
            value: 'red',
            important: false
          }
        ]
      }
    ]
  }
]
```

When we walk this tree, and we encounter a `rule`, then we will track
the selector on a stack. When we are done walking over the rule, then we
will pop the selector from the stack. This means that the top-most
selector on the stack will always be the parent selector.
```ts
let selectorStack = []

walk(ast, {
  enter(node) {
    selectorStack.push(node.selector)
  },
  exit(node) {
    selectorStack.pop()
  },
})
```

The moment we encounter a `rule`, and if a previous rule was seen, then
we push the `selector` of the rule onto the stack, but in a way that the
`&` is already replaced by the selector. This way, a sibling rule will
also get the already-prepared parent selector.

The simple version looks like this:
```ts
walk(ast, {
  enter(node) {
    // In the real code we properly handle `&` replacement, and make sure that
    // parent selector is prepended if there is no `&` used in the selector of the
    // node.
    let selector =
      selectorStack.length > 0
        // At this point, we don't optimize anything related to the selector yet
        ? node.selector.replaceAll('&', `:is(${selectorStack.at(-1)})`)
        : node.selector

    selectorStack.push(selector)
  },
  exit(node) {
    selectorStack.pop()
  },
})
```

So far we aren't doing much yet, but the interesting part is when we
encounter a `declaration` (or a `comment`). The moment we see any of
those, then will we emit a node with the information from the
`selectorStack`.

We then also track the last node's `nodes` we created such that we can
push more declarations into it as a shortcut.

```ts
let result: AstNode[] = []
let nodes: AstNode[] | null = null
walk(ast, {
  enter(node) {
    if (node.kind === 'declaration') {
      // `nodes` is available, nothing special to do
      if (nodes) {
        nodes.push(node)
        return
      }

      // Track new nodes
      let nodes = [node]

      // Create a new node with a reference to `nodes` for future declarations
      let newNode = rule(selectorStack.at(-1), nodes)
      result.push(newNode)
    }
  },
})
```

The last important part is that whenever we see a new `rule`, then we
have to reset that `nodes` tracking variable such that we can create a
fresh node the next time we see a declaration.

For the `at-rules`, something similar happens but they are tracked in a
similar but separate stack. The idea there is that we can then wrap
those `at-rules` around the `newNode` we create. That way the at-rules
naturally float to the top.

I can keep going here, but I think if you're interested in this, then
you could go over the commits in this PR, or you can look at the
`ast.ts` implementation directly to see what's going on.

</details>

## Test plan

1. Existing tests should pass
2. New tests have been added to test the flattening of nested CSS
2026-07-07 18:28:41 +02:00
Lazizbek Ergashev
9b0e8af258
Ensure @tailwindcss/postcss rebuilds when the input CSS changes but its mtime is unchanged (#20310)
## Summary

`@tailwindcss/postcss` chooses between an incremental and a full rebuild
by comparing the mtimes of the entry file and its resolved
`@import`/`@config`/`@plugin` graph. It never looks at the input CSS
itself, so when that CSS is produced by an upstream tool (e.g. Sass) and
passed to the plugin via `process()`, it can change while the `from`
file's mtime stays the same. The plugin then re-emits its previously
cached output and silently drops the change.

This stores the input CSS per cache entry and takes the existing full
rebuild path when it differs from the previous compile, mirroring the
fix the CLI watcher already has for changed input files.

Fixes #20307

## Test plan

- Added a regression test in
`packages/@tailwindcss-postcss/src/index.test.ts` that compiles two
different inputs for the same on-disk `from` file (unchanged mtime) and
asserts the second compile reflects the new CSS.
- Confirmed it fails on `main` (the second compile returns the stale
first output) and passes with the fix.
- Ran the `@tailwindcss/postcss` package tests (all green) and checked
formatting with Prettier.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-07-06 11:32:20 +00:00
Robin Malfait
67c745efde
Fix bug in attribute selector parsing (#20303)
This PR fixes a bug in the selector parser where an attribute selector
followed by a type selector inside a compound selector resulted in the
wrong result.

Given you have this CSS:
```css
[data-foo]div {}
```

Then parsing it before this PR, would result in:
```ts
{
  kind: 'compound',
  nodes: [
    { kind: 'selector', value: '[data-foo]div' },
  ],
},
```

But with this PR, it's properly split:
```ts
{
  kind: 'compound',
  nodes: [
    { kind: 'selector', value: '[data-foo]' },
    { kind: 'selector', value: 'div' },
  ],
},
```

I also tweaked some of the comments in the parser that are unrelated,
but I was there already.

## Test plan

1. Added a regression test
2. All other tests should pass
2026-07-03 21:13:13 +02:00
한국
2683903b86
Support fractional opacity modifiers for named shadow sizes (#20302)
## Summary

`shadow-*`, `text-shadow-*`, `drop-shadow-*`, and `inset-shadow-*`
accept a bare (non-arbitrary) opacity modifier like `/50`, but the
named-size branch of all four utilities validates it with
`isPositiveInteger(candidate.modifier.value)` instead of
`isValidOpacityValue(candidate.modifier.value)` — the helper every other
opacity/alpha modifier in the codebase uses (`asColor`, used by `bg-*`,
`text-*`, `border-*`, `ring-*`, `fill-*`, `stroke-*`, `decoration-*`,
`accent-*`, `caret-*`, `outline-*`, `placeholder-*`, `divide-*`).

This means a fractional modifier like `/12.5` is silently ignored for a
*named size* (`shadow-sm/12.5` behaves exactly like `shadow-sm`,
dropping the modifier), while the exact same `/12.5` modifier works
correctly on the *color* variant of the same utility
(`shadow-red-500/12.5` → `color-mix(in oklab, var(--color-red-500)
12.5%, transparent)`), since that path already goes through `asColor`.

`drop-shadow-*` is worse: its named-size branch has an extra guard (`if
(candidate.modifier && !alpha) return`) that bails out of the *entire*
utility when a modifier is present but couldn't be resolved to an alpha
— so `drop-shadow-sm/12.5` produces no CSS at all.

This isn't a case of fractional percentages being unsupported by design
— the CHANGELOG entry that introduced `shadow-*/<alpha>` explicitly
describes it as controlling shadow **opacity**, and the color branch of
these same utilities already supports fractional values
(`shadow-red-500/2.25`, `/2.5`, `/2.75` are covered by existing tests).
The named-size branch just never got the same treatment.

**Repro** (verified with `pnpm --filter tailwindcss exec vitest run`):
- `shadow-red-500/12.5` → `color-mix(in oklab, var(--color-red-500)
12.5%, transparent)` (correct)
- `shadow-sm/12.5` → identical output to plain `shadow-sm` (modifier
silently dropped)
- `drop-shadow-sm/12.5` → **no CSS generated at all**
- `shadow-sm/50` (integer, control) → works correctly

## Fix

Replaced `isPositiveInteger(candidate.modifier.value)` with
`isValidOpacityValue(candidate.modifier.value)` in the four affected
utility definitions in `packages/tailwindcss/src/utilities.ts`
(`shadow`, `text-shadow`, `drop-shadow`, `inset-shadow`). No other
changes were needed — once `alpha` resolves correctly, `drop-shadow`'s
existing `if (candidate.modifier && !alpha) return` guard naturally
stops bailing out, since `alpha` is no longer `undefined` for valid
fractional modifiers.

## Test plan

- Added a regression test in
`packages/tailwindcss/src/utilities.test.ts` covering `shadow-sm/12.5`,
`text-shadow-sm/12.5`, `drop-shadow-sm/12.5`, and
`inset-shadow-sm/12.5`.
- Verified via `pnpm --filter tailwindcss exec vitest run` that this
test fails (modifier dropped / empty output) with the fix reverted, and
passes with it applied.
- Ran the full `tailwindcss` package test suite (`pnpm --filter
tailwindcss exec vitest run`) — 4685 tests passing, no regressions.
- Verified formatting on the changed files with `npx prettier --check`.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-07-03 12:34:02 +00:00
한국
04588b1e8f
Fix theme() in JS plugins returning unresolved object instead of DEFAULT value (#20299)
## Summary

When a CSS theme key defined via `@theme` (or a JS config's `theme`
object) shares a dash-separated prefix with a sibling key — e.g.
`--color-foo` and `--color-foo-bar` — calling `theme('colors.foo')` from
inside a JS plugin (`addUtilities`, `addComponents`, etc.) does not
resolve to the `foo` value. Instead it returns an internal
disambiguation object shaped like `{ DEFAULT: 'red', bar: 'blue',
__CSS_VALUES__: {...} }`, because there's no way to tell from CSS custom
property names alone whether `foo-bar` is a sibling key or a nested
sub-key of `foo`.

This same ambiguity was already fixed for the CSS-embedded `theme()`
function in #19097 (which unwraps to the `DEFAULT` key when present),
and the changelog entry for that PR states it fixes this "in JS configs
**and plugins**" — but the fix only touched `apply-compat-hooks.ts`'s
`resolveThemeValue`, not `createThemeFn`'s `theme` function that's
exposed directly to plugins in `plugin-functions.ts`. This PR closes
that gap by applying the same DEFAULT-unwrapping there.

Without this fix, passing the raw object into `addUtilities` (a very
natural thing to do, since a plugin author expects a string) produces
broken CSS — the reserved `DEFAULT` key gets mangled into a garbage
property name (`-d-e-f-a-u-l-t`) by the kebab-case conversion, and the
internal `__CSS_VALUES__` bookkeeping leaks into the generated
stylesheet.

### Minimal reproduction

```js
// tailwind.config.js (registered via @config, or any @plugin-registered plugin)
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(function ({ addUtilities, theme }) {
      addUtilities({
        '.example-foo': { color: theme('colors.foo') },
      })
    }),
  ],
}
```
```css
@import "tailwindcss";
@config "./tailwind.config.js";
@theme {
  --color-foo: red;
  --color-foo-bar: blue;
}
```

**Before:**
```css
.example-foo color {
  -d-e-f-a-u-l-t: red;
  bar: blue;
}
.example-foo color __CSS_VALUES__ {
  -d-e-f-a-u-l-t: 0;
  bar: 0;
}
```

**After:**
```css
.example-foo {
  color: red;
}
```

## Test plan

- Added a regression test in
`packages/tailwindcss/src/compat/plugin-api.test.ts` ("theme() resolves
the DEFAULT value when a bare CSS theme key shares a prefix with a
sibling key")
- Verified via `pnpm --filter tailwindcss exec vitest run` that this
test fails with the exact broken output shown above when the fix is
reverted, and passes once it's applied
- Ran the full `tailwindcss` package test suite (`pnpm --filter
tailwindcss exec vitest run`) — 4684 tests passing, no regressions
- Verified formatting on the changed files with `npx prettier --check`

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-07-02 13:20:14 +00:00
highoncomputers
b53fa096c9
fix: exclude iframes from focus-visible auto outline in Preflight (#20292)
## Description

Firefox's UA stylesheet already sets `iframe:focus-visible {
outline-style: none; }`, so Preflight's `:-moz-focusring { outline:
auto; }` rule overrides that and applies an unwanted auto outline to
focused iframes.

Adding `:where(:not(iframe))` to the selector preserves the improved
focus ring behavior for all other elements while respecting Firefox's
native iframe focus styling.

Fixes #19795

Edit by @RobinMalfait 

## Test plan

Before:
<img width="1054" height="383"
alt="file-f833142116d36e1e620290b97455f001"
src="https://github.com/user-attachments/assets/81aa170b-2cef-4964-934d-61f258335e1a"
/>


After:
<img width="1056" height="310"
alt="file-400d9214fedf5b43693e10580a4869de"
src="https://github.com/user-attachments/assets/8f8bb81d-6070-425d-8820-327bf9e88e79"
/>

---------

Co-authored-by: root <root@localhost.localdomain>
Co-authored-by: Kirk Loretz <kirk-loretz-fsn@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-07-02 10:39:28 +00:00
Robin Malfait
ef79119d4e
Bump dependencies (#20300)
This PR bumps most of the dependencies to the latest version. 

This also marked some dependencies using a range such that you get
updates for free during the installation:
```diff
 catalog:
-  enhanced-resolve: 5.21.6
+  enhanced-resolve: ^5.24.1

-  vite: 8.0.14
+  vite: ^8.1.2

-  webpack: 5.107.0
+  webpack: ^5.108.3
```

Fixes: #20291

## Test plan

1. All tests on all OSes still pass [ci-all]
2026-07-02 11:45:49 +02:00
Robin Malfait
e46b3d74ee
Canonicalization: make hex colors case insensitive (#20298)
This PR fixes an issue where hex-based colors in arbitrary properties
and values were considered case-sensitive even though they are
case-insensitive in CSS.

If you look at the linked issue, there is this input CSS:
```css
@theme {
  --color-brand-purple: #3f3cbb;
}
```

We expect that both `bg-[#3f3cbb]` and `bg-[#3F3CBB]` get canonicalized
to `color-brand-purple` but before this pr, only the first one would get
canonicalized that way (since it's a perfect match).

Technically a bunch more values are case-insensitive but a lot of them
_are_ sensitive so to get this 100% correct, a lot more parsing needs to
happen. I think we can start with this and expand the logic when needed.

Fixes: #20295

## Test plan

1. Added a regression test based on the linked issue
2. Added tests for arbitrary properties (`[color:#fff]` vs
`[color:#FFF]`), and tests for arbitrary properties (`bg-[#fff]` vs
`bg-[#FFF]`)
2026-07-02 00:04:44 +02:00
Robin Malfait
39656f7d9a
Add --poll option to @tailwindcss/cli (#20297)
This PR re-adds the `--poll` option to the `@tailwindcss/cli` that we
had in Tailwind CSS v3, but didn't in Tailwind CSS v4.

In Tailwind CSS v4, we started using `@parcel/watcher` instead of
chokidar for our watcher in the CLI. However, this currently doesn't
support a `--poll` option.

This PR implements our own `--poll` option such that you can use it in
environments where fs events don't work properly (e.g. Docker).

Polling can be enabled by using `--watch --poll`, in this case we will
poll every `250ms` (I'm open for a different default value). You can
also pick your own interval by using `--watch --poll 500` which is
defined in milliseconds.

The `--poll` option will be less efficient than a normal `--watch`. But
if you are in a situation where you can't use `--watch` on its own then
this is a good fallback.

One thing you can do today is run the build command manually. If you do
have some tooling that _does_ work on your machine (such as
[`watchexec`](https://github.com/watchexec/watchexec)) then you can
automatically perform a full build. The biggest downside of this
approach is that you are doing a full build every time, instead of an
incremental build.

With this PR, we try to fix that by still allowing incremental builds.
This should result in the same behavior as the normal `--watch`
function:

1. First run, will trigger a full build
1. When any of the source files changes:
- If all classes were already known, then it will be a no-op, but you
will see a log in the terminal about it.
- If a new class is detected, then the CSS will be updated, but it will
be much more efficient than a full rebuild
1. When the input CSS file changes, or any of its dependencies, then a
full rebuild will be triggered (such that your new `@utility` are
available, and `@theme` values are updated).

The implementation is a little bit more complex just because I didn't
want to spam the terminal output even if we are polling every `250ms`.

In the Oxide scanner we do track the modified times of each file. Every
`250ms` we traverse the file system and skip the files that we know
didn't change (since the mtime is the same). If the file was touched,
then we will parse it again to extract possible Tailwind CSS classes. We
will also track which files were scanned such that we can know whether
we have to trigger a full-rebuild or not (in case the input.css file or
any of its dependencies was changed).

Fixes: #18109
Fixes: #18540
Fixes: #15750

## Test plan

1. Existing tests pass
2. An integration test has been added for the `--poll` option
3. Tested it on the tailwindcss.com codebase:

<img width="679" height="320" alt="image"
src="https://github.com/user-attachments/assets/af858da5-3e07-448d-86bd-8eacdf3bf8d1"
/>

Annotated:
```
≈ tailwindcss v4.3.2

Done in 105ms                    Initial build
Done in 3ms                      Saved a file that resulted in a no-op
Done in 2ms                      Saved a file that resulted in a no-op
Done in 3ms                      Saved a file that resulted in a no-op
Done in 53ms                     Saved a file with a new class
Done in 2ms                      Saved a file that resulted in a no-op
Done in 2ms                      Saved a file that resulted in a no-op
Done in 3ms                      Saved a file that resulted in a no-op
Done in 88ms                     Saved a the input.css file
Done in 3ms                      Saved a file that resulted in a no-op
Polling for changes…
```
We check the file system every `250ms` by default, but we won't log to
prevent spamming the terminal.
2026-07-01 21:25:26 +02:00
Robin Malfait
056a155072
4.3.2 (#20281) 2026-06-29 10:10:00 -04:00
Robin Malfait
15b4a8c9af
Use version range for PostCSS (#20289)
This PR fixes an issue where new PostCSS release could lead to type
related issues if newer versions change the types.

Right now `@tailwindcss/postcss` uses a hardcoded PostCSS version. Let's
loosen this up and use a semver range instead.

Fixes: #20288

## Test plan

1. All tests still pass
2026-06-29 15:43:46 +02:00
Robin Malfait
c8b081d963
Add suggestions for named opacity modifiers (#20287)
This PR re-enables suggestions for named opacity modifiers that can be
configured via the `--opacity-*` namespace.

Before we launched v4, we got rid of this feature:
https://github.com/tailwindlabs/tailwindcss/pull/14278 &
https://github.com/tailwindlabs/tailwindcss/pull/14339
But later we re-added the feature, without updating intellisense
suggestions: https://github.com/tailwindlabs/tailwindcss/pull/15009

This PR re-adds suggestions for utilities that read from the
`--opacity-*` namespace for named modifiers. A lot of utilities use some
internal abstraction that get this for free once we use the
`colorUtility` setup, but some utilities don't use these, and I had to
add the `--opacity` modifier theme key manually.

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

## Test plan

1. Update intellisense related tests now that we read from the
`--opacity-*` namespace
2. All tests pass

Before:
<img width="1122" height="1376"
alt="file-5462b830c5ebbd6046494713344ff5ff"
src="https://github.com/user-attachments/assets/ab0771c1-da8d-4179-b19d-183c2c567606"
/>


After:

<img width="1122" height="1376"
alt="file-9247e05165f72e8bb974673ee17bd35a"
src="https://github.com/user-attachments/assets/2afdd50f-6579-4a4c-bf9b-8d402fcf1184"
/>

Notice that the `/foo` suggestion is available
2026-06-29 15:35:45 +02:00
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
Robin Malfait
2c4726d3d5
Simplify selector combinators (#20089)
This PR is a follow-up of #20088 to further improve selectors, in
particular the `combinator`.

This PR explicitly types the `combinator` as:
```ts
type Combinator =
  | ' ' // Descendant combinator
  | '>' // Child combinator
  | '+' // Next-sibling combinator
  | '~' // Subsequent-sibling combinator
```

This allows us to explicitly test for this pattern in various places,
without us having to call `.trim()` first to know what the actual
combinator was.

In the selector parser itself, we already did a `.trim()` to know
whether we were dealing with a descendant combinator or not. With this
PR, we further ensure that there is no whitespace involved aroudn these
combinators.

This introduces a small problem because we need to be able to re-print a
selector's AST. So if we don't track whitespace, we have to re-introduce
it. But there are situations where we don't want it at all (during
canonicalization).

To solve this, we introduced a `minify = false` option in the
`SelectorParser.toCss`. If it's false (the default), then we introduce
whitespace, otherwise we remove all whitespace.

## Test plan

1. All existing tests pass
2. Manual cleanup/trimming of descendants is no longer necessary

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-20 15:30:55 +02:00
Robin Malfait
fc17df05f2
Add list, compound, and complex Selector nodes (#20088)
This PR introduces a few more nodes in the `SelectorParser`:

- A `list` node
- A `complex` node
- A `compound` node

These names are closer to the CSS Selector AST names
(https://developer.mozilla.org/en-US/docs/Web/CSS/Guides/Selectors/Selector_structure),
and are also used in other libraries.

The problem today is that there are situations where we parse a selector
like: `#a.b > .c, .d` as:
```ts
[
  { kind: 'selector', value: '#a' },
  { kind: 'selector', value: '.b' },
  { kind: 'combinator', value: ' > ' },
  { kind: 'selector', value: '.c' },
  { kind: 'separator', value: ', ' },
  { kind: 'selector', value: '.d' }
]
```

Which is a very simple structure, but this contains a flaw that is
annoying to deal with in practice: In order to determine that we are
dealing with multiple selectors, we have to loop through the nodes and
see if a separator occurs somewhere.

The other fun thing is that we already know the difference between
selectors, combinators and separators. So if we tweak this structure a
little bit during parsing, then we can answer the question from above in
a much simpler way:

With this PR, we will parse the selector as:
```ts
[
  {
    kind: 'list',
    nodes: [
      {
        kind: 'complex',
        nodes: [
          {
            kind: 'compound',
            nodes: [
              { kind: 'selector', value: '#a' },
              { kind: 'selector', value: '.b' }
            ]
          },
          { kind: 'combinator', value: '>' },
          { kind: 'selector', value: '.c' }
        ]
      },
      { kind: 'selector', value: '.d' }
    ]
  }
]
```

It definitely looks more complex, but now that we have a `list` node, we
already know that we are dealing with multiple selectors.

If you squint your eyes, in the inner part there is a `compound`
selector. This is essentially a node where each sub-node can be squished
together with no spaces whatsoever.

The `complex` selector is there just to group everything together. In
other tools, a complex selector is often represented as:
```ts
{
  kind: 'complex',
  combinator: '>',
  lhs: { … },
  rhs: { … },
}
```
While I want to have the concept of a `complex` node, I didn't go with
this syntax just because I want to keep the concept of `nodes` which
means that we don't need any special handling when using `walk` (which
loops over `.nodes` internally).

The reason this complex node exists is because otherwise you would end
up with this structure:
```ts
[
  {
    kind: 'list',
    nodes: [
      {
        kind: 'compound',
        nodes: [
          { kind: 'selector', value: '#a' },
          { kind: 'selector', value: '.b' }
        ]
      },
      { kind: 'combinator', value: '>' },
      { kind: 'selector', value: '.c' }
      { kind: 'selector', value: '.d' }
    ]
  }
]
```
But if you look at the `list` node now, it's not clear that we are
dealing with `2` selectors since there are 4 nodes. We could solve this
by re-introducing the separator node (`,`). The fact that the `list`
exists tells us that we're dealing with `n` selectors. But to know which
selectors we're dealing with, then we have to look for that `,` node
again, which introduces the original problem.

This is just an internal refactor to make future changes easier.

## Test plan

1. Everything still works as expected (all tests pass)
2. No public API breaking changes, this parser was never exposed
2026-05-20 15:04:05 +02:00
Robin Malfait
cea8c9721b
Improve parsing of shadow values (#20080)
This PR fixes an issue where a `calc(…)` value used in a shadow value
was incorrectly marked as the color of that shadow.

We have this feature where we can have colored shadows, this requires us
to replace the color in a value with a `var(--tw-shadow-color,
<original-value-here>)` such that we can swap out the color.
For this, we have to parse the value and figure out what the color part.

This PR uses the `ValueParser` to parse values, then figure out what the
color part is. The biggest reason for this is that we know what a
"function" is and what a normal "word" is, which allows us to do more
fine-grained checks on a per-type basis.
We tracked a few more functions that we _know_ produce color values, and
a few cases that we know produce length values so therefore can't be the
color.

Some examples:

- `calc(…)`, `min(…)`, `max(…)`, `clamp(…)`, `--spacing(…)` — these all
produce length-values.
- `color(…)`, `color-mix(…)`, `rgba?(…)`, `--alpha(…)` … — these all
produce color values.

Notice that we added `--spacing(…)` and `--alpha(…)` as well, these are
custom functions that Tailwind CSS provides, but we know what it will
eventually map to internally.

Last but not least, we also detect named colors and hex-based colors.

Fixes: #20065 
Closes: #20074

## Test plan

1. Added an integration test, before the fix the test would've looked
lik this:

  ```diff
    .drop-shadow-calc {
- --tw-drop-shadow-size: drop-shadow(0 0 calc(1 * var(--spacing))
var(--tw-drop-shadow-color, black));
+ --tw-drop-shadow-size: drop-shadow(0 0 var(--tw-drop-shadow-color,
calc(1 * var(--spacing))) black);
      --tw-drop-shadow: drop-shadow(var(--drop-shadow-calc));
filter: var(--tw-blur, ) var(--tw-brightness, ) var(--tw-contrast, )
var(--tw-grayscale, ) var(--tw-hue-rotate, ) var(--tw-invert, )
var(--tw-saturate, ) var(--tw-sepia, ) var(--tw-drop-shadow, );
    }
  ```

2. Added more tests related to the shadow replacement logic itself where
we try to infer the color (or the length-values for x, y, blur, spread)
3. All other existing tests pass
2026-05-19 13:47:50 +02:00
Robin Malfait
36417cbd12
Properly negate custom variants with @container when used with not-* (#20059)
This PR fixes an issue where a custom variant declared with a
`@container` wasn't properly negated when using it in combination with
the `not-*` variant.

Given this CSS:
```css
@custom-variant has-a {
  @container style(--a) {
    @slot;
  }
}
```

If you then used `not-has-a:flex`, then the following CSS was produced:
```css
.not-has-a\:flex {
  @container style(--a) not {
    display: flex;
  }
}
```
But we expect the `not` to be in the correct location:
```css
.not-has-a\:flex {
  @container not style(--a) {
    display: flex;
  }
}
```

The issue was that we did some string related checks, and we assumed
that the `query` part of the `@container` (`style(--a)`) had to start
with a `(` character.

To fix this, we now parse the value to an AST, and verify the AST shape
before manipulating it. This now checks whether the `query` part is a
function (both `(…)` and `style(…)` are considered functions).

Also added some additional tests that were already handled, these cases
look like:

- `@container {query}`
- `@container not {query}`
- `@container {name} not {query}`
- `@container {name} {query}`


Fixes: #20058

## Test plan

1. Added a failing test for the use case of the linked issue.
2. Added a few more additional tests to explicitly track some use cases
we handled already but didn't track via tests.
3. All other tests still pass as expected.
2026-05-15 00:08:10 +02:00
Jordan Pittman
460a008120
v4: Allow @apply to be used with CSS mixins (#19427)
The CSS Custom Functions and Mixins spec [is using `@apply` with dashed
idents](https://drafts.csswg.org/css-mixins-1/#apply-rule) for native
mixin support. We shouldn't attempt to treat these as utilities and try
to compile them since they'll fail.

There one intentional limitation with regards to mixins and our use of
`@apply`: We do not allow users to mix utilities and mixins in the same
at-rule. In other words, all of the following `@apply` rules are
invalid:

```css
.foo {
  /* Invalid because the rules contain both mixins and utilities */
  @apply --my-mixin underline;
  @apply --my-mixin() underline;
  @apply underline --my-mixin;
  @apply underline --my-mixin();
}
```

Aside: Lightning CSS does not yet support this syntax so the results of
a production build won't produce the correct code but we'll at least
handle these correctly in Tailwind CSS itself.

Fixes #19422

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-05-14 14:22:26 +00:00
Robin Malfait
0db226a222
Add Vue integration test with 1000 components (#20055)
This PR adds an integration test with Vue where we use a 1000 components
and where each component references a CSS file via `@reference`. Each
component has a unique class that uses `@apply`.

There are some discussions in
https://github.com/tailwindlabs/tailwindcss/discussions/16429 that
mention that this causes OOM issues. Right now I can't reproduce that,
and even with a 1000 components, it produces CSS in a reasonable time:
```
vite v7.3.3 building client environment for production...
✓ 2011 modules transformed.
dist/index.html                   0.23 kB │ gzip:  0.18 kB
dist/assets/index-DNVNFkYQ.css  106.65 kB │ gzip: 10.79 kB
dist/assets/index-B8v7EbAN.js   223.84 kB │ gzip: 49.81 kB
✓ built in 3.17s
```

I also started a Vite server and triggered file changes to see if the
memory would grow forever, which it didn't. After a 1000 changes,
everything still behaves smoothly:
<img width="1694" height="1856" alt="image"
src="https://github.com/user-attachments/assets/b16800ae-4dce-4f0d-9d97-25f4cab21c1c"
/>

Making changes manually to a single component, result in proper HMR
request that update the browser:


https://github.com/user-attachments/assets/5c79ffc6-2329-4341-9d25-82b000093e31

This test is here to make sure that it keeps working in the future.

---

If I remove all `@reference` references, and usages of `@apply`, then
the build time is indeed faster:
```
vite v7.3.3 building client environment for production...
✓ 2011 modules transformed.
dist/index.html                   0.23 kB │ gzip:  0.18 kB
dist/assets/index-CcxXccJ1.css  106.61 kB │ gzip: 10.76 kB
dist/assets/index-M92YFF0G.js   223.84 kB │ gzip: 49.81 kB
✓ built in 1.97s
```

So we go from `1.97s` → `3.17s`, which is a `1.2s` increase when you use
`@reference` with `@apply` in 1000 files for a fresh build.

I also saw some comments about the CSS growing whenever `@reference` was
used, but as you can see in the snippets above they are at a stable
size.

## Test plan

1. All tests still pass

[ci-all] For testing on Windows / macOS

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-14 15:38:51 +02:00
Robin Malfait
559f92bbf8
Harden workflow security (#20054)
This PR improves the workflows a bit more by:

1. Making sure that we always use `pnpm install` with a frozen lockfile
2. Cleanup permissions
3. By not caching `~/.cargo/bin/`

## Test plan

1. Every test should still pass

[ci-all]
2026-05-14 13:00:37 +02:00
depfu[bot]
6ee13978e6
Update all of react 19.2.5 → 19.2.6 (patch) (#20050)
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.5 → 19.2.6) ·
[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.6">19.2.6</a></h4>

<blockquote><h2 dir="auto">React Server Components</h2>
<ul dir="auto">
<li>Type hardening and performance improvements<br>
(<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/36425">#36425</a>
by <a href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a>
and <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="23f4f9f30d...eaf3e95ca9">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="eaf3e95ca9"><code>Version
19.2.6</code></a></li>
<li><a
href="795203e75e"><code>[FlightReply]
Type hardening and performance improvements</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-05-14 12:41:48 +02:00
Serge Yudin
a2b89a7012
Fix canonicalize crash with plugin component values (#20052)
## Summary

Fixes #20051.

The collapse canonicalization pass speculatively checks compatible
functional utility roots. When one of those roots comes from a plugin
registered with `matchComponents`/`matchUtilities`, the speculative
candidate can call the plugin callback with an arbitrary value that is
not present in the configured `values` map. Plugins such as the Phoenix
Heroicons helper expect the mapped value shape and can throw while
canonicalize is only probing possible replacements.

This change skips speculative replacement utilities whose property
lookup throws, matching the best-effort behavior already used by utility
signature generation. The original candidates are preserved instead of
crashing canonicalization.

## Test plan

- `source ~/.nvm/nvm.sh && nvm use 22.14.0 && pnpm vitest run
packages/tailwindcss/src/canonicalize-candidates.test.ts -t "does not
crash when plugin matchComponents rejects speculative values during
collapse"`
- `source ~/.nvm/nvm.sh && nvm use 22.14.0 && pnpm vitest run
packages/tailwindcss/src/canonicalize-candidates.test.ts`
- `source ~/.nvm/nvm.sh && nvm use 22.14.0 && pnpm prettier --check
packages/tailwindcss/src/canonicalize-candidates.ts
packages/tailwindcss/src/canonicalize-candidates.test.ts`

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-05-14 09:59:19 +00:00
Robin Malfait
eefe64593a
Cleanup tests (#20053)
This PR does some cleanup work in the tests such that every test is
using the custom test helpers instead of creating the compiler itself
and using other test helpers such as `optimizeCss` or `pretty`.

For tests we have some helpers that introduce a small layer of
indirection. While you typically want to avoid indirection, this one
allows us to keep the tests the same whenever we make changes to the
internals.

It also allows us to run through the full core flow where we have to
setup a compiler, pass in the CSS, compile the candidates, optimize the
CSS and pretty print it for snapshot purposes.

Our tests often look like this when you are testing some candidates:
```ts
test('…', async () => {
  expect(await run(['flex', 'hover:flex'])).toMatchInlineSnapshot(`
    "
    .flex {
      display: flex;
    }

    @media (hover: hover) {
      .hover\\:flex:hover {
        display: flex;
      }
    }
    "
  `)
})
```

If you need to compile some CSS, we used the following:
```ts
test('…', async () => {
  expect(
    await compileCss(css`
      @tailwind utilities;
      .foo {
        @apply flex;
      }
    `),
  ).toMatchInlineSnapshot(`
    "
    .foo {
      display: flex;
    }
    "
  `)
})
```

With this PR, I normalized the tests by changing the signatures of those
tests slightly:
```ts
export async function run(
  // A list of candidates
  candidates: string[],

  // Optionally, custom CSS
  input = css`
    @tailwind utilities;
  `,

  // Optionally, custom compile options
  options: Parameters<typeof compile>[1] = {},
) {
  let { build } = await compile(input, options)
  return pretty(optimize(build(candidates)).code)
}

// Same as above, but without the candidates
export async function compileCss(css: string, options: Parameters<typeof compile>[1] = {}) {
  return run([], css, options)
}
```

They are very similar, but if we migrate _everything_ to `run`, then
there will be situations where you have to use:
```ts
test('…', async () => {
  expect(
    await run(
      [],
      css`
        @tailwind utilities;
        .foo {
          @apply flex;
        }
      `,
    ),
  ).toMatchInlineSnapshot(`
    "
    .foo {
      display: flex;
    }
    "
  `)
})
```

Which is a little bit confusing because what does `[]` even mean?

The next big change is that a lot of the tests were manually setting up
the compiler, passing in the CSS and compiler options, then building the
candidates, then manually optimizing the CSS and then manually pretty
printing the resulting CSS for snapshot purposes.

Other tests, didn't use all those steps and skipped the optimization
step for example. This means that not all tests were testing the
end-to-end core workflow.

This PR uses the tests helpers wherever we could. This now also means
that our tests look the same as much as possible.

The biggest goal of this was to get everything in a similar state.
Future PRs that add additional optimizations will now see those
optimizations reflected in the actual test output.

## Test plan

1. All tests still pass
- Some test _output_ has changed because the `optimizeCss` will now kick
in. But this reflects production better anyway.
2. No source code was changed
2026-05-14 11:43:34 +02:00
depfu[bot]
ece4824f0d
Update jiti 2.6.1 → 2.7.0 (minor) (#20044)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ jiti (2.6.1 → 2.7.0) · [Repo](https://github.com/unjs/jiti) ·
[Changelog](https://github.com/unjs/jiti/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/unjs/jiti/releases/tag/v2.7.0">2.7.0</a></h4>

<blockquote><p dir="auto"><a
href="https://bounce.depfu.com/github.com/unjs/jiti/compare/v2.6.1...v2.7.0">compare
changes</a></p>
<h3 dir="auto">🚀 Enhancements</h3>
<ul dir="auto">
<li>Add explicit resource management (<code
class="notranslate">using</code>/<code class="notranslate">await
using</code>) support (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/422">#422</a>)</li>
<li>Support opt-in <code class="notranslate">tsconfigPaths</code> (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/427">#427</a>)</li>
<li>Support virtual modules (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/428">#428</a>)</li>
<li>Add <code class="notranslate">jiti/static</code> subpath (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/430">#430</a>)</li>
</ul>
<h3 dir="auto">🔥 Performance</h3>
<ul dir="auto">
<li>
<strong>interopDefault:</strong> Add caching to reduce proxy overhead by
~2x (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/421">#421</a>)</li>
</ul>
<h3 dir="auto">🩹 Fixes</h3>
<ul dir="auto">
<li>
<strong>require:</strong> Passthrough resolve options (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/412">#412</a>)</li>
<li>
<strong>require:</strong> Fallback to transpilation when <code
class="notranslate">tryNative</code> fails (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/413">#413</a>)</li>
<li>Fallback for <code class="notranslate">ENAMETOOLONG</code> when
evaluating esm (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/429">#429</a>)</li>
</ul>
<h3 dir="auto">📦 Build</h3>
<ul dir="auto">
<li>Upgrade rspack to v2 (<a
href="55194fb">55194fb</a>)</li>
<li>Experimental rolldown config (<a
href="8c0243f">8c0243f</a>)</li>
</ul>
<h3 dir="auto">✅ Tests</h3>
<ul dir="auto">
<li>Ignore jsx test for bun/cjs (<a
href="3a744ca">3a744ca</a>)</li>
</ul>
<h3 dir="auto">❤️ Contributors</h3>
<ul dir="auto">
<li>Pooya Parsa (<a
href="https://bounce.depfu.com/github.com/pi0">@pi0</a>)</li>
<li>Kricsleo (<a
href="https://bounce.depfu.com/github.com/kricsleo">@kricsleo</a>)</li>
<li>Espen Hovlandsdal (<a
href="https://bounce.depfu.com/github.com/rexxars">@rexxars</a>)</li>
<li>Rintaro Itokawa (<a
href="https://bounce.depfu.com/github.com/re-taro">@re-taro</a>)</li>
<li>Matteo Collina (<a
href="https://bounce.depfu.com/github.com/mcollina">@mcollina</a>)</li>
<li>Mario Zechner (<a
href="https://bounce.depfu.com/github.com/badlogic">@badlogic</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="aedcdee7fe...fd3bb289b7">See
the full diff on Github</a>. The new version differs by 29 commits:</p>
<ul>
<li><a
href="fd3bb289b7"><code>chore(release):
v2.7.0</code></a></li>
<li><a
href="27fe3f2a49"><code>chore:
update release script</code></a></li>
<li><a
href="4fcd2f23aa"><code>fix:
fallback for `ENAMETOOLONG` when evaluating esm (#429)</code></a></li>
<li><a
href="8c0243f14e"><code>build:
experimental rolldown config</code></a></li>
<li><a
href="55194fbb97"><code>build:
upgrade rspack</code></a></li>
<li><a
href="0abda72c11"><code>ci:
update node test matrix</code></a></li>
<li><a
href="8c7822ef2f"><code>chore:
update tsconfig</code></a></li>
<li><a
href="08fc868c92"><code>chore:
update deps</code></a></li>
<li><a
href="5d552e3beb"><code>feat:
add `jiti/static` export (#430)</code></a></li>
<li><a
href="ae790b0214"><code>feat:
support virtual modules option (#428)</code></a></li>
<li><a
href="a3e705dbdc"><code>fix(require):
fallback to transpilation when `tryNative` fails (#413)</code></a></li>
<li><a
href="4deba16283"><code>chore:
update agents.md</code></a></li>
<li><a
href="f85b0e523d"><code>lint</code></a></li>
<li><a
href="3ce242653f"><code>feat:
support opt-in `tsconfigPaths` (#427)</code></a></li>
<li><a
href="c49c54e42e"><code>chore:
init agents.md</code></a></li>
<li><a
href="b66bd233c2"><code>feat:
add explicit resource management (using/await using) support
(#422)</code></a></li>
<li><a
href="a467d31ffc"><code>perf(interopDefault):
add caching to reduce proxy overhead by ~2x (#421)</code></a></li>
<li><a
href="fe264b49f1"><code>fix(ci):
skip `--coverage` flag for node 18</code></a></li>
<li><a
href="058d91a338"><code>chore:
lint</code></a></li>
<li><a
href="650bc48a76"><code>chore:
add missing prettier dep</code></a></li>
<li><a
href="e28b5e9645"><code>chore(deps):
update autofix-ci/action digest to 7a166d7 (#424)</code></a></li>
<li><a
href="31096aa9e2"><code>chore(deps):
update actions/checkout action to v6 (#419)</code></a></li>
<li><a
href="498e8d73a4"><code>chore:
update deps</code></a></li>
<li><a
href="9ee314fd0f"><code>test:
update</code></a></li>
<li><a
href="3a744ca271"><code>test:
ignore jsx test for bun/cjs</code></a></li>
<li><a
href="e88ac44079"><code>chore:
update deps</code></a></li>
<li><a
href="4045c7a2a2"><code>chore:
fix lint issues</code></a></li>
<li><a
href="39e86de57c"><code>chore(deps):
update actions/setup-node action to v6 (#411)</code></a></li>
<li><a
href="ddb4683d37"><code>fix(require):
passthrough resolve options (#412)</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-05-13 12:02:13 +02:00
depfu[bot]
57521613ef
Update postcss 8.5.10 → 8.5.14 (patch) (#20040)
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?




#### ✳️ postcss (8.5.10 → 8.5.14) ·
[Repo](https://github.com/postcss/postcss) ·
[Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.14">8.5.14</a></h4>

<blockquote><ul dir="auto">
<li>Fixed custom syntax regression (by <a
href="https://bounce.depfu.com/github.com/43081j">@43081j</a>).</li>
</ul></blockquote>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.13">8.5.13</a></h4>

<blockquote><ul dir="auto">
<li>Fixed <code class="notranslate">postcss-scss</code> commend
regression.</li>
</ul></blockquote>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.12">8.5.12</a></h4>

<blockquote><ul dir="auto">
<li>Fixed reading any file via user-generated CSS.</li>
<li>Added <code class="notranslate">opts.unsafeMap</code> to disable
checks.</li>
</ul></blockquote>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.11">8.5.11</a></h4>

<blockquote><ul dir="auto">
<li>Fixed nested brackets parsing performance (by <a
href="https://bounce.depfu.com/github.com/offset">@offset</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="33b9790263...3ec13948ae">See
the full diff on Github</a>. The new version differs by 21 commits:</p>
<ul>
<li><a
href="3ec13948ae"><code>Release
8.5.14 version</code></a></li>
<li><a
href="f2bb827b20"><code>Update
dependencies</code></a></li>
<li><a
href="d75953d608"><code>Merge
pull request #2084 from 43081j/raw-raws-rawing</code></a></li>
<li><a
href="68bd2139b5"><code>fix:
always call `raw` to retrieve raw values</code></a></li>
<li><a
href="af58cf1b7a"><code>Release
8.5.13 version</code></a></li>
<li><a
href="f227dbd0e9"><code>Temporary
ignore pnpm 11 config</code></a></li>
<li><a
href="d3abd40d72"><code>Update
dependencies</code></a></li>
<li><a
href="dd06c3e113"><code>Revert
stringifier changes because of the conflict with
postcss-scss</code></a></li>
<li><a
href="ae889c815f"><code>Try
to fix CI</code></a></li>
<li><a
href="e0093e49bc"><code>Move
to pnpm 11</code></a></li>
<li><a
href="9bc81c48f0"><code>Release
8.5.12 version</code></a></li>
<li><a
href="85c4d7dab8"><code>Another
try to fix coverage</code></a></li>
<li><a
href="94484cae6d"><code>Try
to fix coverage</code></a></li>
<li><a
href="c64b7488d2"><code>Load
only .map source maps</code></a></li>
<li><a
href="aaec7b78b3"><code>Avoid
throwing JSON parsing errors for non-JSON source maps</code></a></li>
<li><a
href="233fb264ea"><code>Mention
original author of the solution</code></a></li>
<li><a
href="2502f75030"><code>Release
8.5.11 version</code></a></li>
<li><a
href="5ca1901949"><code>Speed
up parsing many nested brackets</code></a></li>
<li><a
href="42b5337dd7"><code>Update
dependencies</code></a></li>
<li><a
href="7e36e153d0"><code>Cache
node.raws locally in Stringifier hot methods</code></a></li>
<li><a
href="8ec62b157b"><code>Bypass
MapGenerator for no-source-map stringify in LazyResult</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-05-12 15:21:44 +00:00
depfu[bot]
bcd581c568
🚨 [security] Update next 16.2.4 → 16.2.6 (patch) (#20039)
<hr>

🚨 <b>Your current dependencies have known security vulnerabilities</b> 🚨

This dependency update fixes known security vulnerabilities. Please see
the details below and assess their impact carefully. We recommend to
merge and deploy this as soon as possible!
<hr>



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.4 → 16.2.6) ·
[Repo](https://github.com/vercel/next.js)


<details>

<summary>Security Advisories 🚨</summary>
<h4><a
href="https://bounce.depfu.com/github.com/facebook/react/security/advisories/GHSA-rv78-f8rc-xrxh">🚨
Next.js Vulnerable to Denial of Service with Server Components</a></h4>
<blockquote><p dir="auto">A vulnerability affects certain React Server
Components packages for versions 19.x and frameworks that use the
affected packages, including Next.js 13.x, 14.x, 15.x, and 16.x using
the App Router. The issue is tracked upstream as <a
href="https://bounce.depfu.com/github.com/facebook/react/security/advisories/GHSA-rv78-f8rc-xrxh">CVE-2026-23870</a>.</p>
<p dir="auto">A specially crafted HTTP request can be sent to any App
Router Server Function endpoint that, when deserialized, may trigger
excessive CPU usage. This can result in denial of service in unpatched
environments.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-36qx-fr4f-26g5">🚨
Next.js has a Middleware / Proxy bypass in Pages Router applications
using i18n</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">Applications using the Pages Router with <code
class="notranslate">i18n</code> configured and middleware/proxy-based
authorization can allow unauthorized access to protected page data
through locale-less <code
class="notranslate">/_next/data/&lt;buildId&gt;/&lt;page&gt;.json</code>
requests. In affected configurations, middleware does not run for the
unprefixed data route, allowing an attacker to retrieve SSR JSON for
protected pages without passing the intended authorization checks.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">The matcher logic was updated to perform the same match as
it would on a non-i18n data route.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, enforce authorization
in the page's server-side data path instead of relying solely on
middleware.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">🚨
Next.js has a Middleware / Proxy bypass in App Router applications via
segment-prefetch routes</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">App Router applications that rely on middleware or
proxy-based checks for authorization can allow unauthorized access
through transport-specific route variants used for segment prefetching.
In affected configurations, specially crafted <code
class="notranslate">.rsc</code> and segment-prefetch URLs can resolve to
the same page without being matched by the intended middleware rule,
which can allow protected content to be reached without the expected
authorization check.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now include App Router transport variants when
generating middleware matchers, so middleware protections are applied
consistently to those requests as well as to the normal page URL.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, enforce authorization
in the underlying route or page logic instead of relying solely on
middleware.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-wfc6-r584-vfw7">🚨
Next.js vulnerable to cache poisoning in React Server Component
responses</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">Applications using React Server Components can be
vulnerable to cache poisoning when shared caches do not correctly
partition response variants. Under affected conditions, an attacker can
cause an RSC response to be served from the original URL and poison
shared cache entries so later visitors receive component payloads
instead of the expected HTML.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now validate and interpret <code
class="notranslate">RSC</code> request headers consistently across
request classification and rendering, and we enforce the intended
cache-busting behavior so RSC payloads are not unexpectedly served from
the original URL.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, ensure your CDN or
reverse proxy keys on the relevant RSC request headers and honors <code
class="notranslate">Vary</code>, or disable shared caching for affected
App Router and RSC responses.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-492v-c6pp-mqqv">🚨
Next.js has a Middleware / Proxy bypass through dynamic route parameter
injection</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">Applications that rely on middleware to protect dynamic
routes can be vulnerable to authorization bypass. In affected
deployments, specially crafted query parameters can alter the dynamic
route value seen by the page while leaving the visible path unchanged,
which can allow protected content to be rendered without passing the
expected middleware check.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now only honor internal route-parameter normalization
in trusted routing flows and ignore externally supplied parameter
encodings that should never have been accepted from ordinary
requests.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, enforce authorization
in route or page logic instead of relying solely on middleware path
matching.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-c4j6-fc7j-m34r">🚨
Next.js vulnerable to server-side request forgery in applications using
WebSocket upgrades</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">Self-hosted applications using the built-in Node.js server
can be vulnerable to server-side request forgery through crafted
WebSocket upgrade requests. An attacker can cause the server to proxy
requests to arbitrary internal or external destinations, which may
expose internal services or cloud metadata endpoints. Vercel-hosted
deployments are not affected.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now apply the same safety checks to WebSocket upgrade
handling that already existed for normal HTTP requests, so upgrade
requests are only proxied when routing has explicitly marked them as
safe external rewrites.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, do not expose the
origin server directly to untrusted networks. If WebSocket upgrades are
not required, block them at your reverse proxy or load balancer, and
restrict origin egress to internal networks and metadata services where
possible.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-h64f-5h5j-jqjh">🚨
Next.js has a Denial of Service in the Image Optimization API</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">When self-hosting Next.js with the default image loader,
the Image Optimization API fetches local images entirely into memory
without enforcing a maximum size limit. An attacker could cause
out-of-memory conditions by requesting large local assets from the <code
class="notranslate">/_next/image</code> endpoint that match the <code
class="notranslate">images.localPatterns</code> configuration (by
default, all patterns are allowed).</p>
<ul dir="auto">
<li>If you are using <code
class="notranslate">images.localPatterns</code>, only the patterns in
that array are impacted.</li>
<li>If you are using <code class="notranslate">images.unoptimized:
true</code>, you are NOT impacted.</li>
<li>If you are using <code class="notranslate">images.loader:
'custom'</code>, you are NOT impacted.</li>
<li>If you are using Vercel, you are NOT impacted.</li>
</ul>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now apply response size limits consistently to internal
image fetches, not just external ones, and fail oversized responses
before they can exhaust process memory.</p>
<p dir="auto">This can be adjusted using the <code
class="notranslate">images.maximumResponseBody</code> configuration.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, avoid routing large
local assets through <code class="notranslate">/_next/image</code>,
disable image optimization for large or untrusted local files, or block
image optimization access to those assets at the edge.</p>
<p dir="auto">You can disable using the <code
class="notranslate">images.localPatterns: []</code> configuration. This
will still allow fetching remote images (which is not
impacted).</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-mg66-mrh9-m8jx">🚨
Next.js vulnerable to Denial of Service via connection exhaustion in
applications using Cache Components</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">Applications using Partial Prerendering through the Cache
Components feature can be vulnerable to connection exhaustion through
crafted POST requests to a server action. In affected configurations, a
malicious request can trigger a request-body handling deadlock that
leaves connections open for an extended period, consuming file
descriptors and server capacity until legitimate users are denied
service.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now treat the header used for resuming Partial
Prerendered requests as an internal-only header and strip it from
untrusted incoming requests. This header should never be accepted
directly from external clients.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, block requests that
would be handled by Next.js if they contain the <code
class="notranslate">Next-Resume</code> header at the
edge.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-gx5p-jg67-6x7h">🚨
Next.js has cross-site scripting in beforeInteractive scripts with
untrusted input</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">Applications that use <code
class="notranslate">beforeInteractive</code> scripts together with
untrusted content can be vulnerable to cross-site scripting. In affected
versions, serialized script content was not escaped safely before being
embedded into the document, which could allow attacker-controlled input
to break out of the intended script context and execute arbitrary
JavaScript in a visitor's browser.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now HTML-escape serialized <code
class="notranslate">beforeInteractive</code> script content before
embedding it into the page, preventing attacker-controlled content from
breaking out of the inline script boundary.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, do not pass untrusted
data into <code class="notranslate">beforeInteractive</code> scripts. If
that pattern is unavoidable, sanitize or escape the content before
embedding it.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-vfv6-92ff-j949">🚨
Next.js vulnerable to cache poisoning via collisions in React Server
Component cache-busting</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">React Server Component responses can be vulnerable to
cache poisoning in deployments that rely on shared caches with
insufficient response partitioning. In affected conditions, collisions
in the <code class="notranslate">_rsc</code> cache-busting value can
allow an attacker to poison cache entries so users receive the wrong
response variant for a given URL.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We strengthened the <code class="notranslate">_rsc</code>
cache-busting mechanism to make practical collisions significantly
harder and to better separate response variants that should not share
cache entries.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, ensure intermediary
caches correctly honor <code class="notranslate">Vary</code> for
RSC-related request headers, or disable shared caching for affected RSC
responses until you can deploy a patched release.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-ffhc-5mcf-pf4q">🚨
Next.js vulnerable to cross-site scripting in App Router applications
using CSP nonces</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">App Router applications that rely on CSP nonces can be
vulnerable to stored cross-site scripting when deployed behind shared
caches. In affected versions, malformed nonce values derived from
request headers could be reflected into rendered HTML in an unsafe way,
allowing an attacker to poison cached responses and cause script
execution for later visitors.</p>
<h3 dir="auto">Fix</h3>
<p dir="auto">We now reject or ignore malformed nonce values before they
are embedded into HTML and apply stricter nonce sanitization so
request-derived nonce data cannot break out of the intended attribute
context.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">If you cannot upgrade immediately, strip inbound <code
class="notranslate">Content-Security-Policy</code> request headers from
untrusted traffic.</p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-3g8h-86w9-wvmq">🚨
Next.js's Middleware / Proxy redirects can be cache-poisoned</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">Next.js uses the <code
class="notranslate">x-nextjs-data</code> request header for internal
data requests. On affected versions, an external client could send this
header on a normal request to a path handled by middleware that returns
a redirect.</p>
<p dir="auto">When that happened, the middleware/proxy could treat the
request as a data request and replace the standard <code
class="notranslate">Location</code> redirect header with the internal
<code class="notranslate">x-nextjs-redirect</code> header. Browsers do
not follow <code class="notranslate">x-nextjs-redirect</code>, so the
response became an unusable redirect for normal clients.</p>
<p dir="auto">If the application was deployed behind a CDN or reverse
proxy that caches 3xx responses without varying on this header, a single
attacker request could poison the cached redirect response for the
affected path. Subsequent visitors could then receive a cached redirect
response without a <code class="notranslate">Location</code> header,
causing a denial of service for that redirect path until the cache entry
expired or was purged.</p>
<h3 dir="auto">Affected scenarios</h3>
<p dir="auto">This affects applications that:</p>
<ul dir="auto">
<li>use middleware or proxy redirects</li>
<li>are deployed behind a caching CDN or reverse proxy</li>
<li>allow 3xx responses on those paths to be cached without
differentiating internal data requests from normal requests</li>
</ul>
<h3 dir="auto">Fix</h3>
<p dir="auto">The fix stops trusting <code
class="notranslate">x-nextjs-data</code> by itself for middleware
redirect handling. A request is now treated as an internal data request
only when it is validated as such by internal routing state, preserving
legitimate data-request redirect behavior while preventing external
header injection from changing normal redirect responses.</p>
<h3 dir="auto">Workarounds</h3>
<p dir="auto">Before upgrading, users can reduce risk by:</p>
<ul dir="auto">
<li>configuring the CDN or reverse proxy to vary its cache key on <code
class="notranslate">x-nextjs-data</code> for affected responses</li>
</ul></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">🚨
Next.js has a Middleware / Proxy bypass in App Router applications via
segment-prefetch routes - Incomplete Fix Follow-Up</a></h4>
<blockquote><h3 dir="auto">Impact</h3>
<p dir="auto">It was found that the fix addressing <a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">CVE-2026-44575</a>
did not apply to <code class="notranslate">middleware.ts</code> with
Turbopack. Refer to <a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">CVE-2026-44575</a>
for further details.</p>
<h3 dir="auto">References</h3>
<ul dir="auto">
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">CVE
CVE-2026-44575</a></li>
</ul></blockquote>
</details>

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

<blockquote><p dir="auto">This release contains security fixes for the
following advisories:</p>
<p dir="auto">High:</p>
<ul dir="auto">
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-8h8q-6873-q5fj">GHSA-8h8q-6873-q5fj:
Denial of Service with Server Components</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">GHSA-267c-6grr-h53f:
Middleware / Proxy bypass in App Router applications via
segment-prefetch routes</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-26hh-7cqf-hhc6">GHSA-26hh-7cqf-hhc6:
Middleware / Proxy bypass in App Router applications via
segment-prefetch routes - Incomplete Fix Follow-Up</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-mg66-mrh9-m8jx">GHSA-mg66-mrh9-m8jx:
Denial of Service via connection exhaustion in applications using Cache
Components</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-492v-c6pp-mqqv">GHSA-492v-c6pp-mqqv:
Middleware / Proxy bypass through dynamic route parameter
injection</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-c4j6-fc7j-m34r">GHSA-c4j6-fc7j-m34r:
Server-side request forgery in applications using WebSocket
upgrades</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-36qx-fr4f-26g5">GHSA-36qx-fr4f-26g5:
Middleware / Proxy bypass in Pages Router applications using
i18n</a></li>
</ul>
<p dir="auto">Moderate:</p>
<ul dir="auto">
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-ffhc-5mcf-pf4q">GHSA-ffhc-5mcf-pf4q:
Cross-site scripting in App Router applications using CSP
nonces</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-gx5p-jg67-6x7h">GHSA-gx5p-jg67-6x7h:
Cross-site scripting in beforeInteractive scripts with untrusted
input</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-h64f-5h5j-jqjh">GHSA-h64f-5h5j-jqjh:
Denial of Service in the Image Optimization API</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-wfc6-r584-vfw7">GHSA-wfc6-r584-vfw7:
Cache poisoning in React Server Component responses</a></li>
</ul>
<p dir="auto">Low:</p>
<ul dir="auto">
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-vfv6-92ff-j949">GHSA-vfv6-92ff-j949:
Cache poisoning via collisions in React Server Component
cache-busting</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-3g8h-86w9-wvmq">GHSA-3g8h-86w9-wvmq:
Middleware / Proxy redirects can be cache-poisoned</a></li>
</ul></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v16.2.5">16.2.5</a></h4>

<blockquote><p dir="auto">This release contains security fixes for the
following advisories:</p>
<p dir="auto">High:</p>
<ul dir="auto">
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-8h8q-6873-q5fj">GHSA-8h8q-6873-q5fj:
Denial of Service with Server Components</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-267c-6grr-h53f">GHSA-267c-6grr-h53f:
Middleware / Proxy bypass in App Router applications via
segment-prefetch routes</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-mg66-mrh9-m8jx">GHSA-mg66-mrh9-m8jx:
Denial of Service via connection exhaustion in applications using Cache
Components</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-492v-c6pp-mqqv">GHSA-492v-c6pp-mqqv:
Middleware / Proxy bypass through dynamic route parameter
injection</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-c4j6-fc7j-m34r">GHSA-c4j6-fc7j-m34r:
Server-side request forgery in applications using WebSocket
upgrades</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-36qx-fr4f-26g5">GHSA-36qx-fr4f-26g5:
Middleware / Proxy bypass in Pages Router applications using
i18n</a></li>
</ul>
<p dir="auto">Moderate:</p>
<ul dir="auto">
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-ffhc-5mcf-pf4q">GHSA-ffhc-5mcf-pf4q:
Cross-site scripting in App Router applications using CSP
nonces</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-gx5p-jg67-6x7h">GHSA-gx5p-jg67-6x7h:
Cross-site scripting in beforeInteractive scripts with untrusted
input</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-h64f-5h5j-jqjh">GHSA-h64f-5h5j-jqjh:
Denial of Service in the Image Optimization API</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-wfc6-r584-vfw7">GHSA-wfc6-r584-vfw7:
Cache poisoning in React Server Component responses</a></li>
</ul>
<p dir="auto">Low:</p>
<ul dir="auto">
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-vfv6-92ff-j949">GHSA-vfv6-92ff-j949:
Cache poisoning via collisions in React Server Component
cache-busting</a></li>
<li><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-3g8h-86w9-wvmq">GHSA-3g8h-86w9-wvmq:
Middleware / Proxy redirects can be cache-poisoned</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="2275bd8598...ee6e79b179">See
the full diff on Github</a>. The new version differs by 36 commits:</p>
<ul>
<li><a
href="ee6e79b179"><code>v16.2.6</code></a></li>
<li><a
href="afa053d9eb"><code>Turbopack:
Match proxy matchers with webpack implementation
(#93594)</code></a></li>
<li><a
href="97a154e5bb"><code>Turbopack:
Fix middleware matcher suffix (#93590)</code></a></li>
<li><a
href="83899bc891"><code>[backport]
Disable build caches for production/staging/force-preview deploys
(#93586)</code></a></li>
<li><a
href="7b222b9095"><code>[backport][test]
Pin package manager to patch versions (#93595)</code></a></li>
<li><a
href="a8dc24f1fe"><code>[backport]
Turbopack: more strict vergen setup (#93587)</code></a></li>
<li><a
href="766148f9cd"><code>v16.2.5</code></a></li>
<li><a
href="0dd94836a8"><code>fix:
add explicit checks for RSC header (#83) (#98)</code></a></li>
<li><a
href="d166096c39"><code>fix
proxy matching for segment prefetch URLs (#89) (#96)</code></a></li>
<li><a
href="9d50c0b719"><code>Strip
next-resume header from incoming requests (#92)</code></a></li>
<li><a
href="df7ab5ad72"><code>fix:
skip internal param normalization in unsupported
environments</code></a></li>
<li><a
href="ed41d1d454"><code>Move
htmlescape to shared/lib (#91)</code></a></li>
<li><a
href="b4c6705c70"><code>Ignore
malformed CSP nonce headers</code></a></li>
<li><a
href="5b194ee2d4"><code>router-server:
guard upgrade proxy against absolute-url SSRF (#77)</code></a></li>
<li><a
href="cb171d7494"><code>Fix
i18n middleware matching for default-locale data routes
(#82)</code></a></li>
<li><a
href="89e995431a"><code>[16.x]
Type hardening and performance improvements (#80)</code></a></li>
<li><a
href="66f6017f15"><code>Escape
properties for beforeInteractive scripts (#86)</code></a></li>
<li><a
href="3d98505a24"><code>[backport]
fix: preserve HTTP access fallbacks during prerender recovery
(#93470)</code></a></li>
<li><a
href="bb5ada6e38"><code>[backport]
[test] Deflake `instant-navs-devtools` (#93534)</code></a></li>
<li><a
href="f1c11203d5"><code>[backport]
Fix double-encoding of URL pathname parts in client param parsing
(#93506)</code></a></li>
<li><a
href="2d08397b3d"><code>[backport]
fix accidental test duplication (#93507)</code></a></li>
<li><a
href="75d19ecbb3"><code>[backport]
Include deployment id in `cacheHandlers` keys (#93471)</code></a></li>
<li><a
href="7ab1e2e93d"><code>CI:
Download and run self-contained datadog-ci instead of using pnpm dlx or
npx (#92546)</code></a></li>
<li><a
href="084f2bcf19"><code>[ci]:
trigger signed release commit via API (#93285)</code></a></li>
<li><a
href="a3bb370b00"><code>[ci]:
app-based release workflow (#93245)</code></a></li>
<li><a
href="6e23383c56"><code>[ci]:
add environment to publishRelease flow (#93093)</code></a></li>
<li><a
href="f40b8876e6"><code>[ci]:
remove publish token in favor of OIDC (#93065)</code></a></li>
<li><a
href="f6bda26ef9"><code>Fix
fallback route params case in app-page handler (#93109)</code></a></li>
<li><a
href="70defda2a8"><code>[ci]:
switch to GitHub runners (#93164)</code></a></li>
<li><a
href="af0e96ba23"><code>Fix
invalid HTML response for route-level RSC requests in deployment adapter
(#91541)</code></a></li>
<li><a
href="2cdb7ed34f"><code>[tests]:
fix cache-components.test.ts type error (#93113)</code></a></li>
<li><a
href="8cd3fdc111"><code>test:
scope css data-url typing to fixture (#91877)</code></a></li>
<li><a
href="6fd09bf8ab"><code>Patch
setHeader for direct route handlers (#93101)</code></a></li>
<li><a
href="688ed31e21"><code>Strengthen
_rsc cache-busting param (#92755)</code></a></li>
<li><a
href="62ef305096"><code>fix(next/image):
ensure `images.maximumResponseBody` applies to local images too
(#92920)</code></a></li>
<li><a
href="15341fdf49"><code>Ensure
x-nextjs-data header is only set during resolve (#92752)</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-05-12 17:12:36 +02:00
Robin Malfait
54d6b05b44
move localRequire to else branch 2026-05-12 11:19:48 +02:00
Antoine Lethimonnier
fc432e0fd5
fix(node): prefer Module#registerHooks over Module#register (#20028)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

Fix this warning when building apps with TailwindCSS with Node 26+:
```
(node:25346) [DEP0205] DeprecationWarning: `module.register()` is deprecated. Use `module.registerHooks()` instead.
    at node:internal/util:129:11
    at Module.register (node:internal/modules/esm/loader:969:3)
    at file:///Users/antoine/Developer/my-app/node_modules/.pnpm/@tailwindcss+node@4.3.0/node_modules/@tailwindcss/node/dist/index.mjs:18:214
    at ...
```

This PR:
- Correctly prefers using `Module#registerHooks` instead of
`Module#register` by checking its availability at runtime
- Adjusts the exports of the hooks’ file by creating a synchronous
version for the new API
- Remove now unused exports from the `package.json`, relying on the
[recommended usage in the
docs](https://nodejs.org/docs/latest/api/module.html#registration-of-asynchronous-customization-hooks)
for the `Module#register` calls

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

I'd be happy to ensure this works correctly on my end before merging
this, but it's not as trivial to test locally as a logic fix. Can you
guide me through what I need to do to test this?

I extensively based my change on the docs, following those guides:
- [Fixing imports for
`Module#register`](https://nodejs.org/docs/latest/api/module.html#registration-of-asynchronous-customization-hooks)
- [Using the new `Module#registerHooks`
function](https://nodejs.org/docs/latest/api/module.html#registration-of-synchronous-customization-hooks)

(and other more detailed sections of the same docs page)

---

Fixes #19893
Closes #19907

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-05-11 14:25:28 +00:00
depfu[bot]
ae96721fc5
Update @tybys/wasm-util 0.10.1 → 0.10.2 (minor) (#20031)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​tybys/wasm-util (0.10.1 → 0.10.2) ·
[Repo](https://github.com/toyobayashi/wasm-util)





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-05-11 12:09:58 +02:00
Robin Malfait
f9216b2b8c
update changelog 2026-05-08 22:18:22 +02:00
Robin Malfait
588bd7371f
4.3.0 (#20023) 2026-05-08 22:03:59 +02:00
Robin Malfait
59936c6cbb
Add tab-* utilities (#20022)
This PR adds new `tab-*` utilities.

The `tab-*` utilities set the `tab-size` property. They support positive
integer bare values, and arbitrary values:

| Class | CSS |
| -- | -- |
| `tab-2` | `tab-size: 2;` |
| `tab-[12px]` | `tab-size: 12px;` |

This also adds `tab-size` to the property order near the other text
layout properties.

## Test plan

1. Added new `tab-*` utility tests
2. Updated the intellisense snapshot
3. Other existing tests still pass
2026-05-08 21:22:07 +02:00
Robin Malfait
90a2373620
add zoom-* utilities (#20020)
This PR adds new `zoom-*` utilities.

The `zoom-*` utilities accept bare values that are transformed into `%`
based values, and arbitrary values:

| Class | CSS |
| -- | -- |
| `zoom-50` | `zoom: 50%;` |
| `zoom-[1.1]` | `zoom: 1.1;` |
| `zoom-(--value)` | `zoom: var(--value);` |


This also adds the `zoom` property after the `transform` related
properties. Initially I added it right after `scale`, because logically
they are close together. But then `zoom-*` would sit between `scale` and
other tranfsorm related properties which is a bit weird. Instead, I
moved it after `transform`.

## Test plan

1. Added new zoom based tests
2. All other tests still pass
2026-05-08 21:15:59 +02:00
Robin Malfait
2e1ccf7f11
Add scrollbar-gutter-* utilities (#20018)
This PR adds new `scrollbar-gutter-*` utilities. The API looks like
this:

| Class | CSS |
| -- | -- |
| `scrollbar-gutter-auto` | `scrollbar-gutter: auto` |
| `scrollbar-gutter-stable` | `scrollbar-gutter: stable` |
| `scrollbar-gutter-both` | `scrollbar-gutter: stable both-edges` |
2026-05-08 21:13:06 +02:00
depfu[bot]
c00da894dd
Update listhen 1.9.1 → 1.10.0 (minor) (#20017)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ listhen (1.9.1 → 1.10.0) ·
[Repo](https://github.com/unjs/listhen) ·
[Changelog](https://github.com/unjs/listhen/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/unjs/listhen/releases/tag/v1.10.0">1.10.0</a></h4>

<blockquote><p dir="auto"><a
href="https://bounce.depfu.com/github.com/unjs/listhen/compare/v1.9.1...v1.10.0">compare
changes</a></p>
<h3 dir="auto">🚀 Enhancements</h3>
<ul dir="auto">
<li>Support <code class="notranslate">extraURLs</code> and detect <a
href="https://portless.sh/">portless</a> from env by default (<a
href="https://bounce.depfu.com/github.com/unjs/listhen/pull/228">#228</a>)</li>
</ul>
<h3 dir="auto">🩹 Fixes</h3>
<ul dir="auto">
<li>Filter IPv4 link-local addresses from network interfaces (<a
href="https://bounce.depfu.com/github.com/unjs/listhen/pull/226">#226</a>)</li>
<li>Do not use fallback port in production (<a
href="https://bounce.depfu.com/github.com/unjs/listhen/pull/223">#223</a>)</li>
</ul>
<h3 dir="auto">❤️ Contributors</h3>
<ul dir="auto">
<li>Kricsleo (<a
href="https://bounce.depfu.com/github.com/kricsleo">@kricsleo</a>)</li>
<li>Tim Krajcar (<a
href="https://bounce.depfu.com/github.com/tkrajcar">@tkrajcar</a>)</li>
<li>Max (<a
href="https://bounce.depfu.com/github.com/onmax">@onmax</a>)</li>
<li>Pooya Parsa (<a
href="https://bounce.depfu.com/github.com/pi0">@pi0</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="60ba9f2ae2...33c98f262d">See
the full diff on Github</a>. The new version differs by 5 commits:</p>
<ul>
<li><a
href="33c98f262d"><code>fix:
do not use fallback port in production (#223)</code></a></li>
<li><a
href="49ef95e7a7"><code>fix:
filter IPv4 link-local addresses from network interfaces
(#226)</code></a></li>
<li><a
href="de2d49ae00"><code>chore(deps):
update all non-major dependencies (#221)</code></a></li>
<li><a
href="f341eb9f76"><code>feat:
support `extraURLs` and detect portless by env by default
(#228)</code></a></li>
<li><a
href="402c52df87"><code>chore:
update deps</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>

[ci-all]

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
2026-05-07 12:43:28 +00:00
Robin Malfait
754e7512ca
Use non-existing example in tests (#20021)
This PR makes a small change to the tests. In some cases, where we test
`@utility` functionality we use `tab-*` utilities.

But there is a possibility that we will add this as an actual utility,
so instead let's use a much more generic `example-*` utility. Then we
can read from the `--example` theme value, and use `--resolved-value:
…;` and `--resolved-modifier: …;` values.

It's admittedly more vague, but chances of conflicts go way down. Until
CSS or Tailwind CSS introduces an `example-*` utility...


## Test plan

1. Only tests changed, and all of them still pass
2026-05-07 13:23:24 +02:00
Robin Malfait
12eb5ae7b6
Cleanup noisy test output (#20015)
This PR cleans up the noisy test output where some `console.warn`
messages were leaking into the test output.

This also uses the `src/` files instead of the `dist/` files of
`@tailwindcss/node` to get rid of a source map related warning in tests.
It also means that we don't have to rebuild `@tailwindcss/node` when we
make changes.

## Test plan

1. Existing tests pass
2. Output is clean when running tests (`vitest run --reporter=minimal`)

Before:
<img width="1193" height="1376" alt="image"
src="https://github.com/user-attachments/assets/9aceab62-cd99-4391-9734-0ae5c4c3fb48"
/>

After:
<img width="1099" height="294" alt="image"
src="https://github.com/user-attachments/assets/3ada4a0a-8a7a-48a7-9fbd-f3d5dd8700a5"
/>
2026-05-06 11:10:17 +02:00
Robin Malfait
4255671c5f
Improve snapshot tests (#20013)
This PR improves the snapshot tests. At first, this looks like a very
silly PR, but I swear I have legit reasons for these changes:

First, there are a few places where we use `.toMatchInlineSnapshot()`
for tests where we expect that nothing is being generated. This is fine,
but the issue is that this means that if you're not careful, and if we
have a bug, then these snapshots could start producing something.
Updating these snapshots is _too_ easy. So instead, we convert them to
an explicit `.toEqual('')`

Next, I introduced a `pretty` helper function which is used behind the
scenes in the `compileCss(…)`, `run(…)`, and `optimizeCss(…)` test
helpers. It's very silly and simple, it either returns `''` when the
trimmed input is empty, or it will wrap the result in `\n…\n`. The
reason for this is because of how (inline) snapshots works in Vitest. A
snapshot result will be in double quotes, and inside backticks:
```ts
expect(result.css.trim()).toMatchInlineSnapshot(`
  "@layer utilities {
    .foo {
      color: #000;
    }

    .bar {
      color: red;
    }
  }"
`)
```
This CSS now starts with `"` and ends with `"`. While that is fine, it
starts to get annoying when we have merge conflicts when CSS changes
(this is what triggered me to make this PR because I ran into this a
dozen times already). Because the first and last CSS line also contain a
`"` that you have to keep into account.

Instead we now use `\n` around the output, which makes the tests look
like this:
```ts
expect(pretty(result.css)).toMatchInlineSnapshot(`
  "
  @layer utilities {
    .foo {
      color: #000;
    }

    .bar {
      color: red;
    }
  }
  "
`)
```

In a perfect world, I wish we could use something like:
```css
expect(pretty(result.css)).toMatchInlineSnapshot(css`
  @layer utilities {
    .foo {
      color: #000;
    }

    .bar {
      color: red;
    }
  }
`)
```

That way you are only dealing with CSS, nothing else. Most editors will
show syntax highlighting, and even prettier will do formatting on the
CSS to keep everything consistent.

Unfortunately this also causes issues because when prettier formats
this, then the input/output will not always match. We can solve that by
parsing both sides and compare the ASTs but that would make things
slower.

The biggest issue is that Vitest doesn't support this. You can use
custom serializers
https://vitest.dev/guide/snapshot.html#custom-snapshot-matchers and
domains https://vitest.dev/guide/snapshot.html#custom-snapshot-domain
but this has an annoying issue around escaping values.

When you use `css` it's typically implemented as `const css =
String.raw`, which means that you can write actual CSS instead of JS:
```ts
let input = css`
  .\[color:red\] {
    color: red;
  }
`
```

But vitest would double scape the `\`, which would make the snapshot
test fail:
```ts
let input = css`
  .\\[color:red\\] {
    color: red;
  }
`
```

**Edit**: It is possible with a custom snapshot environment! But this
still introduces some levels of indirection. We are also not really
testing the same thing anymore. Prettier will be formatting the CSS, we
rely on our own CSS parser / printer, which is fine but subtle bugs
could maybe be invisible or maybe it unlocks some hidden bugs, who
knows. Funnily enough, running tests with this new matcher also goes
from `23.60s` to `22.88s` (just 1 run comparison).
<img width="1227" height="897" alt="image"
src="https://github.com/user-attachments/assets/698a0c67-a1fc-46e3-ba5a-60e5873b32df"
/>

Long story short, simple `\n` and `\n` boundaries it is!

## Test plan

- Everything still works as expected.
- There are visual changes in tests, but no actual source code was
updated either so there can not be an accidental diff
2026-05-05 22:24:25 +02:00
Robin Malfait
8c779899bb
Ensure math operators are surrounded by whitespace in arbitrary values (#20011)
This PR fixes an issue where some `calc(…)` expressions become invalid
after we canonicalize them to a different syntax.

Let's say you start with:
`left-[calc(-1*(var(--my-var1)+var(--my-var2)))]`, then the produced AST
for this candidate looks like this:
```js
[
  {
    "kind": "functional",
    "root": "left",
    "modifier": null,
    "value": {
      "kind": "arbitrary",
      "dataType": null,
      "value": "calc(-1 * (var(--my-var1) + var(--my-var2)))"
    },
    "variants": [],
    "important": false,
    "raw": "left-[calc(-1*(var(--my-var1)+var(--my-var2)))]"
  }
]
```
Notice that the `+` in between the vars already contain spaces.

And the generated CSS looks like this:
```css
.left-\[calc\(-1\*\(var\(--my-var1\)\+var\(--my-var2\)\)\)\] {
  left: calc(-1 * (var(--my-var1) + var(--my-var2)));
}
```
Again, the `+` has spaces aroudn it.

However, we canoncialize this syntax where we remove the `calc(-1 *
<value>)` and move the `-` in front:
`-left-[(var(--my-var1)+var(--my-var2))]`, which should behave the same,
but it did not. The parsed value for this looks like:
```js
[
  {
    "kind": "functional",
    "root": "-left",
    "modifier": null,
    "value": {
      "kind": "arbitrary",
      "dataType": null,
      "value": "(var(--my-var1)+var(--my-var2))"
    },
    "variants": [],
    "important": false,
    "raw": "-left-[(var(--my-var1)+var(--my-var2))]"
  }
]
```
Notice that the `+` does not contain spaces around it. That's because we
add them when we parse arbitrary values and when we are in a `calc(…)`
expression. But the `calc(<value> * -1)` is added later, so at this
point, no spaces are added yet.
This also means that the generated CSS currently looks like:
```css
.-left-\[\(var\(--my-var1\)\+var\(--my-var2\)\)\] {
  left: calc((var(--my-var1)+var(--my-var2)) * -1);
}
```
Which is invalid.

To solve this, we will make sure to add the whitespace around operators
when we re-insert the `calc(<value> * -1)`. With this fix, the CSS looks
like this:
```css
.-left-\[\(var\(--my-var1\)\+var\(--my-var2\)\)\] {
  left: calc((var(--my-var1) + var(--my-var2)) * -1);
}
```
Which is correct again.

---

There are a few other issues that need a bit more work, but are not
required for this fix.
1. Can we get rid of the additional `(` and `)` parens? E.g.:
   ```diff
   - left-[calc(-1*(var(--my-var1)+var(--my-var2)))]
   - -left-[(var(--my-var1)+var(--my-var2))]
   + -left-[var(--my-var1)+var(--my-var2)]
   ```
Right now this means that we should use `calc((<value>) * -1)` instead
of
`calc(<value> * -1)` since `<value>` can be an expression on its own.
This
could lead to unwanted behavior, but this will be a follow up PR _if_
it's
   worth it.
2. Why did we even allow this canoncialization from A to B if it's not
the same result?

This last question is a bit more scary, but it has to do with how we
compare results. We create a signature where we normalize a bunch of
values to make sure that we can compare them. As a silly example
`calc(var(--a) + var(--b))` and `calc(var(--b) + var(--a))` will result
in the same value, therefor should have the same signature and should be
swappable.
But what's happening is that the signature of
`left-[calc(-1*(var(--my-var1)+var(--my-var2)))]` and
`-left-[(var(--my-var1)+var(--my-var2))]` result in:
```
/* Signature of: left-[calc(-1*(var(--my-var1)+var(--my-var2)))] */
.x {
  left: calc((var(--my-var1)+var(--my-var2))*-1);
}

/* Signature of: -left-[(var(--my-var1)+var(--my-var2))] */
.x {
  left: calc((var(--my-var1)+var(--my-var2))*-1);
}
```

This gets rid of whitespace to store less data, but this is obviously
wrong now, so we need to improve the signatures around this. That said,
that will be a follow up PR as well because this requires much more
testing.
But this PR at least fixes the #20010 issue because we will properly
insert the whitespace around the math operators.

Fixes: #20010

## Test plan

1. Added a regression test to make sure that the new canonicalized
syntax results in the correct CSS. Before the fix, the test would fail:
<img width="623" height="106" alt="image"
src="https://github.com/user-attachments/assets/c470ce38-c3fa-4080-92ca-8a3e509f700b"
/>
2. Existing tests still pass
2026-05-05 17:08:24 +02:00
Adam Wathan
b4db3b99d1
Add scrollbar-width and scrollbar-color utilities (#19981)
## Summary

Adds utilities for the
[`scrollbar-width`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-width)
and
[`scrollbar-color`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-color)
CSS properties.

### `scrollbar-width`

Three static utilities mirroring the spec keywords:

| Class | CSS |
| --- | --- |
| `scrollbar-auto` | `scrollbar-width: auto;` |
| `scrollbar-thin` | `scrollbar-width: thin;` |
| `scrollbar-none` | `scrollbar-width: none;` |

### `scrollbar-color`

The `scrollbar-color` property takes two colors (thumb and track). To
allow them to be set independently, two color utilities are added that
share a pair of CSS variables (`--tw-scrollbar-thumb` and
`--tw-scrollbar-track`), following the same pattern as the gradient stop
utilities (`from-*` / `via-*` / `to-*`):

| Class | CSS |
| --- | --- |
| `scrollbar-thumb-<color>` | `--tw-scrollbar-thumb: <color>;
scrollbar-color: var(--tw-scrollbar-thumb) var(--tw-scrollbar-track);` |
| `scrollbar-track-<color>` | `--tw-scrollbar-track: <color>;
scrollbar-color: var(--tw-scrollbar-thumb) var(--tw-scrollbar-track);` |

Both go through `colorUtility`, so they automatically support the
standard color palette, theme keys (`--scrollbar-thumb-color` /
`--scrollbar-track-color` with `--color` as a fallback), arbitrary
values, and the `/<alpha>` opacity modifier. The variables are
registered with `@property` (initial value `#0000`), matching the
gradient-stop convention so an unset side falls back to transparent.

```html
<div class="scrollbar-thin scrollbar-thumb-red-500 scrollbar-track-zinc-200">…</div>
```

## Test plan

- [x] `pnpm test` (all 4480 tests pass)
- [x] New `scrollbar-width`, `scrollbar-thumb`, and `scrollbar-track`
test cases in `utilities.test.ts` covering palette colors, theme-key
colors, `current` / `inherit` / `transparent`, arbitrary colors,
`/<alpha>` modifiers, and invalid candidates
- [x] `intellisense.test.ts` snapshot updated to include the new class
names


---
_Generated by [Claude
Code](https://claude.ai/code/session_014ajg5maQ4gUHKmqBs5o7vD)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-04 17:16:50 +02:00
Robin Malfait
08cad84bbe
Support --default(…) in --value(…) and --modifier(…) to support fallback values (#19989)
This PR adds a new `--default(…)` option that can be used inside
`--value(…)` or `--modifier(…)` such that functional utilities without
an explicit value/modifier can still be defined as a functional utility.
It would also allow you to use a functional utility without a value and
_with_ a modifier, e.g.: `shadow/50`.

---

This allows us to re-implement functional utilities with a default value
in CSS using `@utility`.

Used the explicit `--default(…)` argument of `--value(…)` for a few
reasons.

1. It's explicit about being a falllback value. If you have `@utility
foo-*`, then you want to be able to use `foo`, but `foo-bad` should not
compile.
2. When `--value(…)` is used in (complex) property values (think a bunch
of `calc(…)` expressions), then we don't need a separate property for
this.

One of the ideas was to have a literal fallback:
```css
@utility tab-* {
  tab-size: 4;
  tab-size: --value(number);
}
```

For `tab`, this would compile to:
```css
.tab {
  tab-size: 4;
}
```

For `tab-123`, this would compile to:
```css
.tab {
  tab-size: 4;
  tab-size: 123;
}
```
Getting rid of the `tab-size: 4` would be an option, but it's a common
pattern in real CSS for fallback values (think hex background color,
over a more modern `oklch` color).

For `tab-foo`, this would compile to:
```css
.tab {
  tab-size: 4;
}
```

Which means that we have an infinite amount classes that would result in
the same class, which is bad. We could special case this one because the
internal `value` would still be `null`, but it might be too confusing.

This syntax without the `--default(…)` also means repetition of certain
properties. Add `--modifier(…)` to the mix, and there is even more
repetition going on.

Another option to consider is that the default fallback is just another
option in the `--value(…, 4)`, but if a default fallback is a keyword,
then there is a chance that this might conflict with actual keywords we
interpret.

Main motivation is to be able to re-implement utilities such as
`shadow/50` purely in CSS. It's also something we support in the JS
based APIs, but not in the CSS based one, so while it's a "new" feature,
it's more like a missing feature right now, and often a reason for
people to use the JS based APIs instead.

For consistency reasons, this is also implemented for `--modifier(…)`
such that you can use a default value there. E.g. when re-implementing
`text-sm` where a default `line-height` is set without the explicit use
of a modifier.

Fixes: https://github.com/tailwindlabs/tailwindcss/issues/16824

## Test plan

1. Added a handful of new tests to make sure this functionality works
2. Existing tests still pass
2026-05-04 17:03:00 +02:00
Robin Malfait
0f6f7d480f
Ensure --value(…) is required in functional @utility definitions (#20005)
While working on #19989, I noticed that `--value(…)` inside functional
`@utility` definitions is not required right now.

That means that the following CSS is valid:
```css
@utility foo-* {
  color: red;
}
```

But this doesn't really makes sense, because this now accepts a value
and `foo-a`, `foo-b` and `foo-c` would generate the following CSS:
```css
.foo-a {
  color: red;
}
.foo-b {
  color: red;
}
.foo-c {
  color: red;
}
```
The `a`, `b`, and `c` are not doing anything here apart from making your
CSS bigger. So this is very likely an actual bug that you forgot to use
`--value(…)`.

Additionally, if a `--value(…)` was used, but it didn't resolve
anything, then we already properly discared the candidate.

## Test plan

1. Add test to ensure `--value(…)` is required in functional `@utility`
definitions
2. Existing tests pass
2026-05-04 16:44:28 +02:00
Robin Malfait
6e2b60e84a
Do not generate CSS for start and end (#20003)
This PR fixes a bug where CSS was generated for `start` and `end`. This
was accidentally introduced when we moved the `start-*` and `end-*`
utilities to the legacy utilities. But this meant that we now generate
CSS for `start` and `end` even if no value is provided.

Fixes: #20002

## Test plan

1. Updated the tests to make sure of `--spacing: 0.25rem` which made the
tests fail, and are fixed again by applying the fix.
2. Other tests are still passing
2026-05-03 01:00:56 +02:00
depfu[bot]
4b5d6a5943
Update enhanced-resolve 5.20.1 → 5.21.0 (minor) (#19998)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ enhanced-resolve (5.20.1 → 5.21.0) ·
[Repo](https://github.com/webpack/enhanced-resolve) ·
[Changelog](https://github.com/webpack/enhanced-resolve/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/webpack/enhanced-resolve/releases/tag/v5.21.0">5.21.0</a></h4>

<blockquote><h3 dir="auto">Minor Changes</h3>
<ul dir="auto">
<li>
<p dir="auto">Added promise API and support to resolve without <code
class="notranslate">context</code> and <code
class="notranslate">resolveContext</code>. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/520">#520</a>)</p>
</li>
<li>
<p dir="auto">Add <code
class="notranslate">extensionAliasForExports</code> option. When <code
class="notranslate">true</code>, <code
class="notranslate">extensionAlias</code> also applies to paths resolved
through the <code class="notranslate">package.json</code> <code
class="notranslate">exports</code> field. Off by default to match
Node.js; opt in for full TypeScript-resolver parity with packages that
ship <code class="notranslate">.ts</code> sources alongside the compiled
<code class="notranslate">.js</code> they declare in <code
class="notranslate">exports</code>. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/554">#554</a>)</p>
</li>
</ul>
<h3 dir="auto">Patch Changes</h3>
<ul dir="auto">
<li>
<p dir="auto">Properly handle DOS device paths (<code
class="notranslate">\\?\…</code> and <code
class="notranslate">\\.\…</code>). (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/551">#551</a>)</p>
</li>
<li>
<p dir="auto">Prevent fallback to parent node_modules when the <code
class="notranslate">exports</code> field target file is not found. (by
<a href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/495">#495</a>)</p>
</li>
<li>
<p dir="auto">Imports field spec deviation: non-relative targets (e.g.
<code class="notranslate">"#a": "#b"</code>) no longer re-enter imports
resolution, aligning with the Node.js ESM spec where <code
class="notranslate">PACKAGE_IMPORTS_RESOLVE</code> does not recursively
resolve <code class="notranslate">#</code> specifiers. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/503">#503</a>)</p>
<p dir="auto">Previously <code class="notranslate">{ "#a": "#b", "#b":
"./the.js" }</code> would chain-resolve <code
class="notranslate">#a</code> to <code
class="notranslate">./the.js</code>; now it correctly fails, matching
Node.js behavior.</p>
</li>
<li>
<p dir="auto">Move <code class="notranslate">cachedJoin</code>/<code
class="notranslate">cachedDirname</code>/<code
class="notranslate">createCachedBasename</code> caches from module-level
globals to per-Resolver instances. This prevents unbounded memory growth
in long-running processes — when a Resolver is garbage collected, its
join/dirname/basename caches are released with it. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/507">#507</a>)</p>
</li>
<li>
<p dir="auto">Fixed when <code class="notranslate">tsconfig: true</code>
is used (default config file) and no <code
class="notranslate">tsconfig.json</code> exists. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/502">#502</a>)</p>
</li>
<li>
<p dir="auto">Apply the <code class="notranslate">extensionAlias</code>
option to the <code class="notranslate">imports</code> field to be align
with typescript resolution. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/549">#549</a>)</p>
</li>
<li>
<p dir="auto">Improved performance of the many plugins. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/529">#529</a>)</p>
</li>
<li>
<p dir="auto">Replace the <code
class="notranslate">Set&lt;string&gt;</code>-based resolver stack with a
singly-linked <code class="notranslate">StackEntry</code> class that
exposes a Set-compatible API. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/526">#526</a>)</p>
<p dir="auto">Each <code class="notranslate">doResolve</code> call now
prepends a single linked-list node instead of cloning the entire Set,
making stack push O(1) in time and memory. Recursion detection walks the
linked list (O(n)), but because the stack is typically shallow this is
much cheaper than cloning a Set per call.</p>
</li>
<li>
<p dir="auto">Cache the result of <code
class="notranslate">stripJsonComments</code> + <code
class="notranslate">JSON.parse</code> in <code
class="notranslate">readJson</code> using a <code
class="notranslate">WeakMap</code> keyed by the raw file buffer. This
avoids redundant comment-stripping and JSON parsing on every resolve
call that reads tsconfig.json files (via <code
class="notranslate">stripComments: true</code>), improving
TsconfigPathsPlugin warm performance by ~20-35% depending on the depth
of the <code class="notranslate">extends</code> chain. (by <a
href="https://bounce.depfu.com/github.com/xiaoxiaojx">@xiaoxiaojx</a> in
<a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/524">#524</a>)</p>
</li>
<li>
<p dir="auto">Avoid OOM in CachedInputFileSystem when duration is
Infinity. (by <a
href="https://bounce.depfu.com/github.com/alexander-akait">@alexander-akait</a>
in <a
href="https://bounce.depfu.com/github.com/webpack/enhanced-resolve/pull/527">#527</a>)</p>
</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="ebc67d3896...35035ca158">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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-05-01 11:29:19 +00:00
Robin Malfait
52f94c74bb
Improve codebase quality (#19999)
Small PR that improves the overall quality of the codebase. It's a bit
of everything:

1. Using correct variants for the variants we are testing
2. Use `@reference` instead of `@import` in a test, testing the
`@reference` according to the test name
3. Use `using` for Vitest related mocks. They have a `Symbol.dispose`
implemented, so we can don't have to restore mocks ourselves (right now,
some of them are not cleaned up at all).
4. Updated deprecated `.toThrowError` with `.toThrow` APIs

## Test plan

Everything still passes.

[ci-all]
2026-04-30 23:32:37 +02:00
Robin Malfait
0db856f3de
Make integration tests more stable in CI (#19995)
This PR is an attempt to make CI more stable related to integration
tests.

[ci-all]
2026-04-30 23:23:36 +02:00
Robin Malfait
1ca0aacdae
Add source map visualization for tests (#19997)
This PR is an internal change only related to how we visualize source
maps.

As part of this PR
(https://github.com/tailwindlabs/tailwindcss/pull/19996) I added a test
for source maps related to how `@variant` is processed. And while the
result was correct, I had a hard time verifying if this was _actually_
correct. I did the mental mapping of comparing locations from the output
to the input.
<img width="495" height="495" alt="image"
src="https://github.com/user-attachments/assets/2b1c12cd-43ee-461a-b93b-bafe6ce1cce5"
/>

With this PR, I want to make that more visual by actually printing the
input source(s) and output file and highlight the necessary parts:
<img width="1101" height="1085" alt="image"
src="https://github.com/user-attachments/assets/14390026-f211-4cfc-8c6c-3293105f6403"
/>

I didn't want to get too clever here. But printing line numbers also
helps in case we point to different spots on the same line:
<img width="1200" height="981" alt="image"
src="https://github.com/user-attachments/assets/e75f7622-eb51-49ee-928e-fdc8672d2c3f"
/>

And if we point to different files, then we visualize these as well:
<img width="851" height="957" alt="image"
src="https://github.com/user-attachments/assets/77e65801-b46b-4579-8659-d919a8750f60"
/>


If you combine this with snapshot tests, then it's very easy to verify
that locations match up correctly. Each source map location is
highlighted and references a symbol starting at `A`, `B`, etc.

A change to the source maps _can_ result in a big diff, and even this PR
introduces a big diff because of the preflight diffs. But at least you
can see how things line up.

## Test plan

1. All tests still pass
2. Added tests for the source map visualizer that is only used in tests
3. No actual source code was touched
2026-04-30 14:52:09 +02:00
Robin Malfait
e1201bc6e3
Simplify @variant usage, allow compound and stacked variants (#19996)
This PR improves and simplifies the `@variant` usage. 

When we originally added support for `@variant`, we wanted to keep
things simple, where we could only use a single variant at a time. The
original PR did have a more complex system with all these features
enabled, but we wanted to make sure that we only introduced the
additional complexity when the community felt like it was needed.

But of course we still wanted to make sure that you could do compound
and stacked variants, it just required some additional code.

For compound variants, where you want to use variant `a` and variant
`b`, you could duplicate the rules as siblings:
```css
.foo {
  @variant a {
    display: flex;
  }

  @variant b {
    display: flex;
  }
}
```

But with this PR, you can comma separate each variant to get the same
effect:
```css
.foo {
  @variant a, b {
    display: flex;
  }
}
```
You can think of this as-if we are expanding this syntax into the
aforementioned syntax. In other words, we would do the duplication for
you.

Additionally, you also want to be able to stack variants. For that you
had to nest your `@variant` rules:
```css
.foo {
  @variant a {
    @variant b {
      display: flex;
    }
  }
}
```

Not the end of the world, but it can get pretty nested if you want to
use multiple variants. Luckily we already have a syntax for this in
normal Tailwind CSS classes: `a🅱️flex`. Which is exactly what we can
use here as well:
```css
.foo {
  @variant a:b {
    display: flex;
  }
}
```
Again, conceptually you can think of this syntax being expanded into the
syntax from above.

Last but not least, we can also combine these:
```css
.foo {
  background: black;

  @variant a, b:c {
    background: red;

    @variant d, e:f {
      background: blue;
    }
  }
}
```
This conceptually translates into the much more verbose version today:
```css
.foo {
  background: black;

  @variant a {
    background: red;

    @variant d {
      background: blue;
    }

    @variant e {
      @variant f {
        background: blue;
      }
    }
  }

  @variant b {
    @variant c {
      background: red;

      @variant d {
        background: blue;
      }

      @variant e {
        @variant f {
          background: blue;
        }
      }
    }
  }
}
```

The biggest downside is that this could potentially easily balloon your
CSS file size if you're not careful. Because with this, it's pretty easy
to add one more variant that introduces a lot of duplicated CSS.

This feature is completely backwards compatible, you can still nest your
`@variant` calls yourself if you want, and combine them with these
features if you want.

This is also a continuation of #19526 and #19884, but for some reason I
don't have push rights, so I'm creating this new PR instead. I did keep
the original commits of those PRs so these contributors are still
properly marked as contributors.
<img width="808" height="135" alt="image"
src="https://github.com/user-attachments/assets/bee334ab-39d7-4d4d-a48f-afa2253cf17b"
/>
<img width="349" height="75" alt="image"
src="https://github.com/user-attachments/assets/fb68906c-db74-43f7-83e4-918ad3d4a036"
/>


Closes: #19526
Closes: #19884

## Test plan

1. Added a bunch of new tests to verify this new behavior
2. Added tests that compare the short (new) version, and the long (old)
version
3. Added a sourcemap related test to ensure that the src and dst
locations are correct
4. Existing tests still pass

---------

Co-authored-by: orteth01 <tortega128@gmail.com>
Co-authored-by: Ray Knight <array.knight+github@gmail.com>
2026-04-30 12:19:44 +02:00
silverwind
6cf1af26b3
Fix TS2742 error when inferring exported Config type (#19707)
Type aliases get resolved through to `UserConfig` during declaration
emit, which lives in a hashed internal DTS chunk with no stable import
path, causing TS2742. This is 100% backwards-compatible because type
aliases and interfaces are structurally comparable.

Fixes: https://github.com/tailwindlabs/tailwindcss/issues/15844
Fixes: https://github.com/tailwindlabs/tailwindcss/issues/19706
Fixes: https://github.com/tailwindlabs/tailwindcss/discussions/15458

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-29 18:48:08 +00:00
Matt Van Horn
f036f80195
Allow multiple @utility definitions with same name but different value types (#19777)
## Summary

Fixes #16948

When defining multiple CSS `@utility foo-*` with different value types
(e.g., one for colors, one for numbers), only the first handler was
tried. If it returned `null` (value didn't match), the compile loop
stopped, preventing subsequent handlers from being attempted.

```css
@utility foo-* {
  color: --value(--color-*);
}
@utility foo-* {
  font-size: --spacing(--value(number));
}
```

Previously, `foo-red-500` worked but `foo-123` did not (or vice versa
depending on definition order).

The fix distinguishes between CSS `@utility` handlers and JS plugin
`matchUtilities` handlers:
- **CSS `@utility`** (no typed options): `null` means "try the next
handler" - allows multiple definitions with different value types to
coexist
- **JS `matchUtilities`** (with explicit types): `null` means "the value
was invalid for this type, stop" - preserves existing behavior where
typed utilities prevent invalid values from falling through

## Test plan

- Added test: two `@utility foo-*` definitions with different value
types - verifies both `foo-red-500` (color) and `foo-123` (number)
produce correct CSS
- All 4621 existing tests pass (including the `matchUtilities`
type-safety tests)
- `pnpm build && pnpm test` passes

This contribution was developed with AI assistance (Claude Code).

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-29 14:56:17 +00:00
Abhijeet Abhi
d194d4c3e6
docs: fix various typos in comments and documentation (#19878)
Fixes a few minor typos across the codebase (e.g. 'overriden' ->
'overridden', 're-use' -> 'reuse').

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-29 13:28:39 +00:00
Daniel Polito
ba667400e8
[@tailwindcss/upgrade] Don’t migrate inline style properties (#19918)
Prevent the upgrade tool from rewriting CSS properties inside inline
`style` attributes.

This fixes cases like `style="flex-grow: 1"` being changed to
`style="grow: 1"` and adds regression tests.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-29 13:18:02 +00:00
Robin Malfait
73f3a6a743
Canonicalization: migrate [&:has(…)] to has-[…] variant (#19991)
This PR improves the canonicalization around arbitrary variants
containing `[&:has(…)]`, by converting them to `has-[…]`.

Essentially when you have a variant, with `&:has(…)`, we will convert
it:

```diff
- [&:has([role=checkbox])]:flex
+ has-[[role=checkbox]]:flex
```

This also means that if the arbitrary selector inside of the `&:has(…)`
can be converted to something that aligns with known variants, then we
will do that as well:

```diff
  // `data-*` can be hoisted outside of the arbitrary value
- [&:has([data-slot=description])]:flex
+ has-data-[slot=description]:flex

  // `aria-visible="true"` can be entirely replaced by `aria-visible`
- [&:has([aria-visible="true"])]:flex
+ has-aria-visible:flex
```

Noticed this while looking into:
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1562

## Test plan

1. Added additional tests for this use case
2. Existing tests still pass
2026-04-29 14:20:11 +02:00
Robin Malfait
b9449612ba
Canonicalization: do not canonicalize units in arbitrary values (#19988)
This PR improves the canonicalization when dealing with arbitrary
values.

As part of the canonicalization process we compute a signature for a
given utility. This way we can ensure that when we canonicalize a
candidate into a simpler candidate that it's still equivalent if the
signatures match.

One thing we do during signature computation is normalizing dimensions
(value + unit) into the same unit to make comparisons easier.

For example:
```css
.foo { margin-top: 20in; }
.bar { margin-top: 1920px; }
```

Will both get converted to `1920px` and therefore `foo` and `bar` will
have the same signature.

Up until this part, everything is fine. However, this normalization also
leaks when we try to canonicalize arbitrary values. One of the things we
do is try to move the `-` into the arbitrary value:
```diff
- -mt-[20in]
+ mt-[calc(20in_*_-1)]
```

This is obviously not cleaner, but we can perform some canonicalization
of the arbitrary value. As part of that we do constant folding _and_ the
normalization of base units. That means that we would see this:
```diff
- -mt-[20in]
- mt-[calc(20in_*_-1)]
+ mt-[-1920px]
```

But this might be very confusing because it might not make sense where
the `1920px` even came from... the only thing we should have done here
is constant fold that calc expression.

That's what this PR does, it only does the constant folding but without
the unit normalization:
```diff
- -mt-[20in]
- mt-[calc(20in_*_-1)]
+ mt-[-20in]
```

Which is exactly what we want!

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

## Test plan

1. Added a regression test based on the linked issue
2. All other tests still pass
2026-04-28 16:42:02 +02:00
Robin Malfait
3aac5dafe8
Improve whitespace handling during canonicalization (#19986)
This PR fixes a printing bug during canonicalization where it converts:
```
[&:has(~_*_*:checked)]:text-green-500
```

into:
```
[&:has(~**:checked)]:text-green-500
```

This is because the `_` was marked as insignificant and therefore
removed. This PR fixes that and maintains the whitespace (`_`)
characters when needed.

Additionally, in the comments of the linked issue somebody mentioned
that:
```
w-[calc(100%_-_--spacing(60))]
```

was turned into:
```
w-[calc(100%---spacing(60))]
```

...and while that's still correct and parseable, it's not the prettiest.

This PR will still get rid of the whitespace, but introduce wrapping
parens `(…)` instead, in case readability is not ideal.

In this case, we will turn it into:
```diff
- w-[calc(100%_-_--spacing(60))]
- w-[calc(100%---spacing(60))]
+ w-[calc(100%-(--spacing(60)))]
```

Of course there are some cases where we don't need to introduce `(…)`
unnecessarily:

- `shadow-[inset_0px_1px_--theme(--color-white/15%)]` would not be
turned into `shadow-[inset_0px_1px_(--theme(--color-white/15%))]`
because no readability is gained when it's part of a normal space
separated list
- `m-[--spacing(12.34)]` would not be turned into
`m-[(--spacing(12.34))]` because there is nothing else it can conflict
with
- `m-[calc(--spacing(12.34)*2)]` would not be turned into
`m-[calc((--spacing(12.34))*2)]` because it's the first argument and
doesn't conflict with the `*`
- `m-[min(100%,--spacing(12.34))]` would not be turned into
`m-[min(100%,(--spacing(12.34)))]` because a `,` doesn't cause
readability issues


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

## Test plan

1. Added new tests to ensure the bug is fixed
2. Added new tests to ensure readability is improved (and not degraged)
when whitespace was used to improve readability
2026-04-27 00:20:53 +02:00
Robin Malfait
107e451e37
Improve CI for NAPI-RS (#19983)
This PR updates the CI workflows related to NAPI-RS. So far, we've been
using a custom ghcr.io based image, but these run on Node v18.

With this PR, I created a new project using `napi new`, with GitHub
Actions workflow setup. Then essentially ported the changes from that
new project to this project (as minimal as possible).

The main goal was to be able to bump NAPI-RS related dependencies _and_
get an insiders release out. The images used in these CI jobs were
blockers to get an actual (insiders) release out.

## Test plan

1. CI still passes
2. Added a dry-run on the prepare release workflow to test that we got
through the entire workflow without issues
2026-04-26 19:37:28 +02:00
Robin Malfait
615835d1f5
Bump NAPI related dependencies (#19982)
This PR bumps all the NAPI related dependencies

Closes: #19977
Closes: #19976
Closes: #19973
Closes: #19972
Closes: #19971
Closes: #19970

## Test plan

1. All tests still pass

[ci-all] to verify on Windows and macOS as well
2026-04-26 17:49:06 +02:00
It's Me!
bfb5732b0b
Fall back to the plugin base when PostCSS has no from option (#19980)
## Summary

`@tailwindcss/postcss` derives `inputBasePath` from `result.opts.from`:

```ts
let inputFile = result.opts.from ?? ''
let inputBasePath = path.dirname(path.resolve(inputFile))
```

When PostCSS calls the plugin without `from` (some bundlers, including
Turbopack, do this for certain CSS inputs), `inputFile` is `''`,
`path.resolve('')` returns `process.cwd()`, and `path.dirname(...)`
therefore returns the **parent of CWD**. The downstream `compileAst({
base: inputBasePath })` call then asks the resolver to find
`tailwindcss` from one level above the project root, which fails with:

```
Can't resolve 'tailwindcss' in '<parent of CWD>'
```

The plugin already computes `base = opts.base ?? process.cwd()` near the
top. Reusing that as the fallback gives a sensible default (CWD) and
respects an explicit `opts.base` when set.

```diff
-let inputBasePath = path.dirname(path.resolve(inputFile))
+let inputBasePath = inputFile
+  ? path.dirname(path.resolve(inputFile))
+  : base
```

## Test plan

Added a test in `packages/@tailwindcss-postcss/src/index.test.ts` that
processes `@import 'tailwindcss'` via `processor.process(input)` with no
`from` option. Before the fix, this throws `Error: Can't resolve
'tailwindcss' in '<parent of CWD>'`; after the fix, the import resolves
and the processor returns non-empty CSS.

I wasn't able to run the suite locally — `pnpm build` requires `cargo`
for `@tailwindcss/oxide` and I don't have a Rust toolchain set up — so
the test has been written to match existing conventions in
`index.test.ts` (vitest, plain
`postcss([tailwindcss({...})]).process(...)`), and I'm relying on CI to
verify.

---------

Co-authored-by: rebasecase <rebasecase@localhost>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-26 13:45:39 +00:00
Robin Malfait
3a890c3572
Bump dependencies (#19957)
This PR bumps dependencies in all the packages, typically just bumping
to the latest patch release.


Closes: #19936
Closes: #19917
Closes: #19899
Closes: #19897
Closes: #19845
Closes: #19832
Closes: #19967
Closes: #19968 

## Test plan

1. All tests still pass
2. All integration tests still pass

[ci-all] to verify Linux, Windows and macOS
2026-04-24 21:21:12 +02:00
Tommy D. Rossi
db27049caa
fix(@tailwindcss/vite): include @variant in feature detection (#19966)
Fixes #19964

CSS files imported via JS that only use `@variant` (no `@apply`,
`theme()`, or utility classes) are silently skipped by the Vite plugin.
The `@variant` directive gets passed through raw to the browser, which
drops it as an unknown at-rule.

**Root cause** — `Features.Variants` is missing from the [feature
detection
bitmask](https://github.com/tailwindlabs/tailwindcss/blob/v4.2.4/packages/%40tailwindcss-vite/src/index.ts#L526):

```ts
// before
Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities

// after
Features.AtApply | Features.JsPluginCompat | Features.ThemeFunction | Features.Utilities | Features.Variants
```

**Reproduction** — https://github.com/remorses/tailwind-variant-bug

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-24 14:11:48 +00:00
Robin Malfait
5a799900d4
Always resolve relative files, relative to the current .css file (#19965)
This PR fixes an issue where resolving of certain CSS or JS files
results in the wrong paths. The issue happens if you have a setup where
a relative file path _also_ exists in the parent folder:

```css
/* src/foo.css */
.foo-in-root {}

/* src/theme/a.css */
@import "./foo.css"; /* This resolved to the file above, instead of the file below */

/* src/theme/foo.css */
.foo-in-theme {}
```

This happened because we resolved relative to a `base` folder, but Vite
expects an `importer` instead. The difference is subtle, but they expect
a file. On that file they use `let base = path.dirname(importer)` to get
a base path out themselves.

This in turn means that if you pass in a folder, you get this:
```js
path.dirname('/path/to/my-project') // /path/to
```

If we gave it a proper file, then we get the proper base path
```js
path.dirname('/path/to/my-project/index.css') // /path/to/my-project
```

I'm actually surprised that this didn't cause issues earlier... but it
did result in error since we recently started resolving files using
Vite's `aliasOnly: true` feature such taht Vite aliases work as well.

With this change, we now make sure that:

1. We use a proper `importer` instead of the `base` path
2. We refactor the resolving logic such that we try with `aliasOnly:
true` first, then `aliasOnly: false`

We also still ensure that in the CSS resolver we expect a `.css` file,
and in the JS resolver we _don't_ expect a `.css` file (which can happen
if a `"browser": "./dist/index.css"` field in package.json points to a
CSS file, daisyUI does this for example).

Fixes: #19956

## Test plan

1. Added additional (failing) integration tests to reproduce the linked
issue
2. Existing integration tests pass

While the build still worked, the linked issue resulted in a much bigger
file size because the wrong .css files were included. With this fix, the
number is correct again:
<img width="1234" height="1037" alt="image"
src="https://github.com/user-attachments/assets/fdff803c-0db2-4066-92fa-064c2816b35c"
/>

Since this is touching code related to previous PRs, I wanted to
manually make sure that these still work as expected:

- https://github.com/tailwindlabs/tailwindcss/issues/19950: This one is
about daisyUI and the `.css` file referenced in the package.json's
`"browser"` field: <img width="782" height="1229" alt="image"
src="https://github.com/user-attachments/assets/fb7b9d3b-527b-414b-94b0-2be7e96056f1"
/>

- https://github.com/tailwindlabs/tailwindcss/issues/19946: This one is
about the Vite alias being just a single `@` causing issues with
`@plugin "@tailwindcss/typography";` for example: <img width="1694"
height="1856" alt="image"
src="https://github.com/user-attachments/assets/d1935ecf-e80a-4945-a69a-ec3c5223efab"
/>


[ci-all]
2026-04-24 15:07:56 +02:00
Robin Malfait
f62597a82c
Bump dependencies in playgrounds (#19954)
This PR bumps dependencies in our playgrounds.

Closes: https://github.com/tailwindlabs/tailwindcss/pull/19952
Closes: https://github.com/tailwindlabs/tailwindcss/pull/19945

### Test plan

Ran each playground separately to make sure they are still working
2026-04-23 12:51:20 +02:00
ArcherGu
f3fdda2a5c
fix(vite): avoid resolving JS plugins to browser CSS entries (#19949)
Edit: some edits by @RobinMalfait 

---

## Summary

Fix a regression in `@tailwindcss/vite` introduced by `#19803` where JS
plugin resolution could incorrectly resolve a package to its `browser`
CSS entry.

In cases like `daisyui`, Vite can resolve `@plugin "daisyui"` to
`daisyui.css` instead of the package's JS entry, which causes Tailwind
to try to load a CSS file as a JS plugin and fail with:

```txt
Unknown file extension ".css"
```

This change keeps the `aliasOnly: false` behavior from `#19803` so
tsconfig path resolution still works, but adds a JS-entry guard to
`customJsResolver` in `@tailwindcss/vite`. If Vite resolves a plugin
request to a non-JS file like `.css`, the custom resolver now returns
`undefined` so Tailwind's internal fallback resolver can resolve the
package as a JS plugin entry instead.

I also added integration coverage for a package whose `main`/`module`
points to JS while `browser` points to CSS, and verified that `@plugin
"pkg"` still resolves to the JS entry in both build and dev mode.

## Test plan

Added new integration tests in `integrations/vite/resolvers.test.ts`
covering a package with:

- `main` / `module` -> JS
- `browser` -> CSS
- `@plugin "pkg"` -> should resolve to JS, not CSS

Verified with:

```sh
pnpm test:integrations vite/resolvers.test.ts -t "browser points to CSS"
pnpm test:integrations vite/resolvers.test.ts -t "resolves tsconfig paths"
```

These verify that:

- `@plugin` no longer resolves to a CSS browser entry
- the original tsconfig paths fix from `#19803` still works in both
build and dev mode

---

Maintainer edits:

Instead of hardcoding file extensions, first try to resolve aliases and
then fallback to the default resolving system we had before. We still
check for a `.css` extension, even in the JS resolver because some
dependencies (like `daisyUI`) put the CSS file there instead of in an
`exports.style`. If we detect that, we still fallback to the default
resolving logic.

This should be compatible with the original issue we were trying to fix
where we wanted to make Vite aliases work.

Fixes: #19950

[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-22 17:25:01 +02:00
Robin Malfait
69ad7cc5ec
4.2.4 (#19948) 2026-04-21 14:52:34 +02:00
Robin Malfait
685c19e266
Fix issue around resolving paths in @tailwindcss/vite (#19947)
This PR fixes an issue when using `@tailwindcss/vite` and you're trying
to resolve paths. In the latest 4.2.3 release, we added support for
following the vite `aliases` option.

However, some people run into issues because if you just use `@` as an
alias then using `@tailwindcss/typography` wouldn't resolve because it's
a package, and not something local.

With this PR we fix that by making sure that Vite's resolver can
actually resolve to an absolute path. If not, then we fallback to the
resolving that happens in `@tailwindcss/node`.

Fixes: #19946

## Test plan

1. Added an integration test that reproduces this issue, and is now
solved
2. Tested it on a reproduction provided in the corresponding issue

Before:
<img width="1694" height="1856" alt="image"
src="https://github.com/user-attachments/assets/c50f7104-78b3-476d-9e60-0c83e0976a5c"
/>


After:
<img width="1694" height="1856" alt="image"
src="https://github.com/user-attachments/assets/44ea0e3b-f60f-479f-a4a1-203a6230475b"
/>
2026-04-21 14:11:46 +02:00
Robin Malfait
2e3fa490a5
4.2.3 (#19944) 2026-04-20 22:32:52 +02:00
Pavan Shinde
4527123f68
docs(postcss): remove duplicated optimize example from README (#19938)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->
Removed a duplicated optimize: { minify: false } example from
@tailwindcss/postcss README (doc-only, one file).

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
Check the duplicate block is removed, the earlier optimize example still
exists, and only packages/@tailwindcss-postcss/README.md changed.
2026-04-20 17:22:33 +02:00
Robin Malfait
77801e7c62
use pnpm@9.6.0 2026-04-20 17:02:28 +02:00
Robin Malfait
039bd7d82f
Setup OIDC publishing (#19943)
This PR merges the `release-insiders.yml` and `release.yml` such that we
can setup OIDC publishing to npmjs.com.
2026-04-20 16:59:31 +02:00
Robin Malfait
998a6be85d
format 2026-04-20 13:12:24 +02:00
Steffen Deusch
5a835e1728
support NODE_PATH in standalone build (#19617)
References https://github.com/tailwindlabs/tailwindcss/pull/19391.
References https://github.com/tailwindlabs/tailwindcss/pull/16274.

Right now, when using the standalone build of the TailwindCSS CLI, you
cannot use a custom `NODE_PATH`, but you can when using it via Node.js
directly.

A custom NODE_PATH allows you to resolve imports from multiple
locations. For example, in [Phoenix
LiveView](https://github.com/phoenixframework/phoenix_live_view/), we
have a feature where you can write scripts in templates that we extract
at compile time to a custom folder and users can import those in their
application bundle by saying

```javascript
import { hooks as colocatedHooks } from "phoenix-colocated/my_app"
```

where the "phoenix-colocated" folder lives in a different location than
the usual `node_modules` folder. This works fine with the default
esbuild setup, as it respects `NODE_PATH`, so we can pass it a custom
location.

We want to also support colocating CSS in templates soon, but the same
approach doesn't work with the standalone Tailwind CLI we ship with
default Phoenix projects. It works when running Tailwind through
Node.js, but we don't want to tell users they need to install it, just
to use the feature.

This patch changes the lookup logic for the standalone CLI to also
account for `NODE_PATH`. Note that you can pass multiple paths, that are
split according the the OS PATH separator.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-04-17 18:29:18 +02:00
Pavan Shinde
aad601711f
docs/fix-lightning-css-typo-postcss-readme (#19913)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary
Fix a typo in the @tailwindcss/postcss README by changing Lighting CSS
to Lightning CSS in the optimize option documentation.
<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan
Verified the README now says Lightning CSS and that the diff is
docs-only.
<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2026-04-07 11:31:10 +02:00
depfu[bot]
e01946f8b7
Update @vitejs/plugin-react 5.1.3 → 5.2.0 (minor) (#19826)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (5.1.3 → 5.2.0) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)



<details>
<summary>Release Notes</summary>

<h4>5.1.4 (from changelog)</h4>
<blockquote><h3 dir="auto">Fix <code
class="notranslate">canSkipBabel</code> not accounting for <code
class="notranslate">babel.overrides</code> (<a
href="https://bounce.depfu.com/github.com/vitejs/vite-plugin-react/pull/1098">#1098</a>)</h3>
<p dir="auto">When configuring <code
class="notranslate">babel.overrides</code> without top-level plugins or
presets, Babel was incorrectly skipped. The <code
class="notranslate">canSkipBabel</code> function now checks for <code
class="notranslate">overrides.length</code> to ensure override
configurations are processed.</p></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please
let us know.</a></em></p>
</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-04-03 19:28:21 +00:00
Robin Malfait
2093a72e2d
Bump Next.js and drop ESLint (#19898)
This PR bumps Next.js and drops ESLint. 

We only had ESLint installed because it came with `next lint`. We don't
actively use ESLint, it was just used in the playgrounds where we test
things out. Dropping this reduces the amount of dependencies we have to
deal with.

Closes: #19867
Closes: #19852

# Test plan

Tested both the `nextjs` and `v3` playgrounds after bumping the Next.js
dependency and getting rid of ESLint, and they still work as expected.
2026-04-03 21:07:27 +02:00
Jordan Pittman
d7fc281a0e
Use json preprocessor for new-line delimited JSON files (#19862)
## Summary

This specializes the `.jsonl` and `.ndjson` file extensions so they're
preprocessed like JSON instead of by the standard scanner. This prevents
them from creating thousands of sub machines and reduces scanning time
(see #17125 where this was done for `.json` files).

It seems reasonable to handle new-line delimited JSON files as well
otherwise scanning these files can take quite a long time.

It's quite unlikely that these will contain classes so, alternatively,
these *could* go in the binary extensions list so they get ignored
entirely.

## Test plan

I ran manual tests inside the `oxide` crate against some large-ish JSONL
files (5MB–15MB). These changes bring down scanning time from 2s–3s on
my M3 Max (via `cargo test --release …`) to less than 20ms.

I also ran tests through a full CLI build pipeline on a low-spec linux
box. This change brought scanning time down from ~90s to ~300ms for a
single ~15MB file.
2026-03-26 23:42:03 +01:00
Robin Malfait
df6209ab8b
Canonicalize negative arbitrary values (#19858)
This PR adds a few more canonicalizations for some cases I noticed on
our templates.

When dealing with arbitrary values, and the utility is a "negative"
utility, then we will try to put the `-` inside of the arbitrary value:

```diff
- -left-[9rem]
+ left-[-9rem]
```

The idea is that the arbitrary value is already an escape hatch for when
a value is not available by default. The `-` in front uses an implicit
`calc(<expression> * -1)` which might be confusion if you have an value
like this already.

This also can allow for some further optimizations. For example
```diff
- -mt-[492px]
  ↓↓↓↓↓↓↓                           Into a simpler arbitrary value
+ mt-[-492px]
  ↓↓↓↓↓↓↓                           Into a bare value
+ mt-123
```

This PR also improve the constant folding of calc expressions a bit more
such that nested calc expressions with 2 constants and an unknown can be
folded. Bit of a mouthful, but it allows us to handle this:
```diff
- mt-[calc(-1*calc(-1*var(--foo)))]
  ↓↓↓↓↓↓↓                           The -1 * -1 becomes a no-op
+ mt-[var(--foo)]
  ↓↓↓↓↓↓↓                           Into the shorthand for CSS variables
+ mt-(--foo)
```

Now that we can handle moving the `-` into the arbitrary value, there
are also cases where we can get the `-` _out_ of the arbitrary value:
```diff
- mt-[calc(-1*var(--foo))]
  ↓↓↓↓↓↓↓                           Simplify calc, move `-` to the front
+ -mt-[var(--foo)]
  ↓↓↓↓↓↓↓                           Into the shorthand for CSS variables
+ -mt-(--foo)
```

Another missing piece that this PR adds is the concept of canonicalizing
or normalizing calc expressions. This is a separate step used when
calculating the signature for each utility. This allows us to normalize
`calc(-1*var(--foo))` and `calc(var(--foo)*-1)`. Without this they would
not be considered the same, but not it will.

It's only used when comparing values, it won't unify the actual
arbitrary values with this logic (at least for now).

With the additional constant folding logic and the canonicalization when
comparing signatures it unlocks the necessary power to perform the above
transformations.

## Test plan

1. Existing tests still pass
2. Added additional tests for the constant folding logic
3. Added tests for the canonicalization of calc expressions
4. Added new tests where we move the `-` inside the value, or move the
`-` outside of the arbitrary value.
2026-03-26 14:44:09 +01:00
Robin Malfait
52fd421cc9
Small refactor of canonicalization tests (#19851)
This are getting a little bit out of hand here, so this is an initial
refactor.

## Test plan

1. All tests are still there
2. All tests are still passing
2026-03-25 15:18:14 +01:00
Robin Malfait
c385fd36bc
use test.each instead of manual loop 2026-03-25 12:16:50 +01:00
Robin Malfait
0d6e038889
fix index in test name 2026-03-25 12:16:50 +01:00
Robin Malfait
88a2d22c2f
Add more canonicalization rules for deprecated utilities (#19849)
This PR adds more canonicalization rules for deprecated utilities.

| Before | After |
| --- | --- |
| `overflow-ellipsis` | `text-ellipsis` |
| `start-full` | `inset-s-full` |
| `-start-full` | `-inset-s-full` |
| `start-auto` | `inset-s-auto` |
| `start-px` | `inset-s-px` |
| `-start-px` | `-inset-s-px` |
| `start-8` | `inset-s-8` |
| `-start-8` | `-inset-s-8` |
| `start-123` | `inset-s-123` |
| `-start-123` | `-inset-s-123` |
| `end-full` | `inset-e-full` |
| `-end-full` | `-inset-e-full` |
| `end-auto` | `inset-e-auto` |
| `end-px` | `inset-e-px` |
| `-end-px` | `-inset-e-px` |
| `end-8` | `inset-e-8` |
| `-end-8` | `-inset-e-8` |
| `end-123` | `inset-e-123` |
| `-end-123` | `-inset-e-123` |

In a few cases we already had canonicalization rules, for example
`start-8` where `8` is one of the default suggested spacing scale
values. But this now adds support for positive and negative values that
exceed the default suggested spacing scale as well as some keywords.

## Test plan

1. Existing tests pass
2. Added new tests to ensure these canonicalizations work
2026-03-25 11:38:28 +01:00
Robin Malfait
e4856c9720
Make upgrade tooling more stable (#19846)
This PR is an attempt to make the upgrade tooling more stable.


### TL;DR

1. When migrating from Tailwind CSS v3 → Tailwind CSS v4, only migrate
files listed in the `config.content` instead of relying on v4's auto
content detection feature
2. Skip writing files that have not been changed
3. Write changed files in a safe way: first write to a temporary file,
then rename the file atomically
4. Never migrate files that are git ignored, even if they are listed in
the `config.content` file
5. Always ignore `.env` and `.env.*` files when scanning for files. Most
people will have this in their `.gitignore` file, but if not, then this
is a fallback mechanism.

---

Looking at the #18972 issue, it looks like some people are running into
weird situations where some of the contents is just gone.

I have never been able to reproduce this on my own devices and in my own
projects unfortunately. But there is definitely _something_ happening
that's not right that people are running into.

Therefore, this PR is an attempt to fix what I think _might_ be wrong,
but I'm not 100% sure if these fixes are enough, or if something else is
still happening here.

This builds on top of the #19779 PR which has some small fixes, but is
incomplete to make this work.

### What's happening

Looking at some of the comments, it looks like a few things are
happening such as:

1. The upgrade tool is emptying out my files — it looks like these are
only happening if you ctrl+c while the process is taking a while. It
could be that a lot of files are being checked and therefore the tooling
looks like its stuck.
6. The upgrade tool is looking at files it shouldn't look at — in
Tailwind CSS v4 we have this concept of the auto-content detection. This
means that we will look at any plain text file that is not git ignored.

### Fixes


#### Emptying out files

The files being emptied looks like it's because how `fs.writeFile`
behaves by default. It opens the file handle with the `w` flag, which
will first truncate the file before writing the new contents. This is
not a single atomic operation, so a killed process in the middle will
cause invalid state.

When we migrate your template files, everything is happening in promises
to migrate things at the same time. When a lot of files are being
scanned, truncating might have happened already before we write the new
content. Since we migrate a bunch of files in parallel, a ctrl+c could
cause data loss in multiple files.

To mitigate this, I switched to an alternative way of writing files. 

1. First, we do some quick checks where if the contents didn't change we
just bail out immediately. Files that don't include Tailwind CSS classes
won't change, and therefore we don't need to override these files with
the same contents.
2. When the migrated contents is empty, we bail out as well. I'm 100%
sure that this is not the spot where the "emptying out" happens, I still
believe it happens in the `writeFile` itself, but added it just in case.
7. Next, I introduced a safe write, where we first write to a temporary
file in the same folder. We could write it to `/tmp`, but then we can't
guarantee that we are on the same file system.
   
If we ctrl+c at this stage, then the worst case scenario is that you
have additional temporary files in your project, but your original files
are still there.
   
Once that file was written, we will use the atomic `fs.rename`. This
should be atomic as long as we are on the same file system, so either
the rename didn't happen yet, or it completed.

I added an integration test for this, but I had to change the
`writeFile` implementation slightly. In the test, we will truncate the
file first, after that we will write the new contents. This is so that
we have enough time to kill the current process and allows us to verify
that we didn't clear out the file. Again, this is a hacky way of testing
this, just because I can't reproduce this issue myself, let alone
reproduce it reliable in a CI environment.

Note: we are also using `realpath` to make sure that we are updating the
real file. Otherwise, if we were dealing with a symlinked file, we would
override the symlink with a "hard" copy instead.

#### Touching files that should not be touched

During the migration, we rely on the Tailwind CSS v4 auto detection
logic which means that it will scan any plain text file that is not git
ignored. Therefore changes to php files could happen because in theory
they could contain Tailwind CSS classes.

To solve this, when migrating from Tailwind CSS v3 to Tailwind CSS v4,
we will _only_ take the sources into account that were listed in the
`config.content` array. Since this was a requirement in Tailwind CSS v3,
it should be safe to rely on this array.

Additionally, this will make sure that we are dealing with way fewer
files to migrate as well.

On top of that, files that match the patterns in the content array that
are git ignored will also be skipped. This is to prevent that we mutate
files in `node_modules` for example.

In one of the comments I read that `.env` files were emptied out. In
most cases people will have these files gitignored but I explicitly
added `.env` and `.env.*` as files to never ever touch by default when
scanning.

Last but not least, this also updates the output a little bit of the
upgrade tool in case we skip content files (because of git ignore) and
if we changed a file.

<img width="1122" height="1376" alt="image"
src="https://github.com/user-attachments/assets/318fdbbf-e319-4c7e-9648-ee9283842624"
/>

- "Git ignored folder, skipping: `./node_modules`": this is because the
content array looks like this while the `node_modules` are being
ignored:
<img width="1090" height="398" alt="image"
src="https://github.com/user-attachments/assets/7d694720-5671-47ec-bb2e-f24c5f2c4248"
/>

- "Migrated
`./resources/views/vendor/filament-panels/components/logo.blade.php`":
this is because **I** made a change to showcase this feature.

Fixes: #18972
Closes: #19779

### Test plan

1. Existing tests still pass
1. Added a dedicated integration test to ensure that we only take
`config.content` into account when migrating from Tailwind CSS v3 to
Tailwind CSS v4 projects.
1. Added a dedicated integration test to make sure that files listed in
`config.content` that are also git ignored, will still be skipped.
1. Added a dedicated integration test to ensure that when `writeFile` is
cancelled mid-write that our old files are still present.
1. Added a dedicated integration test to ensure that we ignore `.env`
and `.env.*` files even if you didn't git ignore them.

[ci-all] To verify on Windows

---------

Co-authored-by: Sami <sychocouldy@gmail.com>
2026-03-24 17:24:12 +01:00
Robin Malfait
2c1ef9eb25
Use --placeholder-color instead of --background-color for placeholder-* utilities (#19843)
This PR fixes an issue where `placeholder-*` utilities were reading
values from `--background-color` instead of `--placeholder-color`. In
Tailwind CSS v3, we read from `placeholderColor` which is why we should
use `--placeholder-color` here as well.


f38be227df/src/corePlugins.js (L2317)

That said, this is technically a breaking change in case somebody relies
on `--background-color` for `placeholder` values. But since this is text
related, and most people will rely on the default `--color` values
instead, I think it's safe to change this as-is.

In the unlikely event that somebody _does_ rely on this, then we have 2
options:

1. Guide them to make use of `--placeholder-color` instead (preferred
solution)
2. Re-add `--background-color` after the `--placeholder-color` (band-aid
solution, but might be worth it who knows)

Fixes: #19838

## Test plan

1. Existing tests still pass
2. Verified in the Tailwind CSS v3 codebase that we did read from
`placeholderColor` which in turn reads from `color` by default. Which is
equivalent to `--placeholder-color` and `--color` in Tailwind CSS v4.
2026-03-23 15:57:20 +01:00
Robin Malfait
6ec8b89191
update CHANGELOG 2026-03-23 15:35:48 +01:00
Robin Malfait
28d526859d
Collapse more utilities by expanding their declarations (#19842)
This PR adds more declaration expansions such that we can collapse more
utilities.

While testing #19837 I noticed that in my tests some utilities weren't
canonicalized correctly. As part of that PR, we check for
`parsedCandidate.value === null`, which means that a functional utility
without a value is skipped. We do have utilities like that such as
`border` (which is equivalent to `border-1`). But while testing, I
noticed that `border-x border-y` should collapse to `border` but they
didn't. This PR fixes that.

By expanding these properties to their long-form physical properties
(instead of the shorter logical properties) we make the signatures of
utilities a bit bigger, but also more correct such that we can collapse
the physical form into logical utilities.

To make this more concrete, this PR allows for the following
canonicalizations now:

| Input | Output |
| --- | --- |
| `border-t-123 border-r-123 border-b-123 border-l-123` | `border-123` |
| `border-t-1 border-r-1 border-b-1 border-l-1` | `border` |
| `border-t-123 border-b-123` | `border-y-123` |
| `border-l-123 border-r-123` | `border-x-123` |
| `border-t-red-500 border-r-red-500 border-b-red-500 border-l-red-500`
| `border-red-500` |
| `border-t-red-500 border-b-red-500` | `border-y-red-500` |
| `border-l-red-500 border-r-red-500` | `border-x-red-500` |
| `scroll-mt-123 scroll-mr-123 scroll-mb-123 scroll-ml-123` |
`scroll-m-123` |
| `scroll-mt-123 scroll-mb-123` | `scroll-my-123` |
| `scroll-ml-123 scroll-mr-123` | `scroll-mx-123` |
| `scroll-pt-123 scroll-pr-123 scroll-pb-123 scroll-pl-123` |
`scroll-p-123` |
| `scroll-pt-123 scroll-pb-123` | `scroll-py-123` |
| `scroll-pl-123 scroll-pr-123` | `scroll-px-123` |
| `overflow-x-hidden overflow-y-hidden` | `overflow-hidden` |
| `overscroll-x-contain overscroll-y-contain` | `overscroll-contain` |

## Test plan

1. Existing tests pass
2. Added a few more tests to verify that these canonicalizations work
2026-03-23 15:33:44 +01:00
Aaron Tinio
b55d96002c
fix(canonicalize): collapse arbitrary values into shorthand utilities (#19837)
The guard on `dynamicUtilities` restricted root-swapping to named
values, so arbitrary values like `px-[1.2rem] py-[1.2rem]` were never
collapsed into `p-[1.2rem]`.

This is what caused #19835 — in `--stream` mode, the collapse only
happened if an earlier line caused the shorthand to be registered in
`STATIC_UTILITIES_KEY` as a side effect, making the output
non-deterministic. The underlying issue is that arbitrary value collapse
wasn't supported at all.

The fix relaxes the guard from `parsedCandidate.value?.kind !== 'named'`
to `parsedCandidate.value === null`. `cloneCandidate` and
`printCandidate` already handle arbitrary values, so the root-swapping
machinery works without other changes.

The iteration in `dynamicUtilities` is over
`designSystem.utilities.keys('functional')` — a fixed set of roots, not
input-proportional — so the performance cost of including arbitrary
values should be negligible.

Fixes #19835.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-23 11:32:25 +01:00
Qingyu Wang
17d324f896
Fix webpack loader cache key for resource queries (#19723)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

`@tailwindcss/webpack` currently uses `this.resourcePath` as the cache
key, which ignores the resource query. When the same CSS file is
imported multiple times with different `resourceQuery` values, all of
those imports share a single `CacheEntry`. That means the utilities
discovered for one entry can leak into the CSS output for another entry.

This PR changes the cache key to use `this.resource` (path + query)
instead, while still using `this.resourcePath` for all filesystem work.

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

- `pnpm test:integrations -- webpack/loader.test.ts`  
  - Confirms all existing webpack loader integration tests pass.  
- Confirms the new `@tailwindcss/webpack loader isolates cache by
resource including query` test passes, verifying that two entries
importing the same CSS file with different queries produce isolated
outputs (`dist/a.css` only contains `only-a` / `--color-red-500`, and
`dist/b.css` only contains `only-b` / `--color-blue-500`).

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-20 16:34:11 +00:00
Amir Zarrinkafsh
5cb1efdf41
fix(vite): resolve tsconfig paths in CSS and JS resolvers (#19803)
Change `aliasOnly` from `true` to `false` when calling Vite's resolver
so that the full resolution pipeline runs, including the oxc resolver
responsible for tsconfig path resolution.

When `aliasOnly` was `true`, only the @rollup/plugin-alias plugin ran,
which meant `resolve.tsconfigPaths: true` had no effect on CSS `@import`
or JS `@plugin` resolution in `@tailwindcss/vite`.

Closes #19802.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-20 15:19:18 +00:00
Robin Malfait
bd30a716e6
Fix crash due to invalid characters in candidate (#19829)
This PR fixes an issue where the compiler can crash if it encounters an
invalid codepoint.

When we extract potential candidates from files, it could be that we
encounter values that look like a class or a CSS variable, if it turns
out that it's an invalid CSS variable we can ignore it.

The problem is that sometimes there are escaped values in there that
result in invalid code points crashing the compiler.

This PR fixes that by gracefully handling that and making sure that
invalid code points are replaced by `\uFFFD` as per the spec.

The bug report
(https://github.com/tailwindlabs/tailwindcss/issues/19786) has a clean
example where a piece of text looks like a CSS variable, but contains
invalid code points.

```
--Coding-Projects-CharacterMapper-Master-Workspace\d8819554-4725-4235-9d22-2d0ed572e924
```

Luckily we can fix this today by ignoring the file paths that contain
these strings using `@source not "…";`, but the better way is to
actually fix this.

To solve this, instead of blindly passing numbers to
`String.fromCodePoint`, we will first validate whether it's a valid
codepoint:

1. `0x0000` — `0x10FFFF` (inclusive) is the range of valid code points.
See: https://infra.spec.whatwg.org/#code-point
2. `0xD800` — `0xDBFF` (inclusive) are leading surrogates. See:
https://infra.spec.whatwg.org/#leading-surrogate
3. `0xDC00` — `0xDFFF` (inclusive) are trailing surrogates. See:
https://infra.spec.whatwg.org/#trailing-surrogate

In the code we use the `0xD800` — `0xDFFF` range because the ranges
overlap.

There are various references in the spec to replace surrogates (and
invalid codepoints) with `\uFFFD`. Here is one of them:
https://drafts.csswg.org/css-syntax-3/#consume-escaped-code-point

Fixes: https://github.com/tailwindlabs/tailwindcss/issues/19786
Fixes: #19801 (this issue talks about a similar invalid code point
issue)

## Test plan

1. Added a regression test where the above string was used as a CSS
variable
2. Added a regression test for the unescape functionality to make sure
that invalid code points and surrogates are replaced by the `\uFFFD`
replacement character.

[ci-all] Just to verify on Windows as well
2026-03-20 14:51:37 +01:00
Robin Malfait
7482d47a54
Add canonicalizations for tracking-* utilities (#19827)
This PR adds support for canonicalizations for `tracking-*` utilities.

This one is a bit of a funny one, if you take a look at the linked
issue, there is a beautiful table:

| Utility Name | Value | Arbitrary Value | Throws Suggestion |
| - | -: | - | - |
| tracking-tighter | -0.05em | tracking-[-0.05em] | ✗ |
| tracking-tight | -0.025em | tracking-[-0.025em] | ✗ |
| tracking-normal | 0em | tracking-[0em] | ✗ |
| tracking-wide | 0.025em | tracking-[0.025em] | ✗ |
| tracking-wider | 0.05em | tracking-[0.05em] | ✗ |
| tracking-widest | 0.1em | tracking-[0.1em] | ✓ |

It doesn't really make sense to _why_ only the `tracking-widest` one is
properly suggested here. Until you look a little bit closer.

Turns out that `-tracking-tighter` is equivalent to `tracking-wider`,
`-tracking-tight` is equivalent to `tracking-wide` and so on.

The way the canonicalization works internally is by generating a
signature for a given utility class. If two utilities have the exact
same signature, we can consider them the same. In this case
`tracking-widest` and `tracking-[0.1em]` have the same signature.

One of the rules we have internally is that if we find more than one
replacement utility then we don't really know what to do, so we bail.
Because if you get `foo` or `bar`, which one do you pick?

If we refer to this above table again, the moment we want to
canonicalize the `tracking-[-0.05em]` we get two suggestions:
`tracking-tighter` and `-tracking-wider`, since we don't know what to
do, we bail and we don't suggest anything.

So the reason that `tracking-widest` _was_ suggested is just because we
don't have a `-tracking-tightest`.

How do we fix this? Well, since we have `tracking-*` and `-tracking-*`
utilities, I wanted to deprecate the `-tracking-*` ones for named
utilities (where the values come from your theme) because that doesn't
really make sense.

However, we have this exact pattern documented here:
https://tailwindcss.com/docs/letter-spacing#using-negative-values Which
means that I can't just deprecate those utilities.

<img width="723" height="511" alt="image"
src="https://github.com/user-attachments/assets/164b659b-abe9-4f6e-a176-701dd7ea505a"
/>

Instead, I added a different rule which says that if you get multiple
possible replacements, then we prefer the "positive" one, the one
without the `-`. Also added some additional checks to make sure that if
you get `foo`, `-bar`, `baz`, that we also bail because we know that we
should prefer `foo` or `baz` over `-bar`, but we don't know if we should
pick `foo` or `baz`...

This additional rule does solve the original issue, and we already
prefer possible values over negative values in other places (related to
bare values).

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

## Test plan

1. Existing tests pass
2. Added regression tests to make sure that the table from above _does_
get canonicalized correctly into the expected values.
2026-03-19 19:53:16 +01:00
Robin Malfait
d596b0c43d
4.2.2 (#19821) 2026-03-18 11:44:08 -04:00
Robin Malfait
2228a57a9e
Bump Lightning CSS (#19771)
This PR bumps Lightning CSS. The `node/index.js` file in the package
changed, so we had to update the patched version of it as well.

We also had to update the patches for `@parcel/watcher` and
`lightningcss` now that they ship with `detect-libc@2`. Unfortunately,
we still require the patches because we also need to take the
`process.env.PLATFORM_LIBC` into account. Another issue is that bun
needs to be able to statically analyze the `require(…)` calls, so using
the nice `parts.push` approach doesn't really work here.

# Test plan

1. Existing tests should pass
2. Updated some tests to reflect the changes in the generated CSS based
on the version bump.

[ci-all]
2026-03-18 15:57:52 +01:00
Robin Malfait
f302fce815
Fix canonicalization resulting in empty list (#19812)
This PR fixes a bug in the canonicalization process where if a few
utilities collapse into a smaller one, and the smaller one is part of
the original list, then it results in an empty list.

It will be more clear with an example. Let's say you have this setup:
```
w-[calc(1rem+0.25rem)] h-[calc(1rem+0.25rem)] size-5
```

The first step is that this will result in:
```
w-5 h-5 size-5
```

Then the `w-5 h-5` can turn into `size-5`. But the existing `size-5`,
can also be replaced by the `size-5`.

Internally, when we have a replacement, then we mark all the classes
that can be replaced as "droppable", so they would be dropped from the
list. But in this scenario we also marked `size-5` as droppable,
resulting in an empty list.

If an additional class existed:
```
w-[calc(1rem+0.25rem)] h-[calc(1rem+0.25rem)] size-5 flex
```

The result would be 
```
flex
```

Instead of the expected:
```
size-5 flex
```


## Test plan

1. Existing tests pass
2. Added new tests with and without an additional class
2026-03-17 13:00:31 +01:00
Robin Malfait
bb2f170514
Improve canonicalization for bare values exceeding default spacing scale suggestions (#19809)
This PR adds support for canonicalization of utilities that accept bare
values and exceed the default spacing scale we use for intellisense.

Right now, all utilities are behind functions, so the only way to know
whether something compiles is by compiling a candidate, e.g. `w-8` and
passing it to the utility functions. To help us, we use the intellisense
APIs that we use for suggestions.

Most utilities that accept bare values, have suggestions up until
`*-96`, so `w-96 h-96` would be canonicalized to `size-96`. But the
moment we exceed that, the result stays as-is.
```
→ w-96 h-96
= size-96

→ w-1234 h-1234
= h-1234 w-1234
```

This PR ensures that the last scenario also gets canonicalized to
`size-1234` instead of staying as `h-1234 w-1234`.

```
→ w-96 h-96
= size-96

→ w-1234 h-1234
= size-1234
```

## Test plan

1. Existing tests pass
2. Added new tests for utilities with bare values

[ci-all] just to see if this additional logic doesn't cause timeouts in
CI for WIndows. In my testing this doesn't have a significant impact on
performance at all.
2026-03-16 22:44:29 +01:00
Aaron Tinio
aaaefe8b5d
Add --stream flag to canonicalize subcommand (#19796)
## Summary

- Adds `--stream` flag to `tailwindcss canonicalize` that reads
candidate groups from stdin line by line and writes canonicalized
results to stdout
- Keeps the design system loaded across requests, making it suitable as
a long-running sidecar process
- Empty lines pass through, keeping request/response pairs aligned

## Motivation

Non-JS tools (formatters, editor plugins, etc.) currently have no
lightweight way to canonicalize Tailwind classes. The existing batch
mode works for one-off use, but tools that need to canonicalize
repeatedly pay the cost of loading the design system each time.

With `--stream`, a tool can start `tailwindcss canonicalize --stream`
once and send candidate groups over stdin as needed:

```sh
$ echo -e "py-3 p-1 px-3\nmt-2 mr-2 mb-2 ml-2" | tailwindcss canonicalize --stream
p-3
m-2
```

Related discussion:
https://github.com/tailwindlabs/tailwindcss/discussions/19736

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-16 15:00:11 +00:00
SidWuChou
d24b1127bc
fix: remove unused braces dependencies (#19797)
Removes unused braces and @types/braces from @tailwindcss/upgrade
package.json as reported in issue #19794

---------

Co-authored-by: CG1AI <sidwu@CG1AIdeMac-mini.local>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-15 21:10:54 +00:00
Robin Malfait
faa5e8849b
Cleanup inconsistencies related to (regex) escapes (#19804)
This PR does some generic cleanup to the codebase.

I'm playing with oxfmt and oxlint and noticed some unnecessary escapes.
Might add these dependencies to the project later (and rolldown for
building). But baby steps for now.

## Test plan

1. All tests should still pass
2026-03-15 22:04:58 +01:00
Robin Malfait
a4be983865
increase timeout of canonicalization tests 2026-03-12 21:08:06 +01:00
Robin Malfait
59b0329f85
Add support for Vite 8 in @tailwindcss/vite (#19790)
This PR adds support for Vite 8 when using the `@tailwindcss/vite`
package.

From the package's perspective, not a lot had to change, just the `vite`
peer dependency now has an additional `^8.0.0` version range.

Closes: #19789

## Test plan

1. Existing tests pass
2. Manually tested in the `./playgrounds/vite` playground
3. Added Vite 8 integration tests to verify that the plugin works with
Vite 8
2026-03-12 20:54:34 +01:00
Robin Malfait
dfc449c9ff
slightly re-word CHANGELOG 2026-03-12 19:50:16 +01:00
Hiroshi Ogawa
bf441a799f
fix(vite): skip full reload for server only modules scanned by client css (#19745)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

- Closes https://github.com/tailwindlabs/tailwindcss/issues/19744
- Closes https://github.com/vitejs/vite-plugin-react/issues/1118
- Closes https://github.com/wakujs/waku/issues/1963

The change in https://github.com/tailwindlabs/tailwindcss/pull/19670
didn't take account for server only modules managed by SSR framework.
Forcing full reload for this path breaks server HMR. This PR added a
check to determine whether the same modified file has associated modules
in a different environment module graph to avoid this.

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

Added an integration test for React router HDR (server loader hmr). This
test fails on main.

Also the local build is tested on `@vitejs/plugin-rsc` CI and confirmed
the fix https://github.com/vitejs/vite-plugin-react/pull/1132

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-12 18:49:41 +00:00
Robin Malfait
6b54dd8630
Fix internal source map warnings (#19788)
This PR fixes an internal source map warning when running tests. To
solve this, we encode the `=` as `\x3d` instead. I'm not 100% sure if
Vitest was hanging on this but it solved the following warning:

```
 ✓  @tailwindcss/cli  src/utils/format-ns.test.ts (21 tests) 3ms
11:43:59 AM [vite] (ssr) Failed to load source map for /Users/robin/github.com/tailwindlabs/tailwindcss/packages/@tailwindcss-node/dist/index.mjs.
Error: An error occurred while trying to read the map file at ${i}
Error: ENOENT: no such file or directory, open '/Users/robin/github.com/tailwindlabs/tailwindcss/packages/@tailwindcss-node/dist/${i}'
    at open (node:internal/fs/promises:634:25)
    at Object.readFile (node:internal/fs/promises:1238:14)
    at extractSourcemapFromFile (file:///Users/robin/github.com/tailwindlabs/tailwindcss/node_modules/.pnpm/vite@7.0.0_@types+node@20.19.1_jiti@2.6.1_lightningcss@1.31.1_patch_hash=tzyxy3asfxcqc7ihroou_7epcep7uhfc7zidzdmxzgvdzwi/node_modules/vite/dist/node/chunks/dep-Bsx9IwL8.js:8349:65)
    at loadAndTransform (file:///Users/robin/github.com/tailwindlabs/tailwindcss/node_modules/.pnpm/vite@7.0.0_@types+node@20.19.1_jiti@2.6.1_lightningcss@1.31.1_patch_hash=tzyxy3asfxcqc7ihroou_7epcep7uhfc7zidzdmxzgvdzwi/node_modules/vite/dist/node/chunks/dep-Bsx9IwL8.js:26405:22)
```

## Test plan

1. Existing tests pass
2. Added a failing test to ensure that the source maps are emitted
correctly
2026-03-12 12:21:00 +01:00
Robin Malfait
ad9fdef005
drop unnecessary test 2026-03-11 20:35:35 +01:00
Robin Malfait
e96909accd
Add tailwindcss canonicalize sub-command (#19783)
This PR adds a new `tailwindcss canonicalize` sub-command.
2026-03-11 20:30:21 +01:00
Robin Malfait
d5717f2307
run prettier 2026-03-11 19:59:38 +01:00
Kirk Ouimet
51aa9d799c
fix(canonicalize): handle utilities with empty property maps in collapse (#19727)
## Problem

`canonicalizeCandidates` crashes when called with `collapse: true` and
the candidate list includes utilities whose CSS output contains no
standard declaration properties (only `@property` rules and CSS custom
properties).

This is reproducible with vanilla Tailwind CSS and no custom
configuration:

```js
designSystem.canonicalizeCandidates(['shadow-sm', 'border'], { collapse: true })
// TypeError: X is not iterable
```

```js
designSystem.canonicalizeCandidates(['shadow-sm', 'border'], { collapse: true })
// TypeError: Cannot read properties of null (reading 'has')
```

All shadow utilities (`shadow-sm`, `shadow-md`, `shadow-lg`,
`shadow-xl`) crash when combined with any other utility and `collapse:
true`.

This was discovered via `eslint-plugin-better-tailwindcss`, which calls
`canonicalizeCandidates` with `collapse: true` for its
`enforce-canonical-classes` rule. The crash brings down ESLint entirely.

## Root cause

In `collapseGroup`, the `otherUtilities` array is built by mapping over
each candidate's property values:

```ts
let otherUtilities = candidatePropertiesValues.map((propertyValues) => {
  let result: Set<string> | null = null
  for (let property of propertyValues.keys()) {
    // ... builds result ...
  }
  return result! // returns null if propertyValues has no keys
})
```

When a utility like `shadow-sm` generates CSS with `@property` rules and
custom property declarations but no standard CSS properties,
`propertyValues.keys()` is empty, the loop never executes, and `result`
stays `null`. The non-null assertion `result!` returns `null` into the
array.

Downstream code then crashes when iterating or calling `.has()` on the
null entry:

```ts
for (let i = 0; i < otherUtilities.length; i++) {
  let current = otherUtilities[i]     // null
  for (let property of current) {     // "X is not iterable"
    if (other.has(property)) {        // "Cannot read properties of null"
```

## Fix

Return an empty `Set` instead of `null` when a utility has no property
keys:

```ts
return result ?? new Set<string>()
```

This is semantically correct: a utility with no standard properties
cannot be linked to or collapsed with any other utility, which is
exactly what an empty Set represents in the linking algorithm. It won't
cause false collapses or suppress valid collapses of other utilities.

## Test plan

- Added test: `collapse does not crash when utilities with no standard
properties are present`
- Verifies `shadow-sm + border`, `shadow-md + p-4`, and `shadow-sm +
shadow-md` don't throw
- Verifies the candidates are returned uncollapsed (correct behavior)
- All 1218 existing tests continue to pass

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-03-10 16:48:07 +01:00
Robin Malfait
c586bd6a94
Canonicalize calc(var(--spacing)*…) expressions into --spacing(…) (#19769)
This PR canonicalizes usages of `calc(var(--spacing)*…)` to
`--spacing(…)` when used in arbitrary values.

Some examples:

| Before | After |
| --- | --- |
| `pt-[min(20%,calc(var(--spacing)*8))]` | `pt-[min(20%,--spacing(8))]`
|
| `pt-[min(20%,calc(var(--spacing)*var(--other)))]` |
`pt-[min(20%,--spacing(var(--other)))]` |
| `pt-[calc(var(--spacing)*8)]` | `pt-8` |
| `pt-[calc(var(--spacing)*var(--other))]` |
`pt-[--spacing(var(--other))]` |
| `[padding-top:min(20%,calc(var(--spacing)*8))]` |
`pt-[min(20%,--spacing(8))]` |
| `[padding-top:min(20%,calc(var(--spacing)*var(--other)))]` |
`pt-[min(20%,--spacing(var(--other)))]` |
| `[padding-top:calc(var(--spacing)*8)]` | `pt-8` |
| `[padding-top:calc(var(--spacing)*var(--other))]` |
`pt-[--spacing(var(--other))]` |


## Test plan

1. Existing tests pass
2. Added new tests
2026-03-08 20:19:35 +01:00
Robin Malfait
bf2e2fe08a
Extract classes from interpolated expressions in Ruby (#19730)
This PR ensures that interpolated expressions in Ruby syntax are
correctly extracted.

The issue was that we ignore comments in Ruby syntax (which start with
`#`). We already made an exception for locals (`<%# locals: … %>`), but
we also need to handle interpolated expressions (`#{ … }`) in the same
way because they are not comments.

Fixes: #19728

## Test plan

1. Existing tests pass
2. Added a regression test for this scenario
3. Tested using the extractor on the given code snippet:

<img width="1461" height="1856" alt="image"
src="https://github.com/user-attachments/assets/b48f7042-ca9d-4133-87ae-4c37c633c073"
/>

Notice that the `w-100` gets extracted now.
2026-02-26 12:23:50 +01:00
Adam Wathan
9ded4a23de
Guard object lookups against inherited prototype properties (#19725)
When user-controlled candidate values like "constructor" are used as
keys to look up values in plain objects (staticValues, plugin values,
modifiers, config), they can match inherited Object.prototype properties
instead of returning undefined. This caused crashes like "V.map is not
a function" when scanning source files containing strings like
"row-constructor".

Use Object.hasOwn() checks before all user-keyed object lookups in:
- utilities.ts (staticValues lookup)
- plugin-api.ts (values, modifiers, and variant values lookups)
- plugin-functions.ts (get() config traversal function)

Fixes #19721

https://claude.ai/code/session_011CYSGw3DLh2Z8xnuyoaCgC

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-25 15:16:09 +00:00
Robin Malfait
097f982d7a
update changelog 2026-02-23 13:46:49 +01:00
Robin Malfait
1dce64ee7e
4.2.1 (#19714) 2026-02-23 11:45:12 +01:00
Robin Malfait
58d1fe3948
Fix missing extracted classes in mdx files (#19711) 2026-02-22 13:02:05 +01:00
Robin Malfait
1a973557ed
update webpack readme to be a bit more consistent with other readmes 2026-02-20 12:04:41 +01:00
Robin Malfait
14083ad9aa
use simpler tailwindcss import 2026-02-20 12:03:25 +01:00
Kirk Ouimet
d15d92ca60
Allow trailing dash in functional utility names (#19696)
## Problem

Tailwind 4.2.0 introduced stricter `@utility` name validation (#19524)
that rejects functional utility names where the root ends with a dash
after stripping the `-*` suffix. This breaks a valid and useful naming
pattern where a double dash separates the CSS property from a value
scale:

```css
@utility border--* {
  border-color: --value(--color-border-*, [color]);
}
```

This produces: `border--0`, `border--1`, `border--2`, etc.

The error message is:

> `@utility border--*` defines an invalid utility name. Utilities should
be alphanumeric and start with a lowercase letter.

## Why this pattern matters

The double-dash convention creates a clear visual grammar in class
names. The first segment names the CSS property, and the double dash
separates it from the semantic scale value. In a dense className string
like `border border--0 background--0 content--4`, the scale values (0,
0, 4) are immediately scannable, distinct from the single-dash property
names around them.

This pattern is actively used in production design systems for semantic
color scales (background, content, border, shadow) with values from
0-10.

## Why the restriction is unnecessary

The validation comment states the concern is that `border--*` could
match the bare class `border-` when using default values. However, this
edge case is already handled:

1. **`findRoots` in `candidate.ts`** (line 887) already rejects empty
values: `if (root[1] === '') break`
2. **The Oxide scanner** already extracts double-dash candidates
correctly, as confirmed by existing tests: `("items--center",
vec!["items--center"])`

The candidate parser and scanner both handle this case. The validation
was an overcorrection.

## Changes

- Removed the trailing-dash check from `isValidFunctionalUtilityName` in
`utilities.ts`
- Updated the existing unit test from `['foo--*', false]` to `['foo--*',
true]`
- Added an integration test proving `@utility border--*` compiles
correctly with theme values

## Test results

All 4121 tests pass across the tailwindcss package, including the new
integration test.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-19 14:40:37 +00:00
ginnyTheCat
7a54d15f72
Correct CHANGELOG to mention inset not inline (#19700)
I noticed this when reading the changelog to migrate a project to the
new version. I'm not sure whether retroactively changing the changelog
is considered acceptable behaviour in this project or whether appending
a notice in the next changelog would be better.

Funnily this was actually guessed in #19613, though hard to notice among
the other many false guesses.
2026-02-19 14:05:37 +01:00
Robin Malfait
1b16411919
4.2.0 (#19695)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2026-02-18 15:45:06 +01:00
Pádraic Slattery
198af9c3ae
chore: Update outdated GitHub Actions versions (#19577)
## Summary  
This PR updates outdated GitHub Action versions by migrating
`actions/download-artifact` from `v6` to `v7` in the following
workflows:
- `.github/workflows/prepare-release.yml`  
- `.github/workflows/release.yml`  
- `.github/workflows/release-insiders.yml`  

## Test plan  
The changes will be tested in the CI pipeline of the pull request.  

[×] Summary  
[×] Test plan
2026-02-18 12:27:13 +00:00
Pavan Shinde
6118f4f6a7
Fix/misc docs and tests (#19652)
## Summary
This PR makes a few small consistency fixes:

- Fix outdated GitHub links that still point to the old
`tailwindcss/tailwindcss` org and update them to
`tailwindlabs/tailwindcss` (README + contributing docs + PR template).
- Fix punctuation in the contributing guide (“i.e., …”).
- Update two test titles to use “cannot” instead of “can not” for
consistency.

No behavior changes.

## Test plan
- Link check (manual): clicked the updated GitHub links in `README.md`,
`.github/CONTRIBUTING.md`, and `.github/PULL_REQUEST_TEMPLATE.md` to
confirm they resolve correctly.
- (Optional) `pnpm test` — not required since changes are
docs/test-title-only.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 12:23:48 +00:00
Cameron
5a4a7eba3a
fix(canonicalize): prevent collapse cache pollution across calls (#19675)
## Summary

fixes
https://github.com/schoero/eslint-plugin-better-tailwindcss/issues/321

This PR fixes an order-sensitive canonicalization bug. This bug caused
issues when running eslint-plugin-better-tailwindcss as the order in
which files are linted in is not consistent. This caused, in some
scenarios, `canonicalizeCandidates(..., { collapse: true,
logicalToPhysical: true, rem: 16 })` to stop collapsing valid
combinations (for example `h-4 + w-4 -> size-4`) after unrelated prior
calls.

To reproduce this issue:
```
# checkout this branch
$ git checkout c/fix-canonicalizeCandidates
# Revert the fix to the current `main` branch
$ git checkout main ./packages/tailwindcss/src/canonicalize-candidates.ts
# Run the tests
$ pnpm run test
```

This should produce a failure like so:

```

 FAIL   tailwindcss  src/canonicalize-candidates.test.ts > regressions > collapse canonicalization is not affected by previous calls
AssertionError: expected [ 'underline', 'h-4', 'w-4' ] to deeply equal [ 'underline', 'size-4' ]

- Expected
+ Received

  [
    "underline",
-   "size-4",
+   "h-4",
+   "w-4",
  ]

 ❯ src/canonicalize-candidates.test.ts:1167:66
    1165|     designSystem.canonicalizeCandidates(['underline', 'mb-4'], options)
    1166| 
    1167|     expect(designSystem.canonicalizeCandidates(target, options)).toEqual(['underline', 'size-4'])
       |                                                                  ^
    1168|   })
    1169| })
```

```
# reset all changes on this branch
git reset --hard
# run the tests again (they should now pass)
pnpm run test
```

The cause of this bug is that the canonicalization caches used
`DefaultMap` in places where lookups were expected to be read-only.
`DefaultMap.get` inserts missing entried, which mutated shared cache
state during intermediate lookups and made later canonicalization
results depend on prior calls.

By replacing the use of `DefaultMap` with a plain `Map`, it avoids
inserting into the map on lookup paths. I've polyfilled
[`Map#getOrInsert`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsert)
as it is not widely available yet, and used that where appropriate.


## Test plan

I wrote a test that fails on `main` branch, I then fixed the issue, and
validated that the test now passes.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 12:16:59 +00:00
depfu[bot]
d520e1f571
Update bun 1.3.7 → 1.3.9 (patch) (#19678)
<!--depfu-start-->
> 👉 **This PR is queued up to get rebased by Depfu**
<!--depfu-end-->





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

### What changed?




#### ✳️ bun (1.3.7 → 1.3.9) · [Repo](https://github.com/oven-sh/bun)





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>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 12:00:11 +01:00
depfu[bot]
acf8dd0bf0
Update @types/bun 1.3.7 → 1.3.9 (patch) (#19689)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/bun (1.3.7 → 1.3.9) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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-02-18 11:59:30 +01:00
depfu[bot]
d9c4cd8c7c
Update enhanced-resolve 5.18.4 → 5.19.0 (minor) (#19658)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ enhanced-resolve (5.18.4 → 5.19.0) ·
[Repo](https://github.com/webpack/enhanced-resolve)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/webpack/enhanced-resolve/releases/tag/v5.19.0">5.19.0</a></h4>

<blockquote><h3 dir="auto">Features</h3>
<ul dir="auto">
<li>Added TsconfigPathsPlugin (replacement for <code
class="notranslate">tsconfig-paths-webpack-plugin</code>) .</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="e8a6ac9fdd...24ba233554">See
the full diff on Github</a>. The new version differs by 4 commits:</p>
<ul>
<li><a
href="24ba233554"><code>chore(release):
5.19.0</code></a></li>
<li><a
href="10dc3bc8b8"><code>chore:
update eslint config webpack (#471)</code></a></li>
<li><a
href="0d6c6c0fd9"><code>feat:
added TsconfigPathsPlugin (replacement for
tsconfig-paths-webpack-plugin) (#463)</code></a></li>
<li><a
href="958aa89219"><code>chore:
migrate to npm from yarn (#469)</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-02-18 11:59:11 +01:00
depfu[bot]
fb42a3ff9e
Update semver 7.7.3 → 7.7.4 (patch) (#19667)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ semver (7.7.3 → 7.7.4) ·
[Repo](https://github.com/npm/node-semver) ·
[Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/npm/node-semver/releases/tag/v7.7.4">7.7.4</a></h4>

<blockquote><h2 dir="auto">
<a
href="https://bounce.depfu.com/github.com/npm/node-semver/compare/v7.7.3...v7.7.4">7.7.4</a>
(2026-01-16)</h2>
<h3 dir="auto">Bug Fixes</h3>
<ul dir="auto">
<li>
<a
href="a29faa5f33"><code
class="notranslate">a29faa5</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/835">#835</a>
cli: pass options to semver.valid() for loose version validation (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/835">#835</a>)
(<a
href="https://bounce.depfu.com/github.com/mldangelo">@mldangelo</a>)</li>
</ul>
<h3 dir="auto">Documentation</h3>
<ul dir="auto">
<li>
<a
href="1d28d5e82d"><code
class="notranslate">1d28d5e</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/836">#836</a>
fix typos and update -n CLI option documentation (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/836">#836</a>)
(<a
href="https://bounce.depfu.com/github.com/mldangelo">@mldangelo</a>)</li>
</ul>
<h3 dir="auto">Dependencies</h3>
<ul dir="auto">
<li>
<a
href="120968b767"><code
class="notranslate">120968b</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/840">#840</a>
<code class="notranslate">@npmcli/template-oss@4.29.0</code> (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/840">#840</a>)</li>
</ul>
<h3 dir="auto">Chores</h3>
<ul dir="auto">
<li>
<a
href="44d7130c60"><code
class="notranslate">44d7130</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/824">#824</a>
bump @npmcli/eslint-config from 5.1.0 to 6.0.0 (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/824">#824</a>)
(<a
href="https://bounce.depfu.com/github.com/dependabot">@dependabot</a>[bot])</li>
<li>
<a
href="70735767b6"><code
class="notranslate">7073576</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/820">#820</a>
reorder parameters in invalid-versions.js test (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/820">#820</a>)
(<a href="https://bounce.depfu.com/github.com/reggi">@reggi</a>)</li>
<li>
<a
href="5816d4cfd6"><code
class="notranslate">5816d4c</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/829">#829</a>
bump @npmcli/template-oss from 4.28.0 to 4.28.1 (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/829">#829</a>)
(<a
href="https://bounce.depfu.com/github.com/dependabot">@dependabot</a>[bot],
<a
href="https://bounce.depfu.com/github.com/npm-cli-bot">@npm-cli-bot</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="a25789b09b...5993c2e42b">See
the full diff on Github</a>. The new version differs by 10 commits:</p>
<ul>
<li><a
href="5993c2e42b"><code>chore:
release 7.7.4 (#839)</code></a></li>
<li><a
href="120968b767"><code>deps:
@npmcli/template-oss@4.29.0 (#840)</code></a></li>
<li><a
href="a29faa5f33"><code>fix(cli):
pass options to semver.valid() for loose version validation
(#835)</code></a></li>
<li><a
href="1d28d5e82d"><code>docs:
fix typos and update -n CLI option documentation (#836)</code></a></li>
<li><a
href="5816d4cfd6"><code>chore:
bump @npmcli/template-oss from 4.28.0 to 4.28.1 (#829)</code></a></li>
<li><a
href="ab9e28a582"><code>chore:
bump @npmcli/template-oss from 4.27.1 to 4.28.0 (#827)</code></a></li>
<li><a
href="44d7130c60"><code>chore:
bump @npmcli/eslint-config from 5.1.0 to 6.0.0 (#824)</code></a></li>
<li><a
href="70735767b6"><code>chore:
reorder parameters in invalid-versions.js test (#820)</code></a></li>
<li><a
href="16a35f5df4"><code>chore:
bump @npmcli/template-oss from 4.26.0 to 4.27.1 (#823)</code></a></li>
<li><a
href="3a3459da45"><code>chore:
bump @npmcli/template-oss from 4.25.1 to 4.26.0 (#818)</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-02-18 11:58:49 +01:00
depfu[bot]
06b6473fcc
Update @types/react 19.2.10 → 19.2.14 (patch) (#19681)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.2.10 → 19.2.14) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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-02-18 11:58:33 +01:00
Adam Wathan
d0a5612872
Add mauve, olive, mist, and taupe color palettes (#19627)
## Summary

This PR adds four new neutral color palettes to Tailwind CSS: `mauve`,
`olive`, `mist`, and `taupe`. Each palette includes 11 shades (50, 100,
200, 300, 400, 500, 600, 700, 800, 900, 950) defined using the OKLch
color space for perceptually uniform color transitions.

These new palettes expand the available neutral color options beyond the
existing stone, slate, gray, zinc, and neutral palettes, providing
designers with more nuanced choices for different design systems and
brand aesthetics.

The changes include:
- Added color definitions to `packages/tailwindcss/src/compat/colors.ts`
- Added corresponding CSS custom properties to
`packages/tailwindcss/theme.css`

## Test plan

To verify these changes:
1. Build the project and ensure no compilation errors occur
2. Verify that the new color palettes are available in the Tailwind
config
3. Test that utilities like `bg-mauve-500`, `text-olive-600`,
`border-mist-300`, and `ring-taupe-400` work correctly
4. Confirm that the colors render with proper OKLch values in the
browser

https://claude.ai/code/session_01QhC1ZYkW3pCd55WbbqYBNV

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 11:52:35 +01:00
Pavan Shinde
d9fff9f595
docs: update package README CI badge to main (#19692)
## Summary
Update the GitHub Actions CI status badge URLs in `packages/*/README.md`
to track `branch=main` instead of `branch=next`. This matches the root
README, keeps the badges consistent across the repo, and ensures the
displayed CI status reflects the default branch.

## Test plan
Docs-only change.
Verified the updated badge URLs in the modified `packages/*/README.md`
files use `ci.yml?branch=main`.
2026-02-18 11:52:07 +01:00
Rózsa Zoltán
ed52d3e6c9
feat: handle backslash in @utility name (#19626)
Resolves #19607

<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

I believe there is no obstacle to simply ignoring backslashes in the
name. This way, various validators - which are not aware of Tailwind
CSS's specific syntax (which allows the `/` character to be used
directly in utility names) - can still be bypassed using backslashes.
For example, instead of `@utility push-1/2`, one could use `@utility
push-1\/2`, while the end result would be identical.

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

I took a previous utility test as a baseline and extended it with
backslashes, and I expect the same result in the output as in the
original test case:

*
https://github.com/rozsazoltan/tailwindcss/blob/main/packages/tailwindcss/src/utilities.test.ts#L28553-L28567
(the original test case I started from)
*
https://github.com/rozsazoltan/tailwindcss/blob/feat/handle-blackslash-in-utility-name/packages/tailwindcss/src/index.test.ts#L4640-L4654
(the current PR's test)

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-17 21:12:20 +00:00
Andrey Viktorov
bc6e4b85bf
Fallback to config.createResolver for client and ssr environments in @tailwindcss/vite (#19679)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

Sometimes even if Vite Envrionment API is available, some plugins are
still override `config.createResolver` function to inject own aliases

Since technically `config.createResolver` was only [properly
deprecated](https://github.com/vitejs/vite/pull/20031) in Vite 7.0.0,
it's still a valid(-ish) to do so, even if it wasn't ever officially
supported

Vite already handles this in its internal css resolvers, but not exposes
the code to do so as part of public API, so I've copied and adapted it

Fixes #19677

## Test plan

Tested by copying built package into my repro from the issue, also ran
vite integration tests

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-17 19:59:57 +00:00
Robin Malfait
095ff96ba3
Improve performance in bigger projects (#19632)
This PR improves the performance of Oxide when scanning large codebases.

The `Oxide` API, looks something like this:

```ts
let scanner = new Scanner({ sources })
let candidates = scanner.scan() // Found candidates
let files = scanner.files // Scanned files
let globs = scanner.globs // Scanned globs
```

The `files` and `globs` are used to tell PostCSS, Vite, webpack etc
which files to watch for changes.
The `.scan()` operation extracts the candidates from the source files.
You can think of these as potential Tailwind CSS classes.

In all these scenarios we have to walk the file system and find files
that match the `sources`.

### 1. Prevent multiple file system walks

The first big win came from the fact that accessing `.files` after a
`.scan()` also does an entire walk of the file system (for the given
`sources`), which is unnecessary because we just walked the file system.

This is something that's not really an issue in smaller codebases
because we have `mtime` tracking. We don't re-scan a file if its `mtime`
hasn't changed since the last scan. However, in large codebases with
thousands of files, even walking the file system to check `mtime`s can
be expensive.

### 2. Use parallel file system walking

Another big win is to use a parallel file system walker instead of a
synchronous one. The big problem here is that the parallel build has
20ms-50ms of overhead which is noticeable on small codebases. We don't
really know if you have a small or big codebase ahead of time, so maybe
some kind of hint in the future would be useful.

So the solution I settled on right now is to use a synchronous walker
for the initial scan, and then switch to a parallel walker for
subsequent scans (think dev mode). This gives us the best of both
worlds: fast initial scan on small codebases, and fast re-scans on large
codebases.

Caveat: if you use the `@tailwindcss/cli` we know exactly which files
changed so we can just re-scan those files directly without walking the
file system at all. But in `@tailwindcss/postcss` we don't know which
files changed, so we have to walk the file system to check `mtime`s.

While this improvement is nice, it resulted in an annoying issue related
to `mtime` tracking. Since the parallel walker processes files in
parallel, the `mtime` was typed as `Arc<Mutex<FxHashMap<PathBuf,
SystemTime>>>` so to avoid locking, I decided to only walk the files
here and collect their paths. Then later we check the `mtime` to know
whether to re-scan them or not.

Initially I just removed the `mtime` tracking altogether. But it did
have an impact when actually extracting candidates from those files, so
I added it back later.

### 3. Delaying work

I was still a bit annoyed by the fact that we had to track `mtime`
values for every file. This seems like annoying overhead, especially
when doing a single build (no dev mode).

So the trick I applied here is to only start tracking `mtime` values
after the initial scan.

This means that, in dev mode, we would do this:

1. Walk entire file system to track files.
2. On a subsequent scan, walk entire file system (again) and start
tracking `mtime` values. This time, we use the parallel walker instead
of the synchronous one.
3. On further scans, only re-scan files whose `mtime` has changed

The trade-off here is that on the second scan we always re-scan all
files, even if they haven't changed. Since this typically only happens
in dev mode, I think this is an acceptable trade-off especially if the
initial build is therefor faster this way.

### 3. Small wins

There are also a few small wins in here that I would like to mention but
that are less significant:

1. Pre-computed normalized `source` patterns instead of in every walker
filter call.
2. Tried to avoid some allocations in various places. For example the
`pre_process_input` always called `content.to_vec()` which allocates.
Instead we now accept an owned `Vec<u8>` so we don't have to call
`.to_vec()` in the default case (in my testing, this is ~92% of the time
in the codebases I checked).
3. Made the `Cursor` struct smaller, which is used a lot during
candidate extraction.

### Benchmarks

Now for the fun stuff, the benchmarks!

<details>

<summary>The code for the benchmarks</summary>

```ts
import path from 'node:path'
import { bench, boxplot, do_not_optimize, run, summary } from 'mitata'

import { Scanner as ScannerPr } from '/path/to/repo/with/pr/branch/tailwindcss/crates/node'
import { Scanner as ScannerMain } from '/path/to/repo/with/main/branch/tailwindcss/crates/node'

let base = '/path/to/some/codebase'
let sources = [{ base, pattern: '**/*', negated: false }]

// Verify the results are the same before benchmarking
let scannerPr = new ScannerPr({ sources })
let scannerMain = new ScannerMain({ sources })

{
  let aCandidates = scannerPr.scan()
  let bCandidates = scannerMain.scan()

  if (aCandidates.length !== bCandidates.length) {
    throw new Error(`Mismatch in candidate count: ${aCandidates.length} vs ${bCandidates.length}`)
  }

  for (let i = 0; i < aCandidates.length; i++) {
    if (aCandidates[i] !== bCandidates[i]) {
      throw new Error(`Mismatch in candidate at index ${i}: ${aCandidates[i]} vs ${bCandidates[i]}`)
    }
  }

  let aFiles = scannerPr.files
  let bFiles = scannerMain.files

  if (aFiles.length !== bFiles.length) {
    throw new Error(`Mismatch in file count: ${aFiles.length} vs ${bFiles.length}`)
  }

  for (let i = 0; i < aFiles.length; i++) {
    if (aFiles[i] !== bFiles[i]) {
      throw new Error(`Mismatch in file at index ${i}: ${aFiles[i]} vs ${bFiles[i]}`)
    }
  }

  console.log('Scanned', aFiles.length, 'files')
  console.log('Extracted', aCandidates.length, 'candidates')
  console.log('Base =', base)
  console.log()
}

summary(() => {
  boxplot(() => {
    bench('PR (build, .scan()))', function* () {
      yield {
        [0]() {
          return new ScannerPr({ sources })
        },
        bench(scanner: ScannerPr) {
          do_not_optimize(scanner.scan())
        },
      }
    })

    bench('main (build, .scan()))', function* () {
      yield {
        [0]() {
          return new ScannerMain({ sources })
        },
        bench(scanner: ScannerMain) {
          do_not_optimize(scanner.scan())
        },
      }
    })
  })
})

summary(() => {
  boxplot(() => {
    bench('PR (build, .scan() + .files)', function* () {
      yield {
        [0]() {
          return new ScannerPr({ sources })
        },
        bench(scanner: ScannerPr) {
          do_not_optimize(scanner.scan())
          do_not_optimize(scanner.files)
        },
      }
    })

    bench('main (build, .scan() + .files)', function* () {
      yield {
        [0]() {
          return new ScannerMain({ sources })
        },
        bench(scanner: ScannerMain) {
          do_not_optimize(scanner.scan())
          do_not_optimize(scanner.files)
        },
      }
    })
  })
})

summary(() => {
  boxplot(() => {
    bench('PR (watch, .scan()))', function* () {
      yield {
        bench() {
          do_not_optimize(scannerPr.scan())
        },
      }
    })

    bench('main (watch, .scan()))', function* () {
      yield {
        bench() {
          do_not_optimize(scannerMain.scan())
        },
      }
    })
  })
})

summary(() => {
  boxplot(() => {
    bench('PR (watch, .scan() + .files)', function* () {
      yield {
        bench() {
          do_not_optimize(scannerPr.scan())
          do_not_optimize(scannerPr.files)
        },
      }
    })

    bench('main (watch, .scan() + .files)', function* () {
      yield {
        bench() {
          do_not_optimize(scannerMain.scan())
          do_not_optimize(scannerMain.files)
        },
      }
    })
  })
})

await run()
```

</details>

#### tailwindcss.com codebase

```
Scanned 462 files
Extracted 13200 candidates
Base = /Users/robin/github.com/tailwindlabs/tailwindcss.com

clk: ~3.09 GHz
cpu: Apple M1 Max
runtime: bun 1.3.3 (arm64-darwin)
```

In these benchmarks the `PR` one is consistently faster than `main`.
It's not by a lot but that's mainly because the codebase itself isn't
that big. It is a codebase with _a lot_ of candidates though, but not
that many files.

The candidate extraction was already pretty fast, so the wins here
mainly come from avoiding re-walking the file system when accessing
`.files`, and from delaying `mtime` tracking until after the initial
scan.

**Single initial build**:

It's not a lot, but it's a bit faster. This is due to avoiding tracking
the `mtime` values initially and making some small optimizations related
to the struct size and allocations.

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (build, .scan()))            22.87 ms/iter  23.28 ms               █
                        (21.49 ms … 25.68 ms)  23.98 ms     ▂  ▂   ▂  █  ▂▂
                      (832.00 kb …   2.69 mb)   1.41 mb ▆▆▆▆█▆▆█▁▆▆█▁▁█▁▆██▁▆

main (build, .scan()))          25.67 ms/iter  26.12 ms █   █      █
                        (24.54 ms … 27.74 ms)  27.06 ms █   █ █    ███
                      (432.00 kb …   2.78 mb) 996.00 kb ██▁████▁█▁████▁█▁▁█▁█

                               ┌                                            ┐
                               ╷    ┌─────┬──┐     ╷
          PR (build, .scan())) ├────┤     │  ├─────┤
                               ╵    └─────┴──┘     ╵
                                                        ╷  ┌─────┬──┐       ╷
        main (build, .scan()))                          ├──┤     │  ├───────┤
                                                        ╵  └─────┴──┘       ╵
                               └                                            ┘
                               21.49 ms           24.28 ms           27.06 ms

summary
  PR (build, .scan()))
   1.12x faster than main (build, .scan()))
```


**Single initial build + accessing `.files`**:

We don't have to re-walk the entire file system even if we're just
dealing with ~462 scanned files.

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (build, .scan() + .files)    22.54 ms/iter  22.99 ms █      ▂
                        (21.41 ms … 25.86 ms)  24.26 ms █ ▅  ▅▅█▅  ▅▅
                      (368.00 kb …   2.05 mb) 853.00 kb █▇█▇▇████▇▇██▁▁▇▁▁▇▁▇

main (build, .scan() + .files)  32.15 ms/iter  32.17 ms  █  ▂
                        (30.78 ms … 36.22 ms)  35.75 ms ▅█ ▅█  ▅
                      (400.00 kb …   2.45 mb) 952.00 kb ██▁██▇▇█▇▁▁▁▁▁▁▁▁▁▁▁▇

                               ┌                                            ┐
                               ╷┌──┬┐   ╷
  PR (build, .scan() + .files) ├┤  │├───┤
                               ╵└──┴┘   ╵
                                                            ╷┌───┬          ╷
main (build, .scan() + .files)                              ├┤   │──────────┤
                                                            ╵└───┴          ╵
                               └                                            ┘
                               21.41 ms           28.58 ms           35.75 ms

summary
  PR (build, .scan() + .files)
   1.43x faster than main (build, .scan() + .files)
```


**Watch/dev mode, only scanning**:

This now switches to the parallel walker, but since it's not a super big
codebase we don't see a huge win here yet.

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (watch, .scan()))             6.85 ms/iter   7.22 ms   █▄
                          (6.34 ms … 7.94 ms)   7.91 ms  ▄██▃
                      ( 64.00 kb … 688.00 kb) 452.82 kb ▃████▆▂▂▁▂▁▂▁▁▁▅█▆▃▅▃

main (watch, .scan()))           7.92 ms/iter   8.08 ms   █ █  ▃ █▃▃
                          (7.41 ms … 8.71 ms)   8.68 ms   █▆█▆▃█████
                      (  0.00  b …  64.00 kb)  19.20 kb ▆▄██████████▆▁▆▆█▄▄▄▆

                               ┌                                            ┐
                               ╷  ┌──────┬──────┐            ╷
          PR (watch, .scan())) ├──┤      │      ├────────────┤
                               ╵  └──────┴──────┘            ╵
                                                    ╷    ┌───┬──┐           ╷
        main (watch, .scan()))                      ├────┤   │  ├───────────┤
                                                    ╵    └───┴──┘           ╵
                               └                                            ┘
                               6.34 ms            7.51 ms             8.68 ms

summary
  PR (watch, .scan()))
   1.16x faster than main (watch, .scan()))
```

**Watch/dev mode, scanning + accessing `.files`**:

Again we avoid re-walking the entire file system when accessing
`.files`.

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (watch, .scan() + .files)    12.10 ms/iter  12.74 ms █ █     █ █ ▃▃▃
                        (10.69 ms … 13.89 ms)  13.81 ms █ █▂▂▂ ▇█▂█▂███▇
                      (128.00 kb …  10.73 mb)   5.23 mb █▆████▁█████████▆▆▆▆▆

main (watch, .scan() + .files)  14.44 ms/iter  14.74 ms   █
                        (13.93 ms … 15.33 ms)  15.18 ms   ███▅ █ ▅   ▅
                      ( 16.00 kb …  80.00 kb)  39.51 kb █▅████▁███▅▁█████▅▁▅▅

                               ┌                                            ┐
                               ╷      ┌──────┬──────┐         ╷
  PR (watch, .scan() + .files) ├──────┤      │      ├─────────┤
                               ╵      └──────┴──────┘         ╵
                                                               ╷ ┌───┬──┐   ╷
main (watch, .scan() + .files)                                 ├─┤   │  ├───┤
                                                               ╵ └───┴──┘   ╵
                               └                                            ┘
                               10.69 ms           12.93 ms           15.18 ms

summary
  PR (watch, .scan() + .files)
   1.19x faster than main (watch, .scan() + .files)
```

#### Synthetic 5000 files codebase

Based on the instructions from #19616 I created a codebase with 5000
files. Each file contains a `flex` class and a unique class like
`content-['/path/to/file']` to ensure we have a decent amount of unique
candidates.

You can test the script yourself by running this:
```
mkdir -p fixtures/app-5000/src/components/{auth,dashboard,settings,profile,notifications,messages,search,navigation,footer,sidebar}/sub{001..500} && for dir in fixtures/app-5000/src/components/*/sub*; do echo "export const Component = () => <div className=\"flex content-['$dir']\">test</div>" > "$dir/index.tsx"; done && find fixtures/app-5000/src/components -type f | wc -lc
```

```
Scanned 5000 files
Extracted 5005 candidates
Base = /Users/robin/github.com/RobinMalfait/playground/scanner-benchmarks/fixtures/app-5000

clk: ~3.08 GHz
cpu: Apple M1 Max
runtime: bun 1.3.3 (arm64-darwin)
```

**Single initial build**:

As expected not a super big win here because it's a single build. But
there is a noticeable improvement.

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (build, .scan()))           217.27 ms/iter 211.97 ms              █
                      (205.99 ms … 289.53 ms) 214.33 ms ▅      ▅ ▅▅▅▅█▅▅    ▅
                      (  3.34 mb …   4.25 mb)   3.72 mb █▁▁▁▁▁▁█▁███████▁▁▁▁█

main (build, .scan()))         249.26 ms/iter 239.88 ms               █
                      (231.51 ms … 381.66 ms) 241.01 ms ▅    ▅ ▅▅  ▅  █▅  ▅▅▅
                      (  4.22 mb …   4.78 mb)   4.49 mb █▁▁▁▁█▁██▁▁█▁▁██▁▁███

                               ┌                                            ┐
                               ╷    ┌─────╷──┬
          PR (build, .scan())) ├────┤     ┤  │
                               ╵    └─────╵──┴
                                                                ╷   ┌───────╷
        main (build, .scan()))                                  ├───┤       ┤
                                                                ╵   └───────╵
                               └                                            ┘
                               205.99 ms         223.50 ms          241.01 ms

summary
  PR (build, .scan()))
   1.15x faster than main (build, .scan()))
```

**Single initial build + accessing `.files`**:

Now things are getting interesting. Almost a 2x speedup by avoiding
re-walking the file system when accessing `.files`.

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (build, .scan() + .files)   216.35 ms/iter 214.53 ms █ █ █
                      (211.00 ms … 242.64 ms) 221.45 ms █ █▅█ ▅▅      ▅     ▅
                      (  2.97 mb …   4.47 mb)   3.97 mb █▁███▁██▁▁▁▁▁▁█▁▁▁▁▁█

main (build, .scan() + .files) 414.79 ms/iter 406.05 ms     ██
                      (396.72 ms … 542.30 ms) 413.69 ms ▅   ██▅ ▅▅ ▅ ▅      ▅
                      (  5.19 mb …   6.03 mb)   5.63 mb █▁▁▁███▁██▁█▁█▁▁▁▁▁▁█

                               ┌                                            ┐
                               ┌┬╷
  PR (build, .scan() + .files) ││┤
                               └┴╵
                                                                        ╷┌──╷
main (build, .scan() + .files)                                          ├┤  ┤
                                                                        ╵└──╵
                               └                                            ┘
                               211.00 ms         312.34 ms          413.69 ms

summary
  PR (build, .scan() + .files)
   1.92x faster than main (build, .scan() + .files)
```

**Watch/dev mode, only scanning**:

This is where we see bigger wins because now we're using the parallel
walker.

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (watch, .scan()))            76.26 ms/iter  77.41 ms                     █
                        (73.56 ms … 79.02 ms)  77.81 ms ▅   ▅  ▅ ▅▅ ▅▅▅   ▅ █
                      (  2.53 mb …   5.52 mb)   3.06 mb █▁▁▁█▁▁█▁██▁███▁▁▁█▁█

main (watch, .scan()))         166.71 ms/iter 165.14 ms █  █
                      (161.49 ms … 198.26 ms) 168.99 ms █ ▅█ ▅▅▅  ▅  ▅      ▅
                      (  1.08 mb …   2.72 mb)   1.24 mb █▁██▁███▁▁█▁▁█▁▁▁▁▁▁█

                               ┌                                            ┐
                               ╷┬┐
          PR (watch, .scan())) ├││
                               ╵┴┘
                                                                        ╷┌─┬╷
        main (watch, .scan()))                                          ├┤ │┤
                                                                        ╵└─┴╵
                               └                                            ┘
                               73.56 ms          121.28 ms          168.99 ms

summary
  PR (watch, .scan()))
   2.19x faster than main (watch, .scan()))
```

**Watch/dev mode, scanning + accessing `.files`**:

This is the biggest win of them all because we have all the benefits
combined:

1. Avoiding re-walking the file system when accessing `.files`
2. Using the parallel walker for faster file system walking

```
benchmark                     avg (min … max) p75 / p99    (min … top 1%)
--------------------------------------------- -------------------------------
PR (watch, .scan() + .files)    84.04 ms/iter  84.84 ms            █
                        (80.96 ms … 87.53 ms)  87.27 ms ▅▅   ▅▅ ▅▅ █▅ ▅     ▅
                      ( 15.42 mb …  31.34 mb)  22.16 mb ██▁▁▁██▁██▁██▁█▁▁▁▁▁█

main (watch, .scan() + .files) 338.59 ms/iter 353.89 ms   █
                      (321.87 ms … 378.43 ms) 358.70 ms   █   █
                      (  2.39 mb …   2.45 mb)   2.42 mb ███▁▁██▁▁▁▁▁▁▁▁▁▁██▁█

                               ┌                                            ┐
                               ┬┐
  PR (watch, .scan() + .files) ││
                               ┴┘
                                                                      ┌──┬─┐╷
main (watch, .scan() + .files)                                        │  │ ├┤
                                                                      └──┴─┘╵
                               └                                            ┘
                               80.96 ms          219.83 ms          358.70 ms

summary
  PR (watch, .scan() + .files)
   4.03x faster than main (watch, .scan() + .files)
```


## Test plan

1. All existing tests still pass
2. All public APIs remain the same
3. In the benchmarks I'm sharing, I first verify that the candidates
returned and the files returned are the same before and after the
change.
4. Benchmarked against real codebases, and against a synthetic large
codebase (5000 files).

Fixes: #19616
2026-02-17 20:15:20 +01:00
Arsher. {Zach}
f212b0fc22
fix: restore full page reload for watched external files on Vite 7.1+ (#19670)
# PR: Fix @source file changes not triggering full page reload on Vite
7.1+

## Description
This PR addresses issue #19637 where template files (PHP, HTML, Blade,
etc.) watched via the `@source` directive fail to trigger a full page
reload when using Vite 7.1 or newer.

## Root Cause
Vite 7.1 introduced the Environment API, which supersedes the legacy
WebSocket API for HMR. Specifically:
- `server.ws.send` is deprecated/ignored for certain external file
updates in favor of `server.hot.send`.
- The `@tailwindcss/vite` plugin currently collects `ViteDevServer`
instances but lacks a `handleHotUpdate` hook to explicitly trigger
reloads for non-module files added via `addWatchFile`.

## Changes
- Implemented a `handleHotUpdate` hook in the `@tailwindcss/vite`
plugin.
- The hook identifies changes to files that are not part of the standard
Vite module graph (e.g., `.php`, `.html`) but are watched by Tailwind.
- Triggers a `full-reload` using the new `server.hot.send` API if
available (Vite 7.1+), with a fallback to `server.ws.send` for backward
compatibility.

## Verification
- Reproduced the issue in a standalone Vite 7.1.0 project using a mock
plugin with the legacy API.
- Confirmed that the browser fails to reload upon editing a watched
`.php` file.
- Verified that migrating to `server.hot.send` restores the expected
reload behavior.

[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-17 20:04:17 +01:00
Robin Malfait
6eb3b32434
Allow multiples of .25 in aspect-* fractions (#19688)
This PR reduces the restrictions of the `aspect-*` utilities when
dealing with fractional values.

Up until now, the numbers had to be positive integers, so `aspect-1/2`
was valid, but `aspect-8.5/11` was not.

This PR allows for any multiple of `.25` as a valid value, so
`aspect-8.5/11` is now valid, but `aspect-8.3/11` is not, this will
still require `aspect-[8.3/11]` arbitrary value syntax to be valid.

This behavior of allowing multiples of `.25` is consistent with other
utilities that handle bare values such as `w-2.5`.

## Test plan

1. Existing tests pass
2. Added a test for `aspect-8.5/11`

Fixes: #19663
Closes: #19680, #19669
2026-02-17 19:32:31 +01:00
Benjamin Bock
d74f8b8392
Add '.jj' to ignored content directories (#19687)
Ignore .jj directory, just like .git, .hg, .svn.

https://www.jj-vcs.dev/latest/

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-17 17:09:59 +00:00
Pavan Shinde
99a7c602f2
docs: fix GitHub links to tailwindlabs org (#19686)
## Summary 
Fix outdated GitHub links that still point to
`github.com/tailwindcss/tailwindcss` (old org). This updates references
across the README, contributing guide, and PR template to the canonical
`tailwindlabs/tailwindcss` URLs to avoid redirects/404s and keep repo
metadata consistent

## Test Plan
This change is docs-only.
2026-02-17 13:06:49 +01:00
depfu[bot]
257dc581ba
Update @vitejs/plugin-react 5.1.2 → 5.1.3 (patch) (#19651)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (5.1.2 → 5.1.3) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
















---
![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-02-09 12:20:45 -05:00
depfu[bot]
4614f5eaa7
Update autoprefixer 10.4.23 → 10.4.24 (patch) (#19642)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ autoprefixer (10.4.23 → 10.4.24) ·
[Repo](https://github.com/postcss/autoprefixer) ·
[Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/autoprefixer/releases/tag/10.4.24">10.4.24</a></h4>

<blockquote><ul dir="auto">
<li>Made Autoprefixer a little faster (by <a
href="https://bounce.depfu.com/github.com/Cherry">@Cherry</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="212ba3c189...36692c25e7">See
the full diff on Github</a>. The new version differs by 3 commits:</p>
<ul>
<li><a
href="36692c25e7"><code>Release
10.4.24 version</code></a></li>
<li><a
href="67df014ae1"><code>Update
dependencies</code></a></li>
<li><a
href="032440ee4a"><code>perf:
reduce array allocations (#1542)</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-02-09 12:20:25 -05:00
Pavan Shinde
8ed67bf551
Fix Tailwind CSS package README GitHub links (#19644)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary
These package READMEs referenced the wrong GitHub org
(tailwindcss/tailwindcss) and outdated branches (master/next) for common
project links.
Update them to point at tailwindlabs/tailwindcss on main for releases,
license, discussions, and contributing docs.
<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan
Docs-only change: No test required.
<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2026-02-07 19:26:38 -05:00
Pavan Shinde
e73c2472fd
Fix README links to use main branch (#19641)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary
The README still referenced the `next` branch for the CI badge and
contributing docs link, but the `next` branch no longer exists upstream.
This updates those URLs to point to `main` so the badge and contributing
docs link work correctly.
<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan
N/A, docs only change 
<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2026-02-06 16:51:07 +01:00
Pavan Shinde
ddb47e84da
Fix README license badge link (#19638)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary
The README license badge linked to a non-existent `master` branch (and
the wrong org).
This updates the link to the current location:
tailwindlabs/tailwindcss/blob/main/LICENSE.
<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan
- Opened `README.md` and clicked the license badge/link to confirm it
resolves to the LICENSE file on the `main` branch.
<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2026-02-05 10:59:10 -05:00
Robin Malfait
23d01bc474
only notify Discord once
If we're in a matrix, and one of the jobs fail, then we only want to
notify Discord once instead of for each failure.
2026-02-04 13:08:20 +01:00
Robin Malfait
1638f35c3a
Bump dependencies (#19608)
This PR bumps a bunch of dependencies. This also moves a few
dependencies that we use in multiple packages to the pnpm catalog.

Closes: #19603, #19604, #19576, #19575, #19573, #19565, #19547, #19546,
#19545, #19609, #19581, #19620, #19619

- #19603
- #19604
- #19576
- #19575
- #19573
- #19565
- #19547
- #19546
- #19545
- #19609
- #19581
- https://github.com/tailwindlabs/tailwindcss/pull/19620 
- #19620
- #19619

## Test Plan

All tests in CI should still pass. [ci-all]
2026-02-04 12:38:50 +01:00
Robin Malfait
df96ea5eba
Fix infinite loop when using @variant inside @custom-variant that points to another @custom-variant (#19633)
This PR fixes an infinite loop when you use a `@variant` inside of a
`@custom-variant`, where the `@variant` used is another
`@custom-variant`.

The issue stems from the fact that a `@custom-variant` can use a `@slot`
that we have to replace with the proper AST nodes. However in this
setup, the AST nodes will include a `@slot` node as well, which causes
us to replace the `@slot` again, and so on, causing an infinite loop.

```css
@custom-variant a {
  @slot;
}

@custom-variant b {
  @variant a {
    @slot;
  }
}
```

The solution here is to replace the `@slot` nodes and then skip walking
the nodes that were just inserted. This does mean that we end up with a
`@slot` node in the final AST but that's not a real issue because that
will get replaced later when handling the next `@custom-variant`.

## Test plan

1. Existing tests still pass
2. Added a regression test to ensure that the infinite loop does not
happen anymore
3. Added additional tests to ensure that the behavior is correct

Thanks @wongjn for your initial debugging help and providing a test case
as well!

Fixes: #19618
2026-02-02 14:42:31 +01:00
Adam Wathan
d52c94ff5f
Simplify logical sizing utility theme namespaces (#19625)
Update inline-size and block-size utilities to only read from --spacing
and --container theme keys, removing backwards-compat references to
--width, --height, --min-width, --min-height, --max-width, and
--max-height.

Since these are new utilities with no backwards compatibility concerns,
the simpler approach is preferred:
- inline/min-inline/max-inline: --spacing, --container
- block/min-block/max-block: --spacing only

https://claude.ai/code/session_01WhrjmutxsLP753VUtFy24S

<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-30 10:40:51 -05:00
Robin Malfait
d8b00d1114
add concurrency control to workflows
Essentially cancel previous workflows when new commits are pushed.
2026-01-30 15:31:23 +01:00
Adam Wathan
53205f5dc5
Add font-features-* utility for font-feature-settings (#19623)
Add a new arbitrary-value-only utility `font-features-*` that sets the
`font-feature-settings` CSS property. This utility only accepts
arbitrary
values (e.g., `font-features-["smcp"]`,
`font-features-[var(--features)]`).

The utility is sorted directly after `font-family` in the property
order.

https://claude.ai/code/session_01EAccbTHJ9dTUJ53ttq2jc4

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 14:21:16 +00:00
Adam Wathan
5395c7e764
Add logical inset utilities (inset-s, inset-e, inset-bs, inset-be) (#19613)
## Summary

This PR adds support for logical inset utilities that map to CSS logical
properties:

- `inset-s` → `inset-inline-start`
- `inset-e` → `inset-inline-end`
- `inset-bs` → `inset-block-start`
- `inset-be` → `inset-block-end`

These utilities complement the existing `inset-x` (inline) and `inset-y`
(block) utilities, providing more granular control over positioning in a
direction-aware manner. This aligns with CSS logical properties and
improves support for internationalization (RTL/LTR languages).

### Changes

1. **property-order.ts**: Added `inset-block-start` and
`inset-block-end` to the property ordering list to ensure consistent
cascade ordering
2. **utilities.ts**: Added four new utility mappings for the logical
inset properties
3. **utilities.test.ts**: Added comprehensive test coverage for all four
new utilities, including:
- Valid class generation with various value types (custom spacing,
percentages, arbitrary values)
   - Negative value support
   - Invalid class rejection (malformed syntax, invalid modifiers)

## Test plan

All changes are covered by unit tests in `utilities.test.ts`:
- `inset-s` test: 8 valid classes + 13 invalid class assertions
- `inset-e` test: 8 valid classes + 13 invalid class assertions  
- `inset-bs` test: 8 valid classes + 13 invalid class assertions
- `inset-be` test: 8 valid classes + 13 invalid class assertions

Each test verifies correct CSS output generation and proper rejection of
malformed utilities.

https://claude.ai/code/session_01JcYXVAMawRuntKatjku1WZ

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 14:13:56 +00:00
Adam Wathan
3d1e654c02
Add logical sizing utilities (inline-size, block-size) (#19612)
## Summary

Adds new utilities for CSS logical properties that provide
writing-direction-aware alternatives to width and height:

- `inline-*` for `inline-size` (logical equivalent of width)
- `min-inline-*` for `min-inline-size` (logical equivalent of min-width)
- `max-inline-*` for `max-inline-size` (logical equivalent of max-width)
- `block-*` for `block-size` (logical equivalent of height)
- `min-block-*` for `min-block-size` (logical equivalent of min-height)
- `max-block-*` for `max-block-size` (logical equivalent of max-height)

## New Utilities

### inline-size utilities

| Class | CSS Property |
|-------|-------------|
| `inline-auto` | `inline-size: auto` |
| `inline-full` | `inline-size: 100%` |
| `inline-min` | `inline-size: min-content` |
| `inline-max` | `inline-size: max-content` |
| `inline-fit` | `inline-size: fit-content` |
| `inline-screen` | `inline-size: 100vw` |
| `inline-svw` | `inline-size: 100svw` |
| `inline-lvw` | `inline-size: 100lvw` |
| `inline-dvw` | `inline-size: 100dvw` |
| `inline-{spacing}` | `inline-size: {value}` (e.g., `inline-4`,
`inline-px`) |
| `inline-{fraction}` | `inline-size: {percent}` (e.g., `inline-1/2`,
`inline-3/4`) |
| `inline-[{value}]` | `inline-size: {value}` (arbitrary values) |

### min-inline-size utilities

| Class | CSS Property |
|-------|-------------|
| `min-inline-auto` | `min-inline-size: auto` |
| `min-inline-full` | `min-inline-size: 100%` |
| `min-inline-min` | `min-inline-size: min-content` |
| `min-inline-max` | `min-inline-size: max-content` |
| `min-inline-fit` | `min-inline-size: fit-content` |
| `min-inline-screen` | `min-inline-size: 100vw` |
| `min-inline-svw` | `min-inline-size: 100svw` |
| `min-inline-lvw` | `min-inline-size: 100lvw` |
| `min-inline-dvw` | `min-inline-size: 100dvw` |
| `min-inline-{spacing}` | `min-inline-size: {value}` |
| `min-inline-[{value}]` | `min-inline-size: {value}` (arbitrary values)
|

### max-inline-size utilities

| Class | CSS Property |
|-------|-------------|
| `max-inline-none` | `max-inline-size: none` |
| `max-inline-full` | `max-inline-size: 100%` |
| `max-inline-min` | `max-inline-size: min-content` |
| `max-inline-max` | `max-inline-size: max-content` |
| `max-inline-fit` | `max-inline-size: fit-content` |
| `max-inline-screen` | `max-inline-size: 100vw` |
| `max-inline-svw` | `max-inline-size: 100svw` |
| `max-inline-lvw` | `max-inline-size: 100lvw` |
| `max-inline-dvw` | `max-inline-size: 100dvw` |
| `max-inline-{spacing}` | `max-inline-size: {value}` |
| `max-inline-[{value}]` | `max-inline-size: {value}` (arbitrary values)
|

### block-size utilities

| Class | CSS Property |
|-------|-------------|
| `block-auto` | `block-size: auto` |
| `block-full` | `block-size: 100%` |
| `block-min` | `block-size: min-content` |
| `block-max` | `block-size: max-content` |
| `block-fit` | `block-size: fit-content` |
| `block-screen` | `block-size: 100vh` |
| `block-lh` | `block-size: 1lh` |
| `block-svh` | `block-size: 100svh` |
| `block-lvh` | `block-size: 100lvh` |
| `block-dvh` | `block-size: 100dvh` |
| `block-{spacing}` | `block-size: {value}` (e.g., `block-4`,
`block-px`) |
| `block-{fraction}` | `block-size: {percent}` (e.g., `block-1/2`,
`block-3/4`) |
| `block-[{value}]` | `block-size: {value}` (arbitrary values) |

### min-block-size utilities

| Class | CSS Property |
|-------|-------------|
| `min-block-auto` | `min-block-size: auto` |
| `min-block-full` | `min-block-size: 100%` |
| `min-block-min` | `min-block-size: min-content` |
| `min-block-max` | `min-block-size: max-content` |
| `min-block-fit` | `min-block-size: fit-content` |
| `min-block-screen` | `min-block-size: 100vh` |
| `min-block-lh` | `min-block-size: 1lh` |
| `min-block-svh` | `min-block-size: 100svh` |
| `min-block-lvh` | `min-block-size: 100lvh` |
| `min-block-dvh` | `min-block-size: 100dvh` |
| `min-block-{spacing}` | `min-block-size: {value}` |
| `min-block-[{value}]` | `min-block-size: {value}` (arbitrary values) |

### max-block-size utilities

| Class | CSS Property |
|-------|-------------|
| `max-block-none` | `max-block-size: none` |
| `max-block-full` | `max-block-size: 100%` |
| `max-block-min` | `max-block-size: min-content` |
| `max-block-max` | `max-block-size: max-content` |
| `max-block-fit` | `max-block-size: fit-content` |
| `max-block-screen` | `max-block-size: 100vh` |
| `max-block-lh` | `max-block-size: 1lh` |
| `max-block-svh` | `max-block-size: 100svh` |
| `max-block-lvh` | `max-block-size: 100lvh` |
| `max-block-dvh` | `max-block-size: 100dvh` |
| `max-block-{spacing}` | `max-block-size: {value}` |
| `max-block-[{value}]` | `max-block-size: {value}` (arbitrary values) |

## Test plan

- [x] Added comprehensive unit tests for all new utilities
- [ ] Verify utilities work correctly in RTL layouts
- [ ] Test with vertical writing modes

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 13:46:37 +00:00
Adam Wathan
991072fcac
Add logical block-start/block-end border, margin, and padding utilities (#19601)
## Summary

This PR adds support for logical block-start and block-end CSS
properties in Tailwind CSS utilities. These properties are part of the
CSS Logical Properties specification and provide writing-mode-aware
alternatives to physical top/bottom properties.

The following new utilities are added:
- **Border utilities**: `border-bs-*` and `border-be-*` (for
`border-block-start` and `border-block-end`)
- **Margin utilities**: `mbs-*` and `mbe-*` (for `margin-block-start`
and `margin-block-end`)
- **Scroll margin utilities**: `scroll-mbs-*` and `scroll-mbe-*` (for
`scroll-margin-block-start` and `scroll-margin-block-end`)
- **Scroll padding utilities**: `scroll-pbs-*` and `scroll-pbe-*` (for
`scroll-padding-block-start` and `scroll-padding-block-end`)
- **Padding utilities**: `pbs-*` and `pbe-*` (for `padding-block-start`
and `padding-block-end`)

These utilities follow the same patterns as their existing
inline-start/inline-end counterparts and support all standard modifiers
(arbitrary values, negative values, opacity modifiers, etc.).

## Changes

1. **utilities.ts**: Added new utility definitions for all
block-start/block-end properties
2. **property-order.ts**: Updated CSS property ordering to include the
new logical properties in the correct cascade order
3. **utilities.test.ts**: Added comprehensive test cases for all new
utilities
4. **utilities.test.ts.snap**: Updated snapshots showing generated CSS
for the new utilities

## Test plan

All changes are covered by existing test infrastructure:
- Unit tests verify correct CSS generation for each utility variant
- Snapshot tests validate the complete output for border utilities
- Invalid modifier combinations are tested to ensure proper error
handling
- Tests cover standard values, arbitrary values, negative values, and
opacity modifiers

https://claude.ai/code/session_01V89HxEtppGVvMtuPbYrayM

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-30 14:37:51 +01:00
Robin Malfait
566eea4ff9
Improve walk performance (#19614)
This PR improves the walk performance by a little bit, but also reduces
memory usage. It's a tiny change and all the APIs remain the same. Not
the most important PR, but we had this idea on the backburner so we
decided to just implement it.

Thanks @thecrypticace!

## Test plan

- All the existing tests should pass.

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2026-01-29 15:49:31 +01:00
Tim Neutkens
e3e85b364f
[tailwindcss-vite] Add Register dependency messages logline (#19611)
The PostCSS implementation has a debug logline for registering
dependencies but this is missing for @tailwindcss/vite.

<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

Adds the "Register dependency messages" instrumentation span to the Vite
plugin's dependency registration code, matching the existing
implementation in @tailwindcss/postcss.

This ensures consistent debug instrumentation across both bundler
integrations when DEBUG is enabled.

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2026-01-29 14:17:50 +00:00
Tim Neutkens
bccf4bbfbd
Add @tailwindcss/webpack loader for Tailwind CSS v4 (#19610)
## Summary

This PR adds a new `@tailwindcss/webpack` package that provides a
dedicated webpack loader for Tailwind CSS v4. This loader works with
both standard webpack and Turbopack's webpack loader compatibility
layer.

### Why a dedicated loader?

The current webpack integration uses `postcss-loader` +
`@tailwindcss/postcss`. While this works, a dedicated loader:

- **Eliminates PostCSS as a middleman** - works directly with CSS
strings (no AST conversions)
- **Simpler and more efficient** - follows the same pattern as
`@tailwindcss/vite`
- **Better for Turbopack** - gives direct control over dependency
reporting via webpack's loader API

### How it works

The loader mirrors the Vite plugin's approach:

1. Uses `compile()` from `@tailwindcss/node` to parse CSS and resolve
`@apply` directives
2. Uses `Scanner` from `@tailwindcss/oxide` to scan content files for
utility candidates
3. Reports dependencies via `this.addDependency()` and
`this.addContextDependency()`
4. Optionally optimizes output with Lightning CSS

### Usage

```javascript
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /.css$/i,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          '@tailwindcss/webpack',  // No PostCSS needed!
        ],
      },
    ],
  },
}
```

### Options

- `base` - The base directory to scan for class candidates (defaults to
`process.cwd()`)
- `optimize` - Whether to optimize/minify the output CSS (defaults to
`true` in production)

### Files added

- `packages/@tailwindcss-webpack/` - New package
  - `src/index.ts` - Main loader implementation
  - `src/index.cts` - CommonJS entry point for webpack compatibility
  - `package.json`, `tsconfig.json`, `tsup.config.ts`, `README.md`
- `integrations/webpack/loader.test.ts` - Integration tests
- `integrations/utils.ts` - Added webpack override for transitive
dependencies

### Test plan

- [x] Build test - verifies basic compilation
- [x] Watch test - verifies HMR when adding new Tailwind classes
- [x] `@apply` test - verifies `@apply` directives work correctly
- [x] Optimization test - verifies minification works

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-01-29 14:16:31 +00:00
Robin Malfait
79711678fa
workaround: increase some CI timeouts 2026-01-06 20:58:00 +01:00
Robin Malfait
b8454b0b3f
Revert "temporarily disable discord notifications"
This reverts commit 7d78fda879.
2026-01-06 16:40:50 +01:00
Robin Malfait
22c0618583
Revert "re-run integration tests in CI if they fail"
This reverts commit efd75c1abb.
2026-01-06 16:23:56 +01:00
Robin Malfait
4b507b0a23
Revert "retry integration tests 3 times before actually failing"
This reverts commit ffcb96a523.
2026-01-06 16:23:46 +01:00
Robin Malfait
efd75c1abb
re-run integration tests in CI if they fail
This prevents the CI job from having to restart the entire job.
2026-01-06 16:17:55 +01:00
Robin Malfait
309d7f6e8a
bump nuxt dependency in integration tests 2026-01-06 16:17:55 +01:00
Robin Malfait
ffcb96a523
retry integration tests 3 times before actually failing
This is not ideal, but it prevents some manual work when CI is a bit too
flaky on the integration tests. This also prevents the CI job from
re-running from scratch which should speed up the process as well.
2026-01-06 16:17:54 +01:00
Robin Malfait
197d21c438
remove .npmrc with auto-install-peers=true
This was necessary in previous versions of Node, but now that we use
Node 24 in CI you get the following warning:

> npm warn Unknown env config "auto-install-peers". This will stop working in the next major version of npm.

So... let's get rid of it.
2026-01-06 15:28:25 +01:00
Robin Malfait
80e35e8f38
pin @napi-rs/cli
Newer versions of `@napi-rs/cli` (I noticed it in 3.5.1) rely on `@inquirer/core` for the CLI.
This version requires a modern Node version because it relies on `import
{ styleText } from 'node:util';` which is not supported on older Node
versions (<20).

The issue now is that we rely on a Docker image on ghcr
(ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine) which ships with Node
v18 which is too old and would crash when trying to build on CI.

Once ghcr is updated with a newer embedded Node version, we can bump
`@napi-rs/cli` again.

But for now, it's pinned.
2026-01-06 15:19:59 +01:00
Robin Malfait
60c6255fb8
use older version of @napi-rs/cli 2026-01-06 15:12:32 +01:00
Robin Malfait
ae130776ab
Revert "pin musl-related docker images"
This reverts commit 26515af2d5.
2026-01-06 15:11:38 +01:00
Robin Malfait
26515af2d5
pin musl-related docker images 2026-01-06 15:07:42 +01:00
Robin Malfait
7d78fda879
temporarily disable discord notifications 2026-01-06 14:37:46 +01:00
Robin Malfait
c6f0c6b318
bump required engine to Node 20 2026-01-06 13:57:52 +01:00
Robin Malfait
6313cf6770
bump to Node 24 in CI
All bumped `actions/*` for GitHub Workflows require Node 24 right now.
We were relying on Node 20, but that's in maintenance mode until April.

Node 24 is the current Active LTS version.
2026-01-06 13:48:17 +01:00
Robin Malfait
bde403e013
bump actions/github-script to v8 2026-01-06 13:46:44 +01:00
Robin Malfait
562acdac54
bump actions/cache to v5 2026-01-06 13:45:57 +01:00
Robin Malfait
d214c30161
bump actions/download-artifact to v6 2026-01-06 13:44:23 +01:00
Robin Malfait
bd7fe7abc8
bump actions/upload-artifact to v6 2026-01-06 13:43:59 +01:00
Robin Malfait
4f76b47f40
bump actions/checkout to v6 2026-01-06 13:41:55 +01:00
Robin Malfait
7107c04713
bump actions/setup-node to v6 2026-01-06 13:39:44 +01:00
Robin Malfait
4719dd9848
Revert "run release workflows against newer Node version"
This reverts commit 1f9ac73daf.
2026-01-06 13:37:35 +01:00
Robin Malfait
1f9ac73daf
run release workflows against newer Node version
When using NAPI to build for release purposes, it relies on `inquirer`,
which relies on `@inquirer/core` which relies on a modern Node version
to support `import { styleText } from 'node:util'`

We want to still make sure that CI / Integration Tests pass on Node v20
(which we currently do), so just bumping the prepare-release.yml,
release-insiders.yml and release.yml workflows.

If this isn't enough, I'll properly drop the `@napi-rs/cli` version or
force an `inquirer`/`@inquirer/core` version. Let's try this first.
2026-01-06 13:25:14 +01:00
Robin Malfait
f82ac39ff2
Improve @utility name validation (#19524)
This PR improves the validation of allowed `@utility …` names.

Each `@utility` name should be a valid Tailwind CSS class, so new
syntaxes should not be allowed, e.g. `foo/bar/baz` would be invalid.

We already enforce this behavior but not consistently. The Oxide scanner
that scans all your source files for potential Tailwind CSS classes does
enforce all of these rules already. So if you used `@utility foo/bar/baz
{}`, the Oxide scanner would not pick up `foo/bar/baz` as a valid class
name, so for that reason it's not a breaking change.

Where we didn't enforce it is in places where you use the
development-only CDN or Tailwind Play. That's because those environments
don't use the Oxide at all, and get the classes from the DOM directly
and pass it to Tailwind's compiler.

This PR moves some of these validation rules into Tailwind's core when
defining custom `@utility` utilities.

Fixes: #19505

### Test plan

1. Existing tests still pass
2. Added a regression test for the linked issue
3. Added new tests with valid / invalid `@utility` names

I also confirmed with Oxide to know which classes were actually valid
and which ones are invalid.

Given this input CSS:
```css
@utility foo { color: red }
@utility foo_ { color: red }     /* This one looks invalid to me, but it works today */
                                 /* and I don't want to introduce unnecessary breaking changes. */
@utility foo-1.5 { color: red }
@utility foo-123 { color: red }
@utility -foo { color: red }
@utility foo-bar { color: red }
@utility foo_bar { color: red }
@utility foo-50% { color: red }
@utility foo-1/2 { color: red }
```

And this HTML:
```html
<!-- Extracted: -->
<div class="foo foo_ foo-123 -foo foo-bar foo_bar foo-50% foo-1/2 foo-1.5"></div>

<!-- Not Extracted: -->
<div class="Foo -Foo foo-1/ foo- foo-p% foo-1..5 foo.bar foo..bar "></div>
```

Then all classes in the `Extracted` section are found. One funny thing
in the not extracted section is that the `bar` in `foo.bar` and
`foo..bar` is also extracted. Feels like a potential bug, but out of
scope for this PR.

<img width="766" height="164" alt="image"
src="https://github.com/user-attachments/assets/fafbaa35-2730-4f61-9b15-6690b02ec686"
/>
2026-01-06 12:54:43 +01:00
depfu[bot]
dc432aaa18
Update @napi-rs/wasm-runtime 1.1.0 → 1.1.1 (patch) (#19527)
<!--depfu-start-->
> 👉 **This PR is queued up to get rebased by Depfu**
<!--depfu-end-->





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

### What changed?




#### ✳️ @​napi-rs/wasm-runtime (1.1.0 → 1.1.1) ·
[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-01-06 11:49:41 +00:00
Robin Malfait
25f7ccff80
Fix class extraction for Rails' strict locals (#19525)
Fixes: #19481

This PR improves the Ruby extractor to better handle strict locals. We
recently introduced skipping comments in the Ruby extractor (PR #19243
for #19239) by ignoring comments that start with `#` until the end of
the line.

Strict locals are implemented like this:
```ruby
<%# locals: (css: "text-amber-600") %>
```

Notice the `#` after the `<%`, we considered this a comment and ignored
it.

This PR changes that behavior slightly where we skip comments that are
preceded by `%`. This means that `<%# anything here _will_ be scanned
%>`. This should solve the strict locals case, and normal comments will
still be skipped.

We can be more strict in the future if needed, but I think that this
should be a good solution for both scenarios.

### Test plan

1. Added a test to ensure we extract candidates in strict locals
2. Added a regression test for issue #19239 where we introduced skipping
comments in the Ruby extractor
3. Other existing tests are still passing

We can also verify the extracted candidates:
(it's subtle, but you can see that the class is being extracted now)

<img width="1187" height="1376" alt="image"
src="https://github.com/user-attachments/assets/74bbfd79-9db4-4a5b-bd8d-25f1565c6bfd"
/>
2026-01-06 12:46:14 +01:00
depfu[bot]
d5beb9534e
Update @napi-rs/cli 3.4.1 → 3.5.1 (minor) (#19528)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/cli (3.4.1 → 3.5.1) ·
[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-01-06 12:44:27 +01:00
depfu[bot]
c07d4a749d
Update turbo 2.6.3 → 2.7.2 (minor) (#19509)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ turbo (2.6.3 → 2.7.2) ·
[Repo](https://github.com/turborepo/turbo)





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-01-05 07:46:49 -05:00
Akash Agarwal
9720692eda
Fix typo in comment (#19493) 2025-12-27 11:56:29 -05:00
depfu[bot]
fbc358cec1
Update prettier-plugin-embed 0.5.0 → 0.5.1 (minor) (#19492)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ prettier-plugin-embed (0.5.0 → 0.5.1) ·
[Repo](https://github.com/Sec-ant/prettier-plugin-embed) ·
[Changelog](https://github.com/Sec-ant/prettier-plugin-embed/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/Sec-ant/prettier-plugin-embed/releases/tag/v0.5.1">0.5.1</a></h4>

<blockquote><h3 dir="auto">Patch Changes</h3>
<ul dir="auto">
<li>
<a
href="0c1b3c457c"><tt>0c1b3c4</tt></a>:
Fix indentation issues in sql, css and markdown embedded languages.</li>
<li>
<a
href="0c1b3c457c"><tt>0c1b3c4</tt></a>:
Fix embedded xml and prisma formatting behavior.</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="488a11218e...41d171dfd7">See
the full diff on Github</a>. The new version differs by 3 commits:</p>
<ul>
<li><a
href="41d171dfd7"><code>chore(release):
v0.5.1 (#158)</code></a></li>
<li><a
href="80173f4eb7"><code>fix(ci):
test workflow</code></a></li>
<li><a
href="0c1b3c457c"><code>fix:
refactor embedded options and embedder logic (#157)</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>
2025-12-26 12:36:51 -05:00
depfu[bot]
fba35d01bb
Update @types/bun 1.3.3 → 1.3.5 (patch) (#19491)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/bun (1.3.3 → 1.3.5) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-12-25 18:22:13 -05:00
depfu[bot]
e8a5a7f092
Update @tailwindcss/forms 0.5.10 → 0.5.11 (minor) (#19490)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​tailwindcss/forms (0.5.10 → 0.5.11) ·
[Repo](https://github.com/tailwindlabs/tailwindcss-forms) ·
[Changelog](https://github.com/tailwindlabs/tailwindcss-forms/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/tailwindlabs/tailwindcss-forms/releases/tag/v0.5.11">0.5.11</a></h4>

<blockquote><h3 dir="auto">Fixed</h3>
<ul dir="auto">
<li>Limit attribute rules to input and select elements (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-forms/pull/159">#159</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="071d1c03fd...e1b609d579">See
the full diff on Github</a>. The new version differs by 3 commits:</p>
<ul>
<li><a
href="e1b609d579"><code>0.5.11</code></a></li>
<li><a
href="67ff8ea3dc"><code>Limit
attribute rules to input and select elements (#159)</code></a></li>
<li><a
href="fc3f7e6bce"><code>docs:
update installation guide to add tailwind css v4 instructions while
keeping v3 details (#180)</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>
2025-12-24 17:20:21 -05:00
depfu[bot]
c804592719
Update bun 1.3.3 → 1.3.5 (patch) (#19489)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ bun (1.3.3 → 1.3.5) · [Repo](https://github.com/oven-sh/bun)





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>
2025-12-24 17:20:02 -05:00
depfu[bot]
8d5e955058
Update dedent 1.7.0 → 1.7.1 (patch) (#19484)
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?




#### ✳️ dedent (1.7.0 → 1.7.1) · [Repo](https://github.com/dmnd/dedent)
· [Changelog](https://github.com/dmnd/dedent/blob/main/CHANGELOG.md)
















---
![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>
2025-12-24 10:31:02 -05:00
depfu[bot]
1628713453
Update autoprefixer 10.4.22 → 10.4.23 (patch) (#19473)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ autoprefixer (10.4.22 → 10.4.23) ·
[Repo](https://github.com/postcss/autoprefixer) ·
[Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/autoprefixer/releases/tag/10.4.23">10.4.23</a></h4>

<blockquote><ul dir="auto">
<li>Reduced dependencies (by <a
href="https://bounce.depfu.com/github.com/hyperz111">@hyperz111</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="73dc62c779...212ba3c189">See
the full diff on Github</a>. The new version differs by 3 commits:</p>
<ul>
<li><a
href="212ba3c189"><code>Release
10.4.23 version</code></a></li>
<li><a
href="7f62fb6c12"><code>Update
dependencies</code></a></li>
<li><a
href="c455bb1bed"><code>chore:
inline and simplify `normalize-range` (#1539)</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>
2025-12-22 22:59:07 -05:00
xibeiyoumian
d979daacc6
chore: fix some typos in comments (#19475)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

fix some typos in comments

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

Signed-off-by: xibeiyoumian <xibeiyoumian@outlook.com>
2025-12-22 13:09:19 +00:00
depfu[bot]
3c06c55450
Update eslint 9.39.1 → 9.39.2 (patch) (#19469)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.39.1 → 9.39.2) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.39.2">9.39.2</a></h4>

<blockquote><h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="5705833194"><code
class="notranslate">5705833</code></a> fix: warn when <code
class="notranslate">eslint-env</code> configuration comments are found
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20381">#20381</a>)
(sethamus)</li>
</ul>
<h2 dir="auto">Build Related</h2>
<ul dir="auto">
<li>
<a
href="506f1549a6"><code
class="notranslate">506f154</code></a> build: add .scss files entry to
knip (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20391">#20391</a>)
(Milos Djermanovic)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="7ca0af7f9f"><code
class="notranslate">7ca0af7</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.39.2</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20394">#20394</a>)
(Francesco Trotta)</li>
<li>
<a
href="c43ce24ff0"><code
class="notranslate">c43ce24</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="4c9858e47b"><code
class="notranslate">4c9858e</code></a> ci: add <code
class="notranslate">v9.x-dev</code> branch (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20382">#20382</a>)
(Milos Djermanovic)</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="e2772811a8...9278324aa0">See
the full diff on Github</a>. The new version differs by 7 commits:</p>
<ul>
<li><a
href="9278324aa0"><code>9.39.2</code></a></li>
<li><a
href="542266ad3c"><code>Build:
changelog update for 9.39.2</code></a></li>
<li><a
href="7ca0af7f9f"><code>chore:
upgrade to `@eslint/js@9.39.2` (#20394)</code></a></li>
<li><a
href="c43ce24ff0"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="5705833194"><code>fix:
warn when `eslint-env` configuration comments are found
(#20381)</code></a></li>
<li><a
href="506f1549a6"><code>build:
add .scss files entry to knip (#20391)</code></a></li>
<li><a
href="4c9858e47b"><code>ci:
add `v9.x-dev` branch (#20382)</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>
2025-12-21 10:02:59 -05:00
depfu[bot]
c7036383a2
Update all of nextjs 16.0.10 → 16.1.0 (minor) (#19468)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint-config-next (16.0.10 → 16.1.0)





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



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



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

<blockquote><em>More info than we can show here.</em></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="581dee67e2...34916762cd">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
2025-12-19 16:20:03 -05:00
Jordan Pittman
543da3f4e5
Detect utilities when containing capital letters followed by numbers (#19465)
Fixes #19463
2025-12-19 05:45:15 -05:00
depfu[bot]
dbd26b90e7
Update all of react 19.2.1 → 19.2.3 (patch) (#19464)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ react (19.2.1 → 19.2.3) ·
[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.3">19.2.3</a></h4>

<blockquote><h2 dir="auto">React Server Components</h2>
<ul dir="auto">
<li>Add extra loop protection to React Server Functions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35351">#35351</a>)</li>
</ul></blockquote>
<h4><a
href="https://github.com/facebook/react/releases/tag/v19.2.2">19.2.2</a></h4>

<blockquote><h2 dir="auto">React Server Components</h2>
<ul dir="auto">
<li>Move <code
class="notranslate">react-server-dom-webpack/*.unbundled</code> to
private <code class="notranslate">react-server-dom-unbundled</code> (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35290">#35290</a>)</li>
<li>Patch Promise cycles and toString on Server Functions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>,
<a
href="https://bounce.depfu.com/github.com/unstubbable">@unstubbable</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35289">#35289</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35345">#35345</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="053df4e856...612e371fb2">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="612e371fb2"><code>Version
19.2.3</code></a></li>
<li><a
href="fc5c53c396"><code>Add
extra protection</code></a></li>
<li><a
href="b910fc15e3"><code>Version
19.2.2</code></a></li>
<li><a
href="dd0519822a"><code>Patch
Promise cycles and toString on Server Functions</code></a></li>
<li><a
href="0eeded69c1"><code>Run
CI for backport releases (#35313)</code></a></li>
<li><a
href="a4d338a6c6"><code>[test]
Cleanup stack assertions in tests mixing React Server and Client
(#35316)</code></a></li>
</ul>
</details>




#### ✳️ react-dom (19.2.1 → 19.2.3) ·
[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.3">19.2.3</a></h4>

<blockquote><h2 dir="auto">React Server Components</h2>
<ul dir="auto">
<li>Add extra loop protection to React Server Functions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35351">#35351</a>)</li>
</ul></blockquote>
<h4><a
href="https://github.com/facebook/react/releases/tag/v19.2.2">19.2.2</a></h4>

<blockquote><h2 dir="auto">React Server Components</h2>
<ul dir="auto">
<li>Move <code
class="notranslate">react-server-dom-webpack/*.unbundled</code> to
private <code class="notranslate">react-server-dom-unbundled</code> (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35290">#35290</a>)</li>
<li>Patch Promise cycles and toString on Server Functions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>,
<a
href="https://bounce.depfu.com/github.com/unstubbable">@unstubbable</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35289">#35289</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/35345">#35345</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="053df4e856...612e371fb2">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="612e371fb2"><code>Version
19.2.3</code></a></li>
<li><a
href="fc5c53c396"><code>Add
extra protection</code></a></li>
<li><a
href="b910fc15e3"><code>Version
19.2.2</code></a></li>
<li><a
href="dd0519822a"><code>Patch
Promise cycles and toString on Server Functions</code></a></li>
<li><a
href="0eeded69c1"><code>Run
CI for backport releases (#35313)</code></a></li>
<li><a
href="a4d338a6c6"><code>[test]
Cleanup stack assertions in tests mixing React Server and Client
(#35316)</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>
2025-12-18 20:28:49 -05:00
depfu[bot]
d6ad8dddcf
Update enhanced-resolve 5.18.3 → 5.18.4 (patch) (#19462)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ enhanced-resolve (5.18.3 → 5.18.4) ·
[Repo](https://github.com/webpack/enhanced-resolve)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/webpack/enhanced-resolve/releases/tag/v5.18.4">5.18.4</a></h4>

<blockquote><h3 dir="auto">Fixes</h3>
<ul dir="auto">
<li>Allow subpath imports that start with <code
class="notranslate">#/</code>
</li>
<li>Handle <code class="notranslate">file:</code> schema</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="52b61d0f03...e8a6ac9fdd">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="e8a6ac9fdd"><code>chore(release):
5.18.4</code></a></li>
<li><a
href="b953768580"><code>fix:
support `file:` schema (#467)</code></a></li>
<li><a
href="6ff935b236"><code>fix:
allow subpath imports that start with `#/` (#468)</code></a></li>
<li><a
href="79e2961c60"><code>fix(AliasPlugin):
correct wildcard replacement in multi-target aliases
(#465)</code></a></li>
<li><a
href="b8af8130f0"><code>fix:
fix always-false condition (#464)</code></a></li>
<li><a
href="f1bc1c2b0b"><code>fix:
note the conditions under which the request couldn&#39;t be found
(#460)</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>
2025-12-18 15:03:51 -05:00
Jordan Pittman
219e019b24
CLI: Emit comment when source maps are saved to files (#19447)
See
https://github.com/tailwindlabs/tailwindcss/issues/19362#issuecomment-3654376948
2025-12-18 15:02:27 -05:00
Justin Wong
7fcdd84e56
Allow whitespace around @source inline() arg (#19461)
## Summary

Inspired by #19460, relaxes whitespace syntax around `@source
inline(…):`

### Before

```css
/* ❌ Error: `@source` paths must be quoted. */
@source inline( "underline" );
@source inline(
  "underline"
);
```

### After

```css
/* ✅ Generates the class names as normal. */
@source inline( "underline" );
@source inline(
  "underline"
);
```

## Test plan

Added tests to `packages/tailwindcss/src/index.test.ts`.
2025-12-18 10:51:00 -05:00
Jordan Pittman
1fbdd51a8d Update test 2025-12-17 22:27:50 -05:00
Rasso Hilber
72dea0cfea
Clarify the replaceAlpha function comment (#19457)
## Summary

Updated the comment of the `replaceAlpha` function to clarify the
function's purpose.

## Test plan

n/a
2025-12-17 22:15:42 -05:00
Rasso Hilber
dd1ca3c709
Do not wrap color-mix in a @supports rule if one already exists (#19450)
Related issue: #19445 

## Summary

Fixes an issue where tailwindcss wraps `color-mix` inside a `@supports`
block even if the original code already checks for support.

Uncompiled Code:

```css
@utility foo {
  @supports (color: color-mix(in lab, red, red)) {
    background: color-mix(in lab, var(--color-1), var(--color-2));
  }
}
```

### Compiled code: Current behavior

https://play.tailwindcss.com/KSvR7wefdh?file=css

```css
@layer utilities {
  .foo {
    @supports (color: color-mix(in lab, red, red)) {
      background: var(--color-1);
      @supports (color: color-mix(in lab, red, red)) {
        background: color-mix(in lab, var(--color-1), var(--color-2));
      }
    }
}
```

### Compiled code: Fixed behavior

```css
@layer utilities {
  @supports (color: color-mix(in lab, red, red)) {
    .foo {
      background: color-mix(in lab, var(--color-1), var(--color-2));
    }
}
```

## Test plan

I have tested this by writing a new test that was at first failing. Now
it passes.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-12-16 16:57:36 -05:00
depfu[bot]
73b5df620c
Update @types/react 19.2.6 → 19.2.7 (patch) (#19448)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.2.2 → 19.2.7) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-12-15 23:33:16 -05:00
depfu[bot]
52e91db52c
Update @vitejs/plugin-react 5.1.1 → 5.1.2 (patch) (#19440)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (5.1.1 → 5.1.2) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
















---
![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>
2025-12-15 09:35:08 -05:00
depfu[bot]
cb3703fc07 Update all of nextjs to version 16.0.10 2025-12-12 14:52:15 +00:00
Jordan Pittman
dd9f656274
Fix failing postcss integration test (#19436)
This is what's causing the postcss test in #19434 to fail wanted to test
this in isolation without the version bumps
2025-12-12 09:38:08 -05:00
depfu[bot]
8198819943
Update turbo 2.6.1 → 2.6.3 (patch) (#19432)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ turbo (2.6.1 → 2.6.3) ·
[Repo](https://github.com/turborepo/turbo)





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>
2025-12-11 13:08:24 -05:00
Jordan Pittman
6518ea7f80 Update changelog 2025-12-11 11:31:02 -05:00
Jordan Pittman
6db2a6d637 Update changelog 2025-12-11 11:03:32 -05:00
Jordan Pittman
9b32f7cb2f
Release v4.1.18 (#19431) 2025-12-11 10:55:25 -05:00
Jordan Pittman
5065de4f9e Revert "Update @napi-rs/cli to version 3.5.0"
This reverts commit 3a1df5a283.
2025-12-10 11:26:51 -05:00
depfu[bot]
0a40849270 Update all of react to version 19.2.1 2025-12-10 15:53:21 +00:00
depfu[bot]
3a1df5a283 Update @napi-rs/cli to version 3.5.0 2025-12-09 10:54:19 +00:00
depfu[bot]
90abc94052
Update @napi-rs/wasm-runtime 1.0.7 → 1.1.0 (minor) (#19424)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/wasm-runtime (1.0.7 → 1.1.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>
2025-12-09 05:36:53 -05:00
Jordan Pittman
164194de06
Don’t try reading from pipes or special file descriptors (#19421)
Fixes #19420
2025-12-08 17:04:51 -05:00
Jordan Pittman
cdc851d3f8
Don't inline all env vars in Standalone CLI (#19391)
Fixes #19389

We inlined env vars in the Standalone CLI because we use some custom
patches + env vars to ensure that only the appropriate `glibc` / `musl`
binaries are included for Lightning CSS and Parcel Watcher for Linux
builds.

The build happens to run on a macOS Github CI machine though so
`NODE_PATH` was getting inlined as the string:
```
/Users/runner/work/tailwindcss/tailwindcss/node_modules/.pnpm/bun@1.3.0/node_modules/bun/bin/node_modules
```

I don't think there's a reason for `NODE_PATH` to work on the Standalone
CLI (and it didn't work because of the above bug *anyway*) so I've done
a few things here:
1. The build setup now uses `Bun.build(…)` which now supports compiling
binaries. This speeds up the build process a bit.
2. We're no longer inlining all env vars. We selectively inline only a
few using `define`.
3. I've explicitly disabled the extra `NODE_PATH` support in
`@tailwindcss/node` when building with the Standalone CLI.
4. The `__tw_readFile` hack is now gone. Async FS APIs were not
originally able to read embedded files but that changed in Bun v1.2.3
making the hack unnecessary.
5. A few more env vars are now inlined + a plugin to simplify the Oxide
loading code when bundled.
6. A plugin + env vars prevents bundling WASI build as it's not
necessary for the Standalone CLI.

I want to find a way to get rid of `__tw_resolve` and `__tw_load` but
don't want to change too much in this PR so I haven't looked into it
yet.
2025-12-08 11:23:20 -05:00
Jordan Pittman
820d90797c
Expose candidatesToAst to the language server (#19405)
This will be used to improve performance and potentially enable future
features that require generated CSS source locations.

Note: This is still 100% internal API. You can only access this via
`__unstable__loadDesignSystem` for a reason. We may chance the structure
of the arguments and/or return values as needed.
2025-12-08 10:54:42 -05:00
Jordan Pittman
478e959097
Don’t emit color-mix fallback rules inside @keyframes (#19419)
Fixes #19417
2025-12-08 09:40:16 -05:00
Adam Royle
563a016f96
Only use the last value when parsing duplicate cli arguments (#19416)
## Summary

When parsing cli args, 'mri' returns an array for duplicate args, which
causes unexpected results. All current arguments assume single values
only. Tailwind v3 works the same way.

## Test plan

Updated tests.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-12-07 12:26:22 -05:00
depfu[bot]
1ee7bb978d Update @types/bun to version 1.3.3 2025-12-06 13:42:20 +00:00
depfu[bot]
f97d094813
Update bun 1.3.2 → 1.3.3 (patch) (#19413)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ bun (1.3.2 → 1.3.3) · [Repo](https://github.com/oven-sh/bun)





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>
2025-12-06 08:29:17 -05:00
depfu[bot]
b4f3d177fc
Update postcss-selector-parser 7.1.0 → 7.1.1 (patch) (#19408)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ postcss-selector-parser (7.1.0 → 7.1.1) ·
[Repo](https://github.com/postcss/postcss-selector-parser) ·
[Changelog](https://github.com/postcss/postcss-selector-parser/blob/master/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/postcss-selector-parser/releases/tag/v7.1.1">7.1.1</a></h4>

<blockquote><h1 dir="auto">7.1.1</h1>
<ul dir="auto">
<li>perf: replace startsWith with strict equality (<a
href="https://bounce.depfu.com/github.com/postcss/postcss-selector-parser/pull/308">#308</a>)</li>
<li>fix(types): add walkUniversal declaration (<a
href="https://bounce.depfu.com/github.com/postcss/postcss-selector-parser/pull/311">#311</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="ea6cd9324b...cf6637ed6a">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="cf6637ed6a"><code>chore(release):
7.1.1</code></a></li>
<li><a
href="54cef58801"><code>fix(types):
add `walkUniversal` declaration (#311)</code></a></li>
<li><a
href="3da25fecba"><code>perf:
replace startsWith with strict equality (#308)</code></a></li>
<li><a
href="b647f7c70f"><code>ci:
test on Node.js 24 (#307)</code></a></li>
<li><a
href="337129d1e3"><code>chore:
replace abandoned pkg coveralls with coveralls-next</code></a></li>
<li><a
href="6343f29e08"><code>ci:
test on Node.js 22 (#301)</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>
2025-12-04 10:55:11 -05:00
Andrés
9c8cf8ac19
Fix formatting of path in README.md (#19407)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

quotation mark is not closed in README doc of the postcss preprocesor

## Test plan

not needed
2025-12-04 08:25:51 -05:00
depfu[bot]
a92fa97499 Update all of nextjs to version 16.0.7 2025-12-03 20:19:40 +00:00
Jordan Pittman
a5f4644507
Validate named values in candidate parser (#19397)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1506
2025-12-03 15:06:25 -05:00
Jordan Pittman
5f107e2822 Fix typo 2025-12-02 18:12:07 -05:00
Kirti Gautam
0d0d97d0cb
Fix invalid template tags in tests (#19400)
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-12-02 14:51:21 -05:00
depfu[bot]
7c69e83e81
Update @playwright/test 1.56.1 → 1.57.0 (minor) (#19399)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​playwright/test (1.56.1 → 1.57.0) ·
[Repo](https://github.com/Microsoft/playwright)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.57.0">1.57.0</a></h4>

<blockquote><h2 dir="auto">Speedboard</h2>
<p dir="auto">In HTML reporter, there's a new tab we call
"Speedboard":</p>
<a
href="https://private-user-images.githubusercontent.com/14912729/518585867-4ba117ea-ea94-4b6a-82b2-8bbd00dfe81c.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NjQxNjA4MDQsIm5iZiI6MTc2NDE2MDUwNCwicGF0aCI6Ii8xNDkxMjcyOS81MTg1ODU4NjctNGJhMTE3ZWEtZWE5NC00YjZhLTgyYjItOGJiZDAwZGZlODFjLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTExMjYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUxMTI2VDEyMzUwNFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTY4YjhlYjY1ZTA1NGU5MjU5NjU5MDQzNDdhZjMzYmEzYjRlMTg2ZGJlZTA1YTA5NWQ3ODY4MTEzNTdlNWUwNTUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.YQEEtVqnrOmR45G6zYUkuaOJL3k2DlbVb4cM0frrGQg"><img
width="600" alt="speedboard"
src="https://private-user-images.githubusercontent.com/14912729/518585867-4ba117ea-ea94-4b6a-82b2-8bbd00dfe81c.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NjQxNjA4MDQsIm5iZiI6MTc2NDE2MDUwNCwicGF0aCI6Ii8xNDkxMjcyOS81MTg1ODU4NjctNGJhMTE3ZWEtZWE5NC00YjZhLTgyYjItOGJiZDAwZGZlODFjLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTExMjYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUxMTI2VDEyMzUwNFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTY4YjhlYjY1ZTA1NGU5MjU5NjU5MDQzNDdhZjMzYmEzYjRlMTg2ZGJlZTA1YTA5NWQ3ODY4MTEzNTdlNWUwNTUmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.YQEEtVqnrOmR45G6zYUkuaOJL3k2DlbVb4cM0frrGQg"
style="max-width: 100%;"></a>
<p dir="auto">It shows you all your executed tests sorted by
slowness,<br>
and can help you understand where your test suite is taking longer than
expected.<br>
Take a look at yours - maybe you'll find some tests that are spending a
longer time waiting than they should!</p>
<h2 dir="auto">Chrome for Testing</h2>
<p dir="auto">Starting with this release, Playwright switches from
Chromium, to using <a
href="https://developer.chrome.com/blog/chrome-for-testing/">Chrome for
Testing</a> builds. Both headed and headless browsers are subject to
this. Your tests should still be passing after upgrading to Playwright
1.57.</p>
<p dir="auto">We're expecting no functional changes to come from this
switch. The biggest change is the new icon and title in your
toolbar.</p>
<a
href="https://private-user-images.githubusercontent.com/14912729/518586000-e9a5c4f2-9f35-4c27-9382-0f5eda377097.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NjQxNjA4MDQsIm5iZiI6MTc2NDE2MDUwNCwicGF0aCI6Ii8xNDkxMjcyOS81MTg1ODYwMDAtZTlhNWM0ZjItOWYzNS00YzI3LTkzODItMGY1ZWRhMzc3MDk3LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTExMjYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUxMTI2VDEyMzUwNFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWQ3YWFkMTAzM2I0YmQ1MDU4ODRlMDA0YzQ2NjQ3YjRlZjJkYTA4MmY5YTdlYzBmZmE1ZWI4NDk0NzQ2YTdiMGMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.IPnNJmILND-iF20WnvLwMZQZMteYB8WFNIPXNL-lofc"><img
width="500" alt="new and old logo"
src="https://private-user-images.githubusercontent.com/14912729/518586000-e9a5c4f2-9f35-4c27-9382-0f5eda377097.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NjQxNjA4MDQsIm5iZiI6MTc2NDE2MDUwNCwicGF0aCI6Ii8xNDkxMjcyOS81MTg1ODYwMDAtZTlhNWM0ZjItOWYzNS00YzI3LTkzODItMGY1ZWRhMzc3MDk3LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTExMjYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUxMTI2VDEyMzUwNFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWQ3YWFkMTAzM2I0YmQ1MDU4ODRlMDA0YzQ2NjQ3YjRlZjJkYTA4MmY5YTdlYzBmZmE1ZWI4NDk0NzQ2YTdiMGMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.IPnNJmILND-iF20WnvLwMZQZMteYB8WFNIPXNL-lofc"
style="max-width: 100%;"></a>
<p dir="auto">If you still see an unexpected behaviour change, please <a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/new">file
an issue</a>.</p>
<p dir="auto">On Arm64 Linux, Playwright continues to use Chromium.</p>
<h2 dir="auto">Waiting for webserver output</h2>
<p dir="auto"><a
href="https://playwright.dev/docs/api/class-testconfig#test-config-web-server">testConfig.webServer</a>
added a <code class="notranslate">wait</code> field. Pass a regular
expression, and Playwright will wait until the webserver logs match
it.</p>
<div class="highlight highlight-source-js" dir="auto"><pre
class="notranslate"><span class="pl-k">import</span> <span
class="pl-kos">{</span> <span class="pl-s1">defineConfig</span> <span
class="pl-kos">}</span> <span class="pl-k">from</span> <span
class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">default</span> <span
class="pl-en">defineConfig</span><span class="pl-kos">(</span><span
class="pl-kos">{</span>
  <span class="pl-c1">webServer</span>: <span class="pl-kos">{</span>
<span class="pl-c1">command</span>: <span class="pl-s">'npm run
start'</span><span class="pl-kos">,</span>
    <span class="pl-c1">wait</span>: <span class="pl-kos">{</span>
<span class="pl-c1">stdout</span>: <span class="pl-s">'/Listening on
port (?&lt;my_server_port&gt;\\d+)/'</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>
  <span class="pl-kos">}</span><span class="pl-kos">,</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span
class="pl-kos">;</span></pre></div>
<p dir="auto">If you include a named capture group into the expression,
then Playwright will provide the capture group contents via environment
variables:</p>
<div class="highlight highlight-source-js" dir="auto"><pre
class="notranslate"><span class="pl-k">import</span> <span
class="pl-kos">{</span> <span class="pl-s1">test</span><span
class="pl-kos">,</span> <span class="pl-s1">expect</span> <span
class="pl-kos">}</span> <span class="pl-k">from</span> <span
class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>

<span class="pl-s1">test</span><span class="pl-kos">.</span><span
class="pl-en">use</span><span class="pl-kos">(</span><span
class="pl-kos">{</span> <span class="pl-c1">baseUrl</span>: <span
class="pl-s">`http://localhost:<span class="pl-s1"><span
class="pl-kos">${</span><span class="pl-s1">process</span><span
class="pl-kos">.</span><span class="pl-c1">env</span><span
class="pl-kos">.</span><span class="pl-c1">MY_SERVER_PORT</span> <span
class="pl-c1">??</span> <span class="pl-c1">3000</span><span
class="pl-kos">}</span></span>`</span> <span
class="pl-kos">}</span><span class="pl-kos">)</span><span
class="pl-kos">;</span>

<span class="pl-en">test</span><span class="pl-kos">(</span><span
class="pl-s">'homepage'</span><span class="pl-kos">,</span> <span
class="pl-k">async</span> <span class="pl-kos">(</span><span
class="pl-kos">{</span> page <span class="pl-kos">}</span><span
class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span
class="pl-kos">{</span>
<span class="pl-k">await</span> <span class="pl-s1">page</span><span
class="pl-kos">.</span><span class="pl-en">goto</span><span
class="pl-kos">(</span><span class="pl-s">'/'</span><span
class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span
class="pl-kos">;</span></pre></div>
<p dir="auto">This is not just useful for capturing varying ports of dev
servers. You can also use it to wait for readiness of a service that
doesn't expose an HTTP readiness check, but instead prints a readiness
message to stdout or stderr.</p>
<h2 dir="auto">Breaking Change</h2>
<p dir="auto">After 3 years of being deprecated, we removed <code
class="notranslate">Page#accessibility</code> from our API. Please use
other libraries such as <a href="https://www.deque.com/axe/">Axe</a> if
you need to test page accessibility. See our Node.js <a
href="https://playwright.dev/docs/accessibility-testing">guide</a> for
integration with Axe.</p>
<h2 dir="auto">New APIs</h2>
<ul dir="auto">
<li>New property <a
href="https://playwright.dev/docs/api/class-testconfig#test-config-tag">testConfig.tag</a>
adds a tag to all tests in this run. This is useful when using <a
href="https://playwright.dev/docs/test-sharding#merging-reports-from-multiple-shards">merge-reports</a>.</li>
<li>
<a
href="https://playwright.dev/docs/api/class-worker#worker-event-console">worker.on('console')</a>
event is emitted when JavaScript within the worker calls one of console
API methods, e.g. console.log or console.dir. <a
href="https://playwright.dev/docs/api/class-worker#worker-wait-for-event">worker.waitForEvent()</a>
can be used to wait for it.</li>
<li>
<a
href="https://playwright.dev/docs/api/class-locator#locator-description">locator.description()</a>
returns locator description previously set with <a
href="https://playwright.dev/docs/api/class-locator#locator-describe">locator.describe()</a>,
and <code class="notranslate">Locator.toString()</code> now uses the
description when available.</li>
<li>New option <a
href="https://playwright.dev/docs/api/class-locator#locator-click-option-steps"><code
class="notranslate">steps</code></a> in <a
href="https://playwright.dev/docs/api/class-locator#locator-click">locator.click()</a>
and <a
href="https://playwright.dev/docs/api/class-locator#locator-drag-to">locator.dragTo()</a>
that configures the number of <code class="notranslate">mousemove</code>
events emitted while moving the mouse pointer to the target
element.</li>
<li>Network requests issued by <a
href="https://playwright.dev/docs/service-workers#network-events-and-routing">Service
Workers</a> are now reported and can be routed through the <a
href="https://playwright.dev/docs/api/class-browsercontext">BrowserContext</a>,
only in Chromium. You can opt out using the <code
class="notranslate">PLAYWRIGHT_DISABLE_SERVICE_WORKER_NETWORK</code>
environment variable.</li>
<li>Console messages from Service Workers are dispatched through <a
href="https://playwright.dev/docs/api/class-worker#worker-event-console">worker.on('console')</a>.
You can opt out of this using the <code
class="notranslate">PLAYWRIGHT_DISABLE_SERVICE_WORKER_CONSOLE</code>
environment variable.</li>
</ul>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 143.0.7499.4</li>
<li>Mozilla Firefox 142.0.1</li>
<li>WebKit 26.0</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="54c711571a...8058197258">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
2025-12-02 07:09:28 -05:00
Robin Malfait
229121dd14
Canonicalization: combine text-* and leading-* classes (#19396)
This PR improves the canonicalization when using `text-*` and
`leading-*` utilities together.

When using classes such as:
```html
<div class="text-sm leading-7"></div>
```

Then the canonical way of writing this is:
```html
<div class="text-sm/7"></div>
```

Similarly, if you already have a modifier applied, and add a new
line-height utility. It will also combine them into the canonical form:
```html
<div class="text-sm/6 leading-7"></div>
```
becomes:
```html
<div class="text-sm/7"></div>
```

This is because the final CSS output of `text-sm/6 leading-7` is:
```css
/*! tailwindcss v4.1.16 | MIT License | https://tailwindcss.com */
.text-sm\/6 {
  font-size: var(--text-sm, 0.875rem);
  line-height: calc(var(--spacing, 0.25rem) * 6);
}
.leading-7 {
  --tw-leading: calc(var(--spacing, 0.25rem) * 7);
  line-height: calc(var(--spacing, 0.25rem) * 7);
}
@property --tw-leading {
  syntax: "*";
  inherits: false;
}
```

Where the `line-height` of the `leading-7` class wins over the
`line-height` of the `text-sm/6` class.

### Implementation

#### On the fly pre-computation

Right now, we are not using any AST based transformations yet and
instead rely on a pre-computed list. However, with arbitrary values we
don't have pre-computed values for `text-sm/123` for example.

What we do instead is if we see a utility that sets `line-height` and
other utilities set `font-size` then we pre-compute those computations
on the fly.

We will prefer named font-sizes (such as `sm`, `lg`, etc). We will also
prefer bare values for line-height (such as `7`) over arbitrary values
(such as `[123px]`).

#### Canonicalization of the CSS AST

Another thing we had to do is to make sure that when multiple
declarations of the same property exist, that we only keep the last one.
In the real world, multiple declarations of the same value is typically
used for fallback values (e.g.: `background-color: #fff;
background-color: oklab(255 255 255 / 1);`).

But for our use case, I believe we can safely remove the earlier
declarations to make the most modern and thus the last declaration win.

#### Trying combinations based on `property` only

One small change we had to make is that we try combinations of utilities
based on property only instead of property _and_ value. This is
important for cases such as `text-sm/6 leading-7`. These 2 classes will
set a `lin-height` of `24px` and `28px` respectively so they will never
match.

However, once combined together, there will be 2 line-height values, and
the last one wins. The signature of `text-sm/6 leading-7` becomes:
```css
.x {
  font-size: 14px;          /* From text-sm/6 */
  line-height: 24px;        /* From text-sm/6 */
  line-height: 28px;        /* From leading-7 */
}
```

↓↓↓↓↓↓↓↓↓

```css
.x {
  font-size: 14px;          /* From text-sm/6 */
  line-height: 28px;        /* From leading-7 */
}
```

This now shows that just `text-sm/7` is the canonical form. Because it
produces the same final CSS output.


## Test plan

1. All existing tests pass
2. Added a bunch of new tests where we combine `text-*` and `leading-*`
utilities with named, bare and arbitrary values. Even with existing
modifiers on the text utilities.

<img width="1010" height="1099" alt="image"
src="https://github.com/user-attachments/assets/d2775692-a442-4604-8371-21dacf16ebfc"
/>
2025-12-01 16:01:24 +01:00
Jordan Pittman
243615e3f2
Handle backwards compatibility for content theme from JS configs (#19381)
Fixes #19343

This PR makes it so the `content-*` utilities read from the
`--content-*` theme namespace. This change is **purely for backwards
compatibility with Tailwind CSS v3**. It is recommended you use
arbitrary values with the `content-*` utility instead.
2025-11-29 11:19:37 -05:00
Jordan Pittman
764275143e
Improve compatibility with special default values in JS configs (#19348)
Fixes #19345

In v3 the `ringColor.DEFAULT` option was used as the default color for
ring utilities (when it was defined). This currently gets translated as
`--ring-color` but that doesn't work this way in v4. Instead it should
translate to `--default-ring-color` and *not* `--ring-color`.

I've also tweaked the upgrade tool to handle this properly as well.
2025-11-28 15:45:10 -05:00
Robin Malfait
af481175e7
remove unnecessary intermediate check 2025-11-26 12:34:42 +01:00
Robin Malfait
9e436f7751
Try to canonicalize any arbitrary utility to a bare value (#19379)
This PR adds an improvement to our canonicalization logic when dealing
with arbitrary values. When trying to canonicalize utilities, we make
use of the intellisense suggestions list where we typically use
multiples of the spacing scale.

This means that a value like `gap-[128px]` gets properly canonicalized
to `gap-32`. However, when you try a value that we typically don't
suggest such as `gap-[116px]` then it doesn't get canonicalized at all.

This PR fixes that by trying to use the spacing scale and convert `116px
/ 4px` and try the `gap-29` utility instead.

This is done by canonicalizing the incoming arbitrary value and the
spacing multipliers such that `--spacing: 0.25rem` and `--spacing: 4px`
both work as expected.

### Test plan

1. Added some tests with a spacing scale of `0.25rem` (which is the
default)
2. Added some tests with the same spacing scale in a different unit
`4px`
3. Added some tests with a different spacing scale `1px`

Also had to update 1 test that now gets canonicalized properly, e.g.:
`w-[124px]` → `w-31`.
2025-11-26 11:07:21 +00:00
Jordan Pittman
117433a100 Revert "Update globby 15.0.0 → 16.0.0 (major) (#19357)"
This reverts commit 28670a9b91.
2025-11-25 13:13:08 -05:00
Jordan Pittman
0e8f075ca2
Fix source map generation during when watching files on the CLI (#19373)
Fixes #19362

We were overwriting the source map with the "decoded" map returned by
the compiler but didn't wrap it in the helper intended to help inline vs
file maps. This resulted in two issues:
1. `undefined` being appended to the CSS file when using `--map`
2. `undefined` being passed to `writeFile(…)` when using `--map <file>`

This PR fixes both.
2025-11-25 12:22:57 -05:00
depfu[bot]
28670a9b91
Update globby 15.0.0 → 16.0.0 (major) (#19357)
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?




#### ✳️ globby (15.0.0 → 16.0.0) ·
[Repo](https://github.com/sindresorhus/globby)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/sindresorhus/globby/releases/tag/v16.0.0">16.0.0</a></h4>

<blockquote><h3 dir="auto">Breaking</h3>
<ul dir="auto">
<li>Respect parent .gitignore files when <code
class="notranslate">gitignore</code> option is enabled <a
href="b9a9a84c76"><tt>b9a9a84</tt></a>
<ul dir="auto">
<li>When <code class="notranslate">{gitignore: true}</code>, Globby now
searches for .gitignore files up to the Git repo root (if detected).
This matches Git's actual behavior where parent .gitignore patterns
apply to subdirectories. Previously, only .gitignore files within the
glob search path were considered. This change could break existing code
that relied on the previous, more limited behavior.</li>
</ul>
</li>
</ul>
<h3 dir="auto">Improvements</h3>
<ul dir="auto">
<li>Support negation-only patterns <a
href="1273541cca"><tt>1273541</tt></a>
</li>
<li>Respect custom fs implementation from options <a
href="c2eb272520"><tt>c2eb272</tt></a>
</li>
<li>Add some more options to <code
class="notranslate">isGitIgnored</code> functions <a
href="e794ff905a"><tt>e794ff9</tt></a>
</li>
<li>Improve error message for invalid <code
class="notranslate">cwd</code> option <a
href="7535e6d74c"><tt>7535e6d</tt></a>
</li>
</ul>
<h3 dir="auto">Fixes</h3>
<ul dir="auto">
<li>Fix <code class="notranslate">{gitignore: true}</code> performance
issue <a
href="fcb07c3907"><tt>fcb07c3</tt></a>
</li>
<li>Fix bundler compatibility <a
href="e6c72c9ff8"><tt>e6c72c9</tt></a>
</li>
<li>Fix parent directory patterns with <code
class="notranslate">gitignore</code> option <a
href="11dd2dfae3"><tt>11dd2df</tt></a>
</li>
<li>Fix ignore patterns with parent directory references <a
href="a58d117b0e"><tt>a58d117</tt></a>
</li>
<li>Fix types for <code class="notranslate">stats</code> option <a
href="c10f600c61"><tt>c10f600</tt></a>
</li>
</ul>
<hr>
<p dir="auto"><a
href="https://bounce.depfu.com/github.com/sindresorhus/globby/compare/v15.0.0...v16.0.0"><tt>v15.0.0...v16.0.0</tt></a></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="b65747ec17...cdbb6a2e66">See
the full diff on Github</a>. The new version differs by 22 commits:</p>
<ul>
<li><a
href="cdbb6a2e66"><code>16.0.0</code></a></li>
<li><a
href="e6c72c9ff8"><code>Fix
bundler compatibility</code></a></li>
<li><a
href="b9a9a84c76"><code>Respect
parent .gitignore files when `gitignore` option is
enabled</code></a></li>
<li><a
href="db9cb722a0"><code>Add
test for `suppressErrors` option with file patterns</code></a></li>
<li><a
href="69e0ee7bbe"><code>Add
test for ignore option with trailing slashes</code></a></li>
<li><a
href="11dd2dfae3"><code>Fix
parent directory patterns with `gitignore` option</code></a></li>
<li><a
href="e4d09180fb"><code>Add
tests for special characters in directory names</code></a></li>
<li><a
href="f92613260e"><code>Improve
path check in gitignore logic</code></a></li>
<li><a
href="7535e6d74c"><code>Improve
error message for invalid `cwd` option</code></a></li>
<li><a
href="c10f600c61"><code>Fix
types for `stats` option</code></a></li>
<li><a
href="a58d117b0e"><code>Fix
ignore patterns with parent directory references</code></a></li>
<li><a
href="40022f3927"><code>Improve
Windows path documentation</code></a></li>
<li><a
href="1273541cca"><code>Support
negation-only patterns</code></a></li>
<li><a
href="c2eb272520"><code>Respect
custom fs implementation from options</code></a></li>
<li><a
href="aa694e0847"><code>Improve
docs for `convertPathToPattern`</code></a></li>
<li><a
href="306a82ddc7"><code>Add
test for .npmignore in subdirectories</code></a></li>
<li><a
href="9370b3574e"><code>Clarify
`gitignore` priority in documentation</code></a></li>
<li><a
href="c743dad53b"><code>Add
test for nested gitignore with negation</code></a></li>
<li><a
href="e794ff905a"><code>Add
some more options to isGitIgnored functions</code></a></li>
<li><a
href="fcb07c3907"><code>Fix
`{gitignore: true}` performance issue</code></a></li>
<li><a
href="2dee4320b6"><code>Add
tests for some fast-glob limitations</code></a></li>
<li><a
href="6da24d3e2b"><code>Add
test for gitignore patterns starting with `./` or `../`</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>
2025-11-25 09:27:57 -05:00
depfu[bot]
a1f533a0e0 Update @emnapi/core to version 1.7.1 2025-11-21 14:57:54 +00:00
depfu[bot]
385edfb80d
Update emnapi 1.6.0 → 1.7.1 (minor) (#19351)
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?




#### ✳️ emnapi (1.6.0 → 1.7.1) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.7.1">1.7.1</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>move Node-API version detection by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/182">#182</a>
</li>
<li>feat: support SharedArrayBuffer in napi_create_dataview by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/183">#183</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.7.0...v1.7.1"><tt>v1.7.0...v1.7.1</tt></a></p></blockquote>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.7.0">1.7.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>feat: add napi_create_object_with_properties method by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/181">#181</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.6.0...v1.7.0"><tt>v1.6.0...v1.7.0</tt></a></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="86d68f88ec...f40577ad60">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="f40577ad60"><code>1.7.1</code></a></li>
<li><a
href="c95f961fbc"><code>feat:
support SharedArrayBuffer in napi_create_dataview (#183)</code></a></li>
<li><a
href="92964e3738"><code>move
Node-API version detection (#182)</code></a></li>
<li><a
href="11f4936f39"><code>1.7.0</code></a></li>
<li><a
href="44bd3c798d"><code>[Backport]
feat: add napi_create_object_with_properties method
(#181)</code></a></li>
<li><a
href="62ab842b64"><code>ci:
fix version retrieval</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>
2025-11-21 09:41:20 -05:00
depfu[bot]
251fe27658
Update @vitejs/plugin-react 5.1.0 → 5.1.1 (patch) (#19355)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (5.1.0 → 5.1.1) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)



<details>
<summary>Release Notes</summary>

<h4>5.1.1 (from changelog)</h4>
<blockquote><h3 dir="auto">Update code to support newer <code
class="notranslate">rolldown-vite</code> (<a
href="https://bounce.depfu.com/github.com/vitejs/vite-plugin-react/pull/976">#976</a>)</h3>
<p dir="auto"><code class="notranslate">rolldown-vite</code> will remove
<code class="notranslate">optimizeDeps.rollupOptions</code> in favor of
<code class="notranslate">optimizeDeps.rolldownOptions</code> soon. This
plugin now uses <code
class="notranslate">optimizeDeps.rolldownOptions</code> to support newer
<code class="notranslate">rolldown-vite</code>. Please update <code
class="notranslate">rolldown-vite</code> to the latest version if you
are using an older version.</p></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please
let us know.</a></em></p>
</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>
2025-11-21 09:41:12 -05:00
depfu[bot]
98d221f24b
Update @emnapi/runtime 1.6.0 → 1.7.1 (minor) (#19349)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/runtime (1.6.0 → 1.7.1) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.7.1">1.7.1</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>move Node-API version detection by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/182">#182</a>
</li>
<li>feat: support SharedArrayBuffer in napi_create_dataview by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/183">#183</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.7.0...v1.7.1"><tt>v1.7.0...v1.7.1</tt></a></p></blockquote>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.7.0">1.7.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>feat: add napi_create_object_with_properties method by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/181">#181</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.6.0...v1.7.0"><tt>v1.6.0...v1.7.0</tt></a></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="86d68f88ec...f40577ad60">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="f40577ad60"><code>1.7.1</code></a></li>
<li><a
href="c95f961fbc"><code>feat:
support SharedArrayBuffer in napi_create_dataview (#183)</code></a></li>
<li><a
href="92964e3738"><code>move
Node-API version detection (#182)</code></a></li>
<li><a
href="11f4936f39"><code>1.7.0</code></a></li>
<li><a
href="44bd3c798d"><code>[Backport]
feat: add napi_create_object_with_properties method
(#181)</code></a></li>
<li><a
href="62ab842b64"><code>ci:
fix version retrieval</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>
2025-11-21 09:40:51 -05:00
Robin Malfait
479b725cd3
Bump Vitest to v4 (#19216)
This PR bumps Vitest from v2 to v4. As far as I know we don't use any
Vitest specific features in our tests, but had to upgrade the
`vitest.workspace.ts` file to a `vitest.config.ts` file instead.

The only features we use are the typical `describe`, `it`, `test`, and
`expect` functions.

The only other part we use is `vi.spyOn` and `vi.fn` but those didn't
change in API either.

The test shards were removed to prevent errors. Not all suites have
enough files / tests to be broken up into 3 parts so Vitest now errors
when that happens.

### Test plan

1. All tests should pass in CI.
2. All integration tests should pass in CI.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-11-20 18:16:20 -05:00
Jordan Pittman
642b9b8576
Don’t unconditionally convert config keys to kebab-case (#19337)
Fixes #18114
Closes #18115

This PR changes JS config handling such that we always preserve casing
for theme keys (when possible).

Now there are *two* exceptions to this rule:

1. Top-level keys in the theme *do* get converted to kebab-case. As CSS
variables are not generated from these this shouldn't be a big issue.

All of our internal plugins look for kebab-case keys. So, for example,
take the path `backgroundColor.red.500`. This must translate to
`--background-color-red-500`. But if you had something like
`backgroundColor.lightBlue` it would be perfectly fine for that to
translate to `--background-color-lightBlue` internally (and thus the
utility be written as `bg-lightBlue`.

2. Tuple object keys are converted to kebab-case as well.

These keys are converted to "nested" key syntax internally and
typtically represent CSS property names.

For example:
```js
export default {
  theme: {
    fontSize: {
      xs: ["1.5rem", { lineHeight: "1.3" }]
    },
    
    fontFamily: {
      sans: ["Potato Mono", { fontVariationSettings: '"XHGT" 0.7' }]
    }
  }
}
```

The `lineHeight` key here must be converted to `line-height` because it
represents a CSS property name. The theme key that represents this value
is `--text-xs--line-height`. The same situation applies for the
`fontVariationSettings` where the theme key is
`--font-sans--font-variation-settings`.
2025-11-20 22:39:55 +00:00
Jordan Pittman
5a8e878319
Handle future and experimental config keys during upgrade (#19344)
Fixes #19342
2025-11-20 22:11:43 +00:00
depfu[bot]
68337df3db Update @types/bun to version 1.3.2 2025-11-20 22:08:27 +00:00
depfu[bot]
7c0eeaa3a6
Update all of nextjs 15.5.6 → 16.0.3 (major) (#19341)
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?




#### ✳️ eslint-config-next (15.5.6 → 16.0.3)





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



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



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

<blockquote><em>More info than we can show here.</em></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v16.0.2">16.0.2</a></h4>

<blockquote><em>More info than we can show here.</em></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v16.0.1">16.0.1</a></h4>

<blockquote><em>More info than we can show here.</em></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v16.0.0">16.0.0</a></h4>

<blockquote><em>More info than we can show here.</em></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="01014d476c...2eda33ce84">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-11-20 10:54:23 -05:00
depfu[bot]
172c663bd6
Update tsup 8.5.0 → 8.5.1 (patch) (#19338)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ tsup (8.5.0 → 8.5.1)





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>
2025-11-19 18:18:45 -05:00
depfu[bot]
3d899f73f8 Update @types/react-dom to version 19.2.3 2025-11-19 10:43:12 +00:00
depfu[bot]
5f243dbb0f
Update bun 1.3.0 → 1.3.2 (patch) (#19334) 2025-11-19 11:25:28 +01:00
Jordan Pittman
a7c968efb1
bump turbo (#19179)
The Bun build issue was caused by Turborepo passing through
`USERPROFILE` from the env. Probably need to file a bug with Bun:

```
Works:
env: USERPROFILE: undefined
CWD: `D:\a\tailwindcss\tailwindcss\packages\@tailwindcss-standalone`

Fails:
env: USERPROFILE: "C:\Users\runneradmin"
CWD: `D:\a\tailwindcss\tailwindcss\packages\@tailwindcss-standalone`
```
2025-11-18 15:14:24 -05:00
Jordan Pittman
e4b6c67bcb
Support Vite Environment API (#18970)
Fixes #18002

Very much a work in progress b/c I don't (yet) understand how the newer
APIs are intended to function.

- [x] Needs env specific tests that verify the environment API is being
used
2025-11-18 12:38:57 -05:00
depfu[bot]
c532955dd1
Update autoprefixer 10.4.21 → 10.4.22 (patch) (#19322)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ autoprefixer (10.4.21 → 10.4.22) ·
[Repo](https://github.com/postcss/autoprefixer) ·
[Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/autoprefixer/releases/tag/10.4.22">10.4.22</a></h4>

<blockquote><ul dir="auto">
<li>Fixed <code class="notranslate">stretch</code> prefixes on new Can I
Use database.</li>
<li>Updated <code class="notranslate">fraction.js</code>.</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="541295c0e6...73dc62c779">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="73dc62c779"><code>Release
10.4.22 version</code></a></li>
<li><a
href="9973c59f48"><code>Lock
CI action versions</code></a></li>
<li><a
href="4b4feca71a"><code>Fix
Node.js 10 on CI</code></a></li>
<li><a
href="15c21d3a7c"><code>Fix
old Node.js CI</code></a></li>
<li><a
href="27523c1c56"><code>Update
fraction.js</code></a></li>
<li><a
href="88a0d3e0f8"><code>Update
dependencies and fix stretch and update example</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>
2025-11-17 17:22:37 -05:00
Alexander Lichter
21f2bf99b9
perf(vite): add plugin hook filter (#19308)
## Summary

This PR adds plugin [hook
filters](https://vite.dev/guide/api-plugin#hook-filters) to the Vite
plugin. They are backwards-compatible, having no impact on older Vite
versions as the check for `isPotentialCssRootFile` is still included.

It can be dropped once you don't need to support older Vite versions.

## See also

https://github.com/e18e/ecosystem-issues/issues/171

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-11-14 10:44:07 -05:00
Jordan Pittman
4455051c48
Skip comments in Ruby files when checking for class names (#19243)
Fixes #19239
2025-11-13 05:53:39 -05:00
Ishita SIngh
5bc90dd2e0
Include filename and line numbers in CSS parse errors (#19282)
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-11-10 13:29:23 -05:00
depfu[bot]
5a89571efd
Update eslint 9.37.0 → 9.39.1 (minor) (#19287)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.37.0 → 9.39.1) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.39.1">9.39.1</a></h4>

<blockquote><h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="650753ee39"><code
class="notranslate">650753e</code></a> fix: Only pass node to JS lang
visitor methods (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20283">#20283</a>)
(Nicholas C. Zakas)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="51b51f4f1c"><code
class="notranslate">51b51f4</code></a> docs: add a section on when to
use extends vs cascading (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20268">#20268</a>)
(Tanuj Kanti)</li>
<li>
<a
href="b44d42699d"><code
class="notranslate">b44d426</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="92db329211"><code
class="notranslate">92db329</code></a> chore: update <code
class="notranslate">@eslint/js</code> version to 9.39.1 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20284">#20284</a>)
(Francesco Trotta)</li>
<li>
<a
href="c7ebefc9ea"><code
class="notranslate">c7ebefc</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="61778f6ca3"><code
class="notranslate">61778f6</code></a> chore: update
eslint-config-eslint dependency @eslint/js to ^9.39.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20275">#20275</a>)
(renovate[bot])</li>
<li>
<a
href="d9ca2fcd9a"><code
class="notranslate">d9ca2fc</code></a> ci: Add rangeStrategy to eslint
group in renovate config (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20266">#20266</a>)
(唯然)</li>
<li>
<a
href="009e5076ff"><code
class="notranslate">009e507</code></a> test: fix version tests for
ESLint v10 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20274">#20274</a>)
(Milos Djermanovic)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.39.0">9.39.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="cc57d87a3f"><code
class="notranslate">cc57d87</code></a> feat: update error loc to key in
<code class="notranslate">no-dupe-class-members</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20259">#20259</a>)
(Tanuj Kanti)</li>
<li>
<a
href="126552fcf3"><code
class="notranslate">126552f</code></a> feat: update error location in
<code class="notranslate">for-direction</code> and <code
class="notranslate">no-dupe-args</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20258">#20258</a>)
(Tanuj Kanti)</li>
<li>
<a
href="167d0970d3"><code
class="notranslate">167d097</code></a> feat: update <code
class="notranslate">complexity</code> rule to highlight only static
block header (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20245">#20245</a>)
(jaymarvelz)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="15f5c7c168"><code
class="notranslate">15f5c7c</code></a> fix: forward traversal <code
class="notranslate">step.args</code> to visitors (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20253">#20253</a>)
(jaymarvelz)</li>
<li>
<a
href="5a1a534e87"><code
class="notranslate">5a1a534</code></a> fix: allow JSDoc comments in
object-shorthand rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20167">#20167</a>)
(Nitin Kumar)</li>
<li>
<a
href="e86b813eb6"><code
class="notranslate">e86b813</code></a> fix: Use more types from
@eslint/core (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20257">#20257</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="927272d1f0"><code
class="notranslate">927272d</code></a> fix: correct <code
class="notranslate">Scope</code> typings (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20198">#20198</a>)
(jaymarvelz)</li>
<li>
<a
href="37f76d9c53"><code
class="notranslate">37f76d9</code></a> fix: use <code
class="notranslate">AST.Program</code> type for Program node (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20244">#20244</a>)
(Francesco Trotta)</li>
<li>
<a
href="ae07f0b333"><code
class="notranslate">ae07f0b</code></a> fix: unify timing report for
concurrent linting (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20188">#20188</a>)
(jaymarvelz)</li>
<li>
<a
href="b165d471be"><code
class="notranslate">b165d47</code></a> fix: correct <code
class="notranslate">Rule</code> typings (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20199">#20199</a>)
(jaymarvelz)</li>
<li>
<a
href="fb97cda70d"><code
class="notranslate">fb97cda</code></a> fix: improve error message for
missing fix function in suggestions (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20218">#20218</a>)
(jaymarvelz)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="d3e81e30ee"><code
class="notranslate">d3e81e3</code></a> docs: Always recommend to include
a files property (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20158">#20158</a>)
(Percy Ma)</li>
<li>
<a
href="0f0385f140"><code
class="notranslate">0f0385f</code></a> docs: use consistent naming
recommendation (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20250">#20250</a>)
(Alex M. Spieslechner)</li>
<li>
<a
href="a3b145609a"><code
class="notranslate">a3b1456</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="cf5f2dd58d"><code
class="notranslate">cf5f2dd</code></a> docs: fix correct tag of <code
class="notranslate">no-useless-constructor</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20255">#20255</a>)
(Tanuj Kanti)</li>
<li>
<a
href="10b995c8e5"><code
class="notranslate">10b995c</code></a> docs: add TS options and examples
for <code class="notranslate">nofunc</code> in <code
class="notranslate">no-use-before-define</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20249">#20249</a>)
(Tanuj Kanti)</li>
<li>
<a
href="2584187e4a"><code
class="notranslate">2584187</code></a> docs: remove repetitive word in
comment (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20242">#20242</a>)
(reddaisyy)</li>
<li>
<a
href="637216bd4f"><code
class="notranslate">637216b</code></a> docs: update CLI flags migration
instructions (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20238">#20238</a>)
(jaymarvelz)</li>
<li>
<a
href="e7cda3bdf1"><code
class="notranslate">e7cda3b</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="7b9446f7cc"><code
class="notranslate">7b9446f</code></a> docs: handle empty flags sections
on the feature flags page (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20222">#20222</a>)
(sethamus)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="dfe3c1b203"><code
class="notranslate">dfe3c1b</code></a> chore: update <code
class="notranslate">@eslint/js</code> version to 9.39.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20270">#20270</a>)
(Francesco Trotta)</li>
<li>
<a
href="2375a6de82"><code
class="notranslate">2375a6d</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="a1f4e52d67"><code
class="notranslate">a1f4e52</code></a> chore: update <code
class="notranslate">@eslint</code> dependencies (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20265">#20265</a>)
(Francesco Trotta)</li>
<li>
<a
href="c7d3229848"><code
class="notranslate">c7d3229</code></a> chore: update dependency
@eslint/core to ^0.17.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20256">#20256</a>)
(renovate[bot])</li>
<li>
<a
href="27549bc774"><code
class="notranslate">27549bc</code></a> chore: update fuzz testing to not
error if code sample minimizer fails (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20252">#20252</a>)
(Milos Djermanovic)</li>
<li>
<a
href="a1370ee40e"><code
class="notranslate">a1370ee</code></a> ci: bump actions/setup-node from
5 to 6 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20230">#20230</a>)
(dependabot[bot])</li>
<li>
<a
href="9e7fad4a18"><code
class="notranslate">9e7fad4</code></a> chore: add script to
auto-generate eslint:recommended configuration (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20208">#20208</a>)
(唯然)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.38.0">9.38.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="ce40f74efd"><code
class="notranslate">ce40f74</code></a> feat: update <code
class="notranslate">complexity</code> rule to only highlight function
header (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20048">#20048</a>)
(Atul Nair)</li>
<li>
<a
href="e37e590aae"><code
class="notranslate">e37e590</code></a> feat: correct <code
class="notranslate">no-loss-of-precision</code> false positives with
<code class="notranslate">e</code> notation (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20187">#20187</a>)
(Francesco Trotta)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="50c3dfd980"><code
class="notranslate">50c3dfd</code></a> fix: improve type support for
isolated dependencies in pnpm (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20201">#20201</a>)
(Francesco Trotta)</li>
<li>
<a
href="a1f06a350c"><code
class="notranslate">a1f06a3</code></a> fix: correct SourceCode typings
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20114">#20114</a>)
(Pixel998)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="462675af8a"><code
class="notranslate">462675a</code></a> docs: improve web accessibility
by hiding non-semantic character (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20205">#20205</a>)
(루밀LuMir)</li>
<li>
<a
href="c070e65f6b"><code
class="notranslate">c070e65</code></a> docs: correct formatting in <code
class="notranslate">no-irregular-whitespace</code> rule documentation
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20203">#20203</a>)
(루밀LuMir)</li>
<li>
<a
href="b39e71a213"><code
class="notranslate">b39e71a</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="cd39983148"><code
class="notranslate">cd39983</code></a> docs: move <code
class="notranslate">custom-formatters</code> type descriptions to <code
class="notranslate">nodejs-api</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20190">#20190</a>)
(Percy Ma)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="d17c795bf1"><code
class="notranslate">d17c795</code></a> chore: upgrade @eslint/js@9.38.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20221">#20221</a>)
(Milos Djermanovic)</li>
<li>
<a
href="25d0e33270"><code
class="notranslate">25d0e33</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="c82b5efa1f"><code
class="notranslate">c82b5ef</code></a> refactor: Use types from
@eslint/core (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20168">#20168</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="ff31609f19"><code
class="notranslate">ff31609</code></a> ci: add Node.js 25 to <code
class="notranslate">ci.yml</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20220">#20220</a>)
(루밀LuMir)</li>
<li>
<a
href="004577eda2"><code
class="notranslate">004577e</code></a> ci: bump github/codeql-action
from 3 to 4 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20211">#20211</a>)
(dependabot[bot])</li>
<li>
<a
href="eac71fb771"><code
class="notranslate">eac71fb</code></a> test: remove use of <code
class="notranslate">nodejsScope</code> option of eslint-scope from tests
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20206">#20206</a>)
(Milos Djermanovic)</li>
<li>
<a
href="4168a18b7e"><code
class="notranslate">4168a18</code></a> chore: fix typo in
legacy-eslint.js (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20202">#20202</a>)
(Sweta Tanwar)</li>
<li>
<a
href="205dbd2d92"><code
class="notranslate">205dbd2</code></a> chore: fix typos (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20200">#20200</a>)
(ntnyq)</li>
<li>
<a
href="dbb200e360"><code
class="notranslate">dbb200e</code></a> chore: use team member's username
when name is not available in data (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20194">#20194</a>)
(Milos Djermanovic)</li>
<li>
<a
href="8962089edb"><code
class="notranslate">8962089</code></a> chore: mark deprecated rules as
available until v11.0.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20184">#20184</a>)
(Pixel998)</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="d5d1bdf5fd...e2772811a8">See
the full diff on Github</a>. The new version differs by 59 commits:</p>
<ul>
<li><a
href="e2772811a8"><code>9.39.1</code></a></li>
<li><a
href="4cdf397b30"><code>Build:
changelog update for 9.39.1</code></a></li>
<li><a
href="92db329211"><code>chore:
update `@eslint/js` version to 9.39.1 (#20284)</code></a></li>
<li><a
href="c7ebefc9ea"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="650753ee39"><code>fix:
Only pass node to JS lang visitor methods (#20283)</code></a></li>
<li><a
href="51b51f4f1c"><code>docs:
add a section on when to use extends vs cascading
(#20268)</code></a></li>
<li><a
href="61778f6ca3"><code>chore:
update eslint-config-eslint dependency @eslint/js to ^9.39.0
(#20275)</code></a></li>
<li><a
href="d9ca2fcd9a"><code>ci:
Add rangeStrategy to eslint group in renovate config
(#20266)</code></a></li>
<li><a
href="009e5076ff"><code>test:
fix version tests for ESLint v10 (#20274)</code></a></li>
<li><a
href="b44d42699d"><code>docs:
Update README</code></a></li>
<li><a
href="ac3a60dffc"><code>9.39.0</code></a></li>
<li><a
href="e79017ff52"><code>Build:
changelog update for 9.39.0</code></a></li>
<li><a
href="dfe3c1b203"><code>chore:
update `@eslint/js` version to 9.39.0 (#20270)</code></a></li>
<li><a
href="2375a6de82"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="d3e81e30ee"><code>docs:
Always recommend to include a files property (#20158)</code></a></li>
<li><a
href="15f5c7c168"><code>fix:
forward traversal `step.args` to visitors (#20253)</code></a></li>
<li><a
href="5a1a534e87"><code>fix:
allow JSDoc comments in object-shorthand rule (#20167)</code></a></li>
<li><a
href="cc57d87a3f"><code>feat:
update error loc to key in `no-dupe-class-members`
(#20259)</code></a></li>
<li><a
href="a1f4e52d67"><code>chore:
update `@eslint` dependencies (#20265)</code></a></li>
<li><a
href="e86b813eb6"><code>fix:
Use more types from @eslint/core (#20257)</code></a></li>
<li><a
href="126552fcf3"><code>feat:
update error location in `for-direction` and `no-dupe-args`
(#20258)</code></a></li>
<li><a
href="927272d1f0"><code>fix:
correct `Scope` typings (#20198)</code></a></li>
<li><a
href="c7d3229848"><code>chore:
update dependency @eslint/core to ^0.17.0 (#20256)</code></a></li>
<li><a
href="0f0385f140"><code>docs:
use consistent naming recommendation (#20250)</code></a></li>
<li><a
href="a3b145609a"><code>docs:
Update README</code></a></li>
<li><a
href="37f76d9c53"><code>fix:
use `AST.Program` type for Program node (#20244)</code></a></li>
<li><a
href="cf5f2dd58d"><code>docs:
fix correct tag of `no-useless-constructor` (#20255)</code></a></li>
<li><a
href="27549bc774"><code>chore:
update fuzz testing to not error if code sample minimizer fails
(#20252)</code></a></li>
<li><a
href="10b995c8e5"><code>docs:
add TS options and examples for `nofunc` in `no-use-before-define`
(#20249)</code></a></li>
<li><a
href="167d0970d3"><code>feat:
update `complexity` rule to highlight only static block header
(#20245)</code></a></li>
<li><a
href="2584187e4a"><code>docs:
remove repetitive word in comment (#20242)</code></a></li>
<li><a
href="ae07f0b333"><code>fix:
unify timing report for concurrent linting (#20188)</code></a></li>
<li><a
href="b165d471be"><code>fix:
correct `Rule` typings (#20199)</code></a></li>
<li><a
href="637216bd4f"><code>docs:
update CLI flags migration instructions (#20238)</code></a></li>
<li><a
href="e7cda3bdf1"><code>docs:
Update README</code></a></li>
<li><a
href="a1370ee40e"><code>ci:
bump actions/setup-node from 5 to 6 (#20230)</code></a></li>
<li><a
href="fb97cda70d"><code>fix:
improve error message for missing fix function in suggestions
(#20218)</code></a></li>
<li><a
href="9e7fad4a18"><code>chore:
add script to auto-generate eslint:recommended configuration
(#20208)</code></a></li>
<li><a
href="7b9446f7cc"><code>docs:
handle empty flags sections on the feature flags page
(#20222)</code></a></li>
<li><a
href="8fe511b4c0"><code>9.38.0</code></a></li>
<li><a
href="f961736693"><code>Build:
changelog update for 9.38.0</code></a></li>
<li><a
href="d17c795bf1"><code>chore:
upgrade @eslint/js@9.38.0 (#20221)</code></a></li>
<li><a
href="25d0e33270"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="50c3dfd980"><code>fix:
improve type support for isolated dependencies in pnpm
(#20201)</code></a></li>
<li><a
href="c82b5efa1f"><code>refactor:
Use types from @eslint/core (#20168)</code></a></li>
<li><a
href="ff31609f19"><code>ci:
add Node.js 25 to `ci.yml` (#20220)</code></a></li>
<li><a
href="ce40f74efd"><code>feat:
update `complexity` rule to only highlight function header
(#20048)</code></a></li>
<li><a
href="e37e590aae"><code>feat:
correct `no-loss-of-precision` false positives with `e` notation
(#20187)</code></a></li>
<li><a
href="004577eda2"><code>ci:
bump github/codeql-action from 3 to 4 (#20211)</code></a></li>
<li><a
href="eac71fb771"><code>test:
remove use of `nodejsScope` option of eslint-scope from tests
(#20206)</code></a></li>
<li><a
href="462675af8a"><code>docs:
improve web accessibility by hiding non-semantic character
(#20205)</code></a></li>
<li><a
href="c070e65f6b"><code>docs:
correct formatting in `no-irregular-whitespace` rule documentation
(#20203)</code></a></li>
<li><a
href="b39e71a213"><code>docs:
Update README</code></a></li>
<li><a
href="4168a18b7e"><code>chore:
fix typo in legacy-eslint.js (#20202)</code></a></li>
<li><a
href="205dbd2d92"><code>chore:
fix typos (#20200)</code></a></li>
<li><a
href="a1f06a350c"><code>fix:
correct SourceCode typings (#20114)</code></a></li>
<li><a
href="dbb200e360"><code>chore:
use team member&#39;s username when name is not available in data
(#20194)</code></a></li>
<li><a
href="cd39983148"><code>docs:
move `custom-formatters` type descriptions to `nodejs-api`
(#20190)</code></a></li>
<li><a
href="8962089edb"><code>chore:
mark deprecated rules as available until v11.0.0
(#20184)</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>
2025-11-09 07:31:49 -05:00
Jordan Pittman
4363426384
Fix validation of source(…) paths (#19274)
Fixes #18833

- [x] Needs tests

Basically we were correctly resolving the path given to `source()`
inside Oxide *but* inside `@tailwindcss/node` when we validated that the
path was a directory we were not.

We incorrectly used the base path of the input file rather than the file
the `source(…)` directive was defined in. This PR fixes that.
2025-11-06 16:24:17 -05:00
Jordan Pittman
e9c9c4f79d
Release v4.1.17 (#19272) 2025-11-06 10:20:36 -05:00
Jordan Pittman
dc6a3ce349
Substitute @variant inside utilities (#19263)
Fixes #19258
2025-11-04 11:28:52 -05:00
Hongxu Xu
babe825f2d
Remove unused crossbeam dependency in oxide (#19256) 2025-11-04 10:37:21 -05:00
depfu[bot]
3e4654d7ea
Update @types/react-dom 19.2.1 → 19.2.2 (patch) (#19259)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react-dom (19.2.1 → 19.2.2) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-11-04 09:49:17 -05:00
depfu[bot]
3cece1d97c Update @napi-rs/cli to version 3.4.1 2025-10-31 14:23:35 +00:00
depfu[bot]
604c22f25e
Update @vitejs/plugin-react 5.0.4 → 5.1.0 (minor) (#19241)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (5.0.4 → 5.1.0) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)



<details>
<summary>Release Notes</summary>

<h4>5.1.0 (from changelog)</h4>
<blockquote><h3 dir="auto">Add <code
class="notranslate">@vitejs/plugin-react/preamble</code> virtual module
for SSR HMR (<a
href="https://bounce.depfu.com/github.com/vitejs/vite-plugin-react/pull/890">#890</a>)</h3>
<p dir="auto">SSR applications can now initialize HMR runtime by
importing <code class="notranslate">@vitejs/plugin-react/preamble</code>
at the top of their client entry instead of manually calling <code
class="notranslate">transformIndexHtml</code>. This simplifies SSR setup
for applications that don't use the <code
class="notranslate">transformIndexHtml</code> API.</p></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please
let us know.</a></em></p>
</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>
2025-10-31 07:35:24 -04:00
depfu[bot]
e71e70eda9
Update magic-string 0.30.19 → 0.30.21 (minor) (#19238)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ magic-string (0.30.19 → 0.30.21) ·
[Repo](https://github.com/rich-harris/magic-string) ·
[Changelog](https://github.com/Rich-Harris/magic-string/blob/master/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/Rich-Harris/magic-string/releases/tag/v0.30.21">0.30.21</a></h4>

<blockquote><p dir="auto"><em>No significant changes</em></p>
<h5 dir="auto">    <a
href="https://bounce.depfu.com/github.com/Rich-Harris/magic-string/compare/v0.30.20...v0.30.21">View
changes on GitHub</a>
</h5></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="bdef7d5ab5...410fd4d080">See
the full diff on Github</a>. The new version differs by 7 commits:</p>
<ul>
<li><a
href="410fd4d080"><code>chore:
release v0.30.21</code></a></li>
<li><a
href="5c1800935d"><code>chore:
update repository url</code></a></li>
<li><a
href="97ea74d842"><code>chore:
release v0.30.20</code></a></li>
<li><a
href="3f6b5eb3fb"><code>chore:
update deps</code></a></li>
<li><a
href="60177d51ed"><code>ci:
setup OIDC</code></a></li>
<li><a
href="fbcf5d7e59"><code>chore:
create release.yml</code></a></li>
<li><a
href="cde8c33ef5"><code>chore:
update readme (#305)</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>
2025-10-31 07:34:21 -04:00
depfu[bot]
4827c465b8
Update @emnapi/core 1.5.0 → 1.6.0 (minor) (#19220)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/core (1.5.0 → 1.6.0) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.6.0">1.6.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>feat: added SharedArrayBuffer api by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/171">#171</a>
</li>
<li>feat: make napi_delete_reference use node_api_basic_env by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/170">#170</a>
</li>
<li>ci: migrate to npm trusted publishing by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/168">#168</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.5.0...v1.6.0"><tt>v1.5.0...v1.6.0</tt></a></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="61628bd5e2...86d68f88ec">See
the full diff on Github</a>. The new version differs by 4 commits:</p>
<ul>
<li><a
href="86d68f88ec"><code>1.6.0</code></a></li>
<li><a
href="100b8a3580"><code>feat:
make napi_delete_reference use node_api_basic_env (#170)</code></a></li>
<li><a
href="bf622fc37b"><code>[Backport]
feat: added SharedArrayBuffer api (#171)</code></a></li>
<li><a
href="971e3d9e37"><code>ci:
migrate to npm trusted publishing (#168)</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>
2025-10-28 12:25:07 +00:00
depfu[bot]
4aa763b2a3
Update emnapi 1.5.0 → 1.6.0 (minor) (#19221)
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?




#### ✳️ emnapi (1.5.0 → 1.6.0) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.6.0">1.6.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>feat: added SharedArrayBuffer api by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/171">#171</a>
</li>
<li>feat: make napi_delete_reference use node_api_basic_env by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/170">#170</a>
</li>
<li>ci: migrate to npm trusted publishing by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/168">#168</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.5.0...v1.6.0"><tt>v1.5.0...v1.6.0</tt></a></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="61628bd5e2...86d68f88ec">See
the full diff on Github</a>. The new version differs by 4 commits:</p>
<ul>
<li><a
href="86d68f88ec"><code>1.6.0</code></a></li>
<li><a
href="100b8a3580"><code>feat:
make napi_delete_reference use node_api_basic_env (#170)</code></a></li>
<li><a
href="bf622fc37b"><code>[Backport]
feat: added SharedArrayBuffer api (#171)</code></a></li>
<li><a
href="971e3d9e37"><code>ci:
migrate to npm trusted publishing (#168)</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>
2025-10-28 08:18:37 -04:00
depfu[bot]
7bab613749
Update @emnapi/runtime 1.5.0 → 1.6.0 (minor) (#19219)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/runtime (1.5.0 → 1.6.0) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.6.0">1.6.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>feat: added SharedArrayBuffer api by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/171">#171</a>
</li>
<li>feat: make napi_delete_reference use node_api_basic_env by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/170">#170</a>
</li>
<li>ci: migrate to npm trusted publishing by <a
href="https://bounce.depfu.com/github.com/toyobayashi">@toyobayashi</a>
in <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/168">#168</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.5.0...v1.6.0"><tt>v1.5.0...v1.6.0</tt></a></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="61628bd5e2...86d68f88ec">See
the full diff on Github</a>. The new version differs by 4 commits:</p>
<ul>
<li><a
href="86d68f88ec"><code>1.6.0</code></a></li>
<li><a
href="100b8a3580"><code>feat:
make napi_delete_reference use node_api_basic_env (#170)</code></a></li>
<li><a
href="bf622fc37b"><code>[Backport]
feat: added SharedArrayBuffer api (#171)</code></a></li>
<li><a
href="971e3d9e37"><code>ci:
migrate to npm trusted publishing (#168)</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>
2025-10-28 08:18:27 -04:00
depfu[bot]
91694fbfc1 Update @playwright/test to version 1.56.1 2025-10-24 11:53:15 +00:00
depfu[bot]
effcd27e17
Update all of nextjs 15.5.4 → 15.5.6 (patch) (#19194)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint-config-next (15.5.4 → 15.5.6)





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



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



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.5.6">15.5.6</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg 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>Turbopack: don't define process.cwd() in node_modules <a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/83452">#83452</a>
</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/mischnic">@mischnic</a> for
helping!</p></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.5.5">15.5.5</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg 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>Split code-frame into separate compiled package (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/84238">#84238</a>)</li>
<li>Add deprecation warning to Runtime config (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/84650">#84650</a>)</li>
<li>fix: unstable_cache should perform blocking revalidation during ISR
revalidation (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/84716">#84716</a>)</li>
<li>feat: <code
class="notranslate">experimental.middlewareClientMaxBodySize</code> body
cloning limit (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/84722">#84722</a>)</li>
<li>fix: missing next/link types with typedRoutes (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/84779">#84779</a>)</li>
</ul>
<h3 dir="auto">Misc Changes</h3>
<ul dir="auto">
<li>docs: early October improvements and fixes (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/84334">#84334</a>)</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/devjiwonchoi">@devjiwonchoi</a>,
<a href="https://bounce.depfu.com/github.com/ztanner">@ztanner</a>, and
<a href="https://bounce.depfu.com/github.com/icyJoseph">@icyJoseph</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="40f1d7814d...55ef0e3ebc">See
the full diff on Github</a>. The new version differs by 10 commits:</p>
<ul>
<li><a
href="55ef0e3ebc"><code>v15.5.6</code></a></li>
<li><a
href="92bbbb1bec"><code>Backport:
don&#39;t define `process.cwd()` in node_modules
(#84957)</code></a></li>
<li><a
href="f895b72762"><code>Fix
url-imports test on 15-5 (#84966)</code></a></li>
<li><a
href="81f530db26"><code>v15.5.5</code></a></li>
<li><a
href="9abbc0e9eb"><code>[backport]
fix: missing `next/link` types with `typedRoutes` (#82814)
(#84779)</code></a></li>
<li><a
href="121e1b566f"><code>[backport]
docs: early October improvements and fixes (#84334)</code></a></li>
<li><a
href="1b276c98f9"><code>[backport]:
`experimental.middlewareClientMaxBodySize` (#84722)</code></a></li>
<li><a
href="2061f04132"><code>[backport]
fix: unstable_cache should perform blocking revalidation during ISR
revalidation (#84716)</code></a></li>
<li><a
href="ce3d9639d1"><code>[backport]
Add deprecation warning to Runtime config (#84168)
(#84650)</code></a></li>
<li><a
href="ec69752d9e"><code>[backport]
Split code-frame into separate compiled package (#84174)
(#84238)</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>
2025-10-24 07:37:00 -04:00
Robin Malfait
2530934a4f
update changelog 2025-10-23 12:55:39 +02:00
Robin Malfait
cbbbe84475
Release 4.1.16 (#19185) 2025-10-23 12:32:27 +02:00
Robin Malfait
601d6719f8
Fix incorrect colors used in pseudo-element (#19184)
This PR essentially reverts
https://github.com/tailwindlabs/tailwindcss/pull/19069

We added the nested `&` inside the `@supports` query when we create
fallbacks for color-mix so that devtools (Safari) doesn't freak out.
This works in most cases, however, if you have a parent pseudo element
like `::before`, then the browser will not allow the nested `&`
resulting in invalid CSS.

This PR means that we go back to the broken devtools experience in
Safari, but at least the CSS is valid and works as expected.

Fixes: #19183
2025-10-23 10:07:01 +00:00
Jordan Pittman
a41add9fab
Improve canonicalization for & > :pseudo and & :pseudo arbitrary variants (#19178)
This improves canonicalization of arbitrary variants that use pseudo
classes a bit.

Before this we would see `[&_:first-child]:flex` and leave it be when it
can instead be written as `**:first:flex`. Likewise, for pseudo classes
that don't have a variant, we can still simplify things a bit as
`[&_:--custom]` can be written `**:[:--custom]`.
2025-10-22 08:29:08 -04:00
Jordan Pittman
0113b88fbd
Fix canonicalization of arbitrary variants with attribute selectors (#19176)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1481

We were detecting when we needed to apply the `*` and `**` variants and
even detecting attribute selectors. However we only cleaned up the
attribute selectors when they were `data-*` or `aria-*` attributes. This
resulted in a "loop" of sorts because we'd:
- See something like `[&_>_[foo]]:flex`
- Notice that it needs a `*` variant
- Add the `*` variant, giving `*:[&_>[foo]]:flex`
- But fail to cleanup the remainder of the variant

This then meant that we'd see the `*:[&_>[foo]]:flex` the next time we
checked, notice that it still needed a `*` variant, and repeat…

This PR fixes this case to clean up the selector.
2025-10-22 08:19:54 -04:00
Jordan Pittman
29687e0183
Discard candidates with an empty data type (#19172)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1479

Maybe should close
https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1480 —
perhaps we can find a workaround there for older versions?

We're building up a class name in code to validate if something is a
valid variant: `{variant}:[color:red]`

if `{variant}` got replaced with `bg-[` then we'd produce
`bg-[:[color:red]` and this parsed as a valid candidate:
```
bg-[:[color:red]
^^                root: `bg`
   ^              data type: `` (empty string) — this should be invalid
     ^^^^^^^^^^   value: `[color:red`
```

The value isn't valid _but_ the syntax for arbitrary values is pretty
lax in core. Oxide already won't pick something like this up though so
no problem there. Only a problem for something like IntelliSense or
clients using the compile() API directly.
2025-10-22 06:14:43 -04:00
Robin Malfait
56e7f3b2c2
Improve memory usage during canonicalization (#19171)
This PR drastically improves the memory usage when performing
canonicalization if you swap out the underlying DesignSystem often. This
will be most noticeable in Intellisense.

The big issue we had is that we used module scoped Map objects where we
cache data based on the DesignSystem. If you then create new design
systems (often), then the cache would just keep growing and growing.

This PR solves that by essentially storing all the caches on the Design
System itself. This way, when you throw away a Design System, all the
caches go with it.

Another approach would've been to use a WeakMap, but then we would have
to make sure that no strong references to the DesignSystem exist
anywhere else, otherwise we would still have the same memory issues.

Note: make sure to go commit by commit and use `?w=1` to ignore
whitespace changes.

## Test plan

1. All existing tests pass

Not super sure how to test this as part of the test suite without making
it slow But I also don't think that's super necessary either. Here is an
experiment I did where I introduce 5 design systems:
<img width="1326" height="274" alt="image"
src="https://github.com/user-attachments/assets/817025e3-0f5b-44be-949b-54ed08f5b3fb"
/>

On the current `main` branch, this looks like:
<img width="619" height="69" alt="image"
src="https://github.com/user-attachments/assets/588ae99b-c978-4c01-bfd1-5cc0725723a8"
/>


In this PR, the memory usage looks like:
<img width="512" height="56" alt="image"
src="https://github.com/user-attachments/assets/0052ad21-7b99-4edf-8a14-8ccef52362db"
/>

The memory usage is stable, but to actually prove that we can still
track multiple design systems, let's track them all in a `Set` so
garbage collection cannot get rid of the unused design system objects:
<img width="847" height="230" alt="image"
src="https://github.com/user-attachments/assets/5f044927-3d53-4c15-8145-78eb2b4d6d54"
/>

Now we're sort of back to the current situation on `main`:
<img width="507" height="53" alt="image"
src="https://github.com/user-attachments/assets/868c0238-8646-41ce-8151-e0ef6dd17d64"
/>
2025-10-21 16:55:30 +02:00
Jordan Pittman
3a4ab8201b
Stop suggesting legacy utilities (#19169)
It was already supposed to work this way — and it appeared via the tests
that it did — but the tests weren't loading the design system normally
so when we split the legacy utilities into a separate file it stopped
testing this properly.

The setup here is a bit iffy but the gist is:
- We allow static utilities to be suggested by default
- A static utility can *opt out* of suggestions by explicitly
registering themselves to return none.
2025-10-21 06:54:10 -04:00
Robin Malfait
7537e34fd1
Ignore --tw- variables during internal signature computation (#19156)
This PR improves some of the signature computation logic. Right now,
when you want to convert `[font-weight:400]` to `font-normal` it's not
going to work because the signatures don't like up:

```css
/* [font-weight:400] */
.x {
  font-weight: 400;
}

/* font-normal */
.x {
  --tw-font-weight: 400;
  font-weight: 400;
}
```

So this PR essentially ignores `--tw-{property}` _if_ the `{property}`
exists with the exact same value.

The main reason we have this, is to make composition of utilities
easier.

As with any of these upgrades, they are the expected behavior in most
cases, but there could always be a case where you don't want this, but
that's why the upgrade tool requires you to review each change before
applying it. Intellisense will recommend the simplified class, but it's
up to you to decide if you want to apply it or not.

There is a known edge case for `leading-{num}`, because the property is
`line-height` and the CSS variable is `--tw-leading`. Right now we map
`--tw-leading` to `line-height` but we can also update the leading
classes to use `--tw-line-height` instead but that feels like a bigger
breaking change _if_ people rely on these internal CSS variables...
2025-10-20 15:21:07 +00:00
Robin Malfait
66c18ca8a4
Collapse multiple utilities (#19147)
Some commits look like a lot of changes, but they just move things
around, so best to use `?w=1` when viewing commit by commit.

This PR adds a new feature to the `designSystem.canonicalizeCandidates`
to collapse multiple utilities to fewer utilities. To make this
possible, we also have to convert some logical properties to physical
properties (controllable via an option).

```ts
ds.canonicalizeCandidates(['w-4', 'h-4'], {
  collapse: true // Default `true`
  logicalToPhysical: true // Default `true`
}) // → ['size-4']
```

We can already compute the signature of each utility, where if two
utilities have the same signature they are considered the same.

However it's kind of impossible to generate all combinations of all
utilities ever to figure out if there is a potential collapse possible.
Even if we just focus on the incoming list of candidates, there could
still be a lot of classes. So instead we have to be a bit more clever.

First, we group candidates together by the used variants and if the
important `!` flag was used. We can improve this in the future, but for
now we won't even try to combine `hover:w-4 h-4`.

Next, for each candidate, we figure out which property and value it
uses. We can build up a lookup table for this. We already did this
process for all utilities in the system as well.

The lookup table for `w-4 h-4 p-4` might look something like this.

```json
{
  "width": {
    "16px": ["w-4", "size-4"],
  },
  "height": {
    "16px": ["h-4", "size-4"],
  },
  "padding": {
    "16px": ["p-4"],
  }
}
```

Next, we can build groups of candidates where an intersection exists in
the lookup table. In the example above, we can see that `w-4` and `h-4`
both map to `size-4` for the value `16px`. So we can group these two
candidates. The `p-4`d doesn't intersect with anything else, so it
remains alone. This also means that we only have to generate
combinations for two candidates (2^2 = 4) instead of three (2^3 = 8). In
practice, your class list might have many classes, so keeping this
number low is important.

When we generate combinations, we will generate the most amount of
candidates first so we have the largest collapse possible. We also stop
when we reach <= 1 combinations because we need at least two candidates
to collapse.

Since this uses the internal design system, if you have custom
`@utility`s, this will work as expected.

```css
@utility example {
  @apply border rounded p-4;
}
```

If you then use:
```html
<div class="border m-0 rounded p-4"></div>
```

Then we can collapse this to:
```html
<div class="example m-0"></div>
```

But even if we used:
```html
<div class="border m-0 rounded pt-4 pb-4 px-4"></div>
```

It would still collapse to:
```html
<div class="example m-0"></div>
```

...because the `pt-4` and `pb-4` can collapse to `py-4`, and `py-4` and
`px-4` can collapse to `p-4`.

This is also where that logical to physical conversion comes into play,
because while `pt-4` and `pb-4` set the physical `padding-top` and
`padding-bottom` properties. The `py-4` utility sets the logical
`padding-block` property. So we internally transform `padding-block` to
the `padding-top` and `padding-bottom` physical properties. The funny
thing is that this logical to physical conversion actually means that we
will convert `pt-4` and `pb-4` from _physical_ to _logical_ properties,
because we converted to `py-4`...

In _most_ cases it's fine, and even preferred to use `py-1` over `pt-1
pb-1`, but in case it's not, then you can disable the
`logicalToPhysical` option.

## Test plan

Added some dedicated tests for this new functionality.
2025-10-20 15:15:31 +00:00
Jordan Pittman
6c4081421e
Incorporate changes from latest ignore crate (#19148)
This updates our internal fork of the `ignore` crate to incorporate
changes from the latest release
[v0.4.24](https://github.com/BurntSushi/ripgrep/compare/ignore-0.4.23...ignore-0.4.24).

Aside: We should look into opening issues about the changes we had to
make to see if they could be addressed in the crate itself so we can get
rid of our fork.
2025-10-20 09:49:40 -04:00
depfu[bot]
a6a7e4cbaf
Update jiti 2.6.0 → 2.6.1 (patch) (#19160)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ jiti (2.6.0 → 2.6.1) · [Repo](https://github.com/unjs/jiti) ·
[Changelog](https://github.com/unjs/jiti/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/unjs/jiti/releases/tag/v2.6.1">2.6.1</a></h4>

<blockquote><p dir="auto"><a
href="https://bounce.depfu.com/github.com/unjs/jiti/compare/v2.6.0...v2.6.1">compare
changes</a></p>
<h3 dir="auto">🩹 Fixes</h3>
<ul dir="auto">
<li>
<strong>interop:</strong> Only passthrough default if it is not a
promise (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/408">#408</a>)</li>
</ul>
<h3 dir="auto">📦 Build</h3>
<ul dir="auto">
<li>Revert to <code class="notranslate">terser-webpack-plugin</code> (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/407">#407</a>)</li>
</ul>
<h3 dir="auto">❤️ Contributors</h3>
<ul dir="auto">
<li>Kricsleo (<a
href="https://bounce.depfu.com/github.com/kricsleo">@kricsleo</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="82919cff8b...aedcdee7fe">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="aedcdee7fe"><code>chore(release):
v2.6.1</code></a></li>
<li><a
href="8b41497481"><code>chore:
remove unused code</code></a></li>
<li><a
href="974ca4008a"><code>chore:
update deps</code></a></li>
<li><a
href="e840692427"><code>build:
revert to `terser-webpack-plugin` (#407)</code></a></li>
<li><a
href="092cdd9952"><code>fix(interop):
only passthrough default if it is not a promise (#408)</code></a></li>
<li><a
href="037c646c80"><code>chore:
update bench</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>
2025-10-20 14:59:11 +02:00
Robin Malfait
b2e2435ccb
Release 4.1.15 (#19159) 2025-10-20 14:48:42 +02:00
Robin Malfait
3b636b74d7
Mark break-words as deprecated, and upgrade to wrap-break-word (#19157)
This PR marks `break-words` as deprecated (such that intellisense
doesn't suggest it anymore). Updates the upgrade tooling to prefer
`wrap-break-word` instead.

Note: `break-words` will still work as expected.

## Test plan

1. `break-words` still generates the correct CSS.
2. Intellisense doesn't suggest `break-words` anymore.
3. Upgrade tooling suggests `wrap-break-word` instead of `break-words`.
2025-10-20 14:14:25 +02:00
depfu[bot]
fb0f432e48
Update @types/bun 1.2.22 → 1.3.0 (minor) (#19145) 2025-10-20 12:20:33 +02:00
Philipp Spiess
afe3de3bd6
Revert agentic issue-triage workflow (#19155)
Reverts #19057

Unfortunately wasn't able to get this to work since the permissions will
always cause it to not be triggered when someone outside our GitHub orgs
contributes.
2025-10-20 12:20:08 +02:00
depfu[bot]
fee19b2d4e
Update bun 1.2.22 → 1.3.0 (minor) (#19144) 2025-10-20 11:43:39 +02:00
Philipp Spiess
70f27d50a6
Remove Oxide postinstall script (#19149)
This PR effectively reverts #17929.

The bug in npm that required it was fixed a couple of months ago and
with recent changes to pnpm that requires manually approving all
postinstall scripts, this is creating some unnecessary noise.
2025-10-19 10:50:21 +02:00
Jordan Pittman
48c274d83e
Update Lightning CSS to 1.30.2 (#19143) 2025-10-17 15:41:49 -04:00
depfu[bot]
e25c1f0594
Update eslint 9.36.0 → 9.37.0 (minor) (#19142)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.36.0 → 9.37.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.37.0">9.37.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="39f7fb493a"><code
class="notranslate">39f7fb4</code></a> feat: <code
class="notranslate">preserve-caught-error</code> should recognize all
static "cause" keys (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20163">#20163</a>)
(Pixel998)</li>
<li>
<a
href="f81eabc584"><code
class="notranslate">f81eabc</code></a> feat: support TS syntax in <code
class="notranslate">no-restricted-imports</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19562">#19562</a>)
(Nitin Kumar)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="a129cced7a"><code
class="notranslate">a129cce</code></a> fix: correct <code
class="notranslate">no-loss-of-precision</code> false positives for
leading zeros (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20164">#20164</a>)
(Francesco Trotta)</li>
<li>
<a
href="09e04fcc3f"><code
class="notranslate">09e04fc</code></a> fix: add missing AST token types
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20172">#20172</a>)
(Pixel998)</li>
<li>
<a
href="861c6da2bd"><code
class="notranslate">861c6da</code></a> fix: correct <code
class="notranslate">ESLint</code> typings (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20122">#20122</a>)
(Pixel998)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="b950359c5f"><code
class="notranslate">b950359</code></a> docs: fix typos across the docs
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20182">#20182</a>)
(루밀LuMir)</li>
<li>
<a
href="42498a2798"><code
class="notranslate">42498a2</code></a> docs: improve ToC accessibility
by hiding non-semantic character (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20181">#20181</a>)
(Percy Ma)</li>
<li>
<a
href="29ea092b93"><code
class="notranslate">29ea092</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="5c97a04578"><code
class="notranslate">5c97a04</code></a> docs: show <code
class="notranslate">availableUntil</code> in deprecated rule banner (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20170">#20170</a>)
(Pixel998)</li>
<li>
<a
href="90a71bf502"><code
class="notranslate">90a71bf</code></a> docs: update <code
class="notranslate">README</code> files to add badge and instructions
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20115">#20115</a>)
(루밀LuMir)</li>
<li>
<a
href="1603ae1526"><code
class="notranslate">1603ae1</code></a> docs: update references from
<code class="notranslate">master</code> to <code
class="notranslate">main</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20153">#20153</a>)
(루밀LuMir)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="afe8a13469"><code
class="notranslate">afe8a13</code></a> chore: update <code
class="notranslate">@eslint/js</code> dependency to version 9.37.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20183">#20183</a>)
(Francesco Trotta)</li>
<li>
<a
href="abee4ca1fa"><code
class="notranslate">abee4ca</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="fc9381f6ca"><code
class="notranslate">fc9381f</code></a> chore: fix typos in comments (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20175">#20175</a>)
(overlookmotel)</li>
<li>
<a
href="e1574a22d3"><code
class="notranslate">e1574a2</code></a> chore: unpin jiti (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20173">#20173</a>)
(renovate[bot])</li>
<li>
<a
href="e1ac05e2fa"><code
class="notranslate">e1ac05e</code></a> refactor: mark <code
class="notranslate">ESLint.findConfigFile()</code> as <code
class="notranslate">async</code>, add missing docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20157">#20157</a>)
(Pixel998)</li>
<li>
<a
href="347906d627"><code
class="notranslate">347906d</code></a> chore: update eslint (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20149">#20149</a>)
(renovate[bot])</li>
<li>
<a
href="0cb5897e24"><code
class="notranslate">0cb5897</code></a> test: remove tmp dir created for
circular fixes in multithread mode test (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20146">#20146</a>)
(Milos Djermanovic)</li>
<li>
<a
href="bb995665e3"><code
class="notranslate">bb99566</code></a> ci: pin <code
class="notranslate">jiti</code> to version 2.5.1 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20151">#20151</a>)
(Pixel998)</li>
<li>
<a
href="177f669adc"><code
class="notranslate">177f669</code></a> perf: improve worker count
calculation for <code class="notranslate">"auto"</code> concurrency (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20067">#20067</a>)
(Francesco Trotta)</li>
<li>
<a
href="448b57bca3"><code
class="notranslate">448b57b</code></a> chore: Mark deprecated formatting
rules as available until v11.0.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20144">#20144</a>)
(Milos Djermanovic)</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="b4857e54e5...d5d1bdf5fd">See
the full diff on Github</a>. The new version differs by 23 commits:</p>
<ul>
<li><a
href="d5d1bdf5fd"><code>9.37.0</code></a></li>
<li><a
href="94865ff41c"><code>Build:
changelog update for 9.37.0</code></a></li>
<li><a
href="afe8a13469"><code>chore:
update `@eslint/js` dependency to version 9.37.0
(#20183)</code></a></li>
<li><a
href="abee4ca1fa"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="b950359c5f"><code>docs:
fix typos across the docs (#20182)</code></a></li>
<li><a
href="42498a2798"><code>docs:
improve ToC accessibility by hiding non-semantic character
(#20181)</code></a></li>
<li><a
href="fc9381f6ca"><code>chore:
fix typos in comments (#20175)</code></a></li>
<li><a
href="e1574a22d3"><code>chore:
unpin jiti (#20173)</code></a></li>
<li><a
href="29ea092b93"><code>docs:
Update README</code></a></li>
<li><a
href="a129cced7a"><code>fix:
correct `no-loss-of-precision` false positives for leading zeros
(#20164)</code></a></li>
<li><a
href="09e04fcc3f"><code>fix:
add missing AST token types (#20172)</code></a></li>
<li><a
href="5c97a04578"><code>docs:
show `availableUntil` in deprecated rule banner (#20170)</code></a></li>
<li><a
href="39f7fb493a"><code>feat:
`preserve-caught-error` should recognize all static &quot;cause&quot;
keys (#20163)</code></a></li>
<li><a
href="f81eabc584"><code>feat:
support TS syntax in `no-restricted-imports` (#19562)</code></a></li>
<li><a
href="e1ac05e2fa"><code>refactor:
mark `ESLint.findConfigFile()` as `async`, add missing docs
(#20157)</code></a></li>
<li><a
href="90a71bf502"><code>docs:
update `README` files to add badge and instructions
(#20115)</code></a></li>
<li><a
href="861c6da2bd"><code>fix:
correct `ESLint` typings (#20122)</code></a></li>
<li><a
href="347906d627"><code>chore:
update eslint (#20149)</code></a></li>
<li><a
href="1603ae1526"><code>docs:
update references from `master` to `main` (#20153)</code></a></li>
<li><a
href="0cb5897e24"><code>test:
remove tmp dir created for circular fixes in multithread mode test
(#20146)</code></a></li>
<li><a
href="bb995665e3"><code>ci:
pin `jiti` to version 2.5.1 (#20151)</code></a></li>
<li><a
href="177f669adc"><code>perf:
improve worker count calculation for `&quot;auto&quot;` concurrency
(#20067)</code></a></li>
<li><a
href="448b57bca3"><code>chore:
Mark deprecated formatting rules as available until v11.0.0
(#20144)</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>
2025-10-17 14:41:16 -04:00
depfu[bot]
71e1931044
Update @napi-rs/wasm-runtime 1.0.5 → 1.0.7 (patch) (#19140)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/wasm-runtime (1.0.5 → 1.0.7) ·
[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>
2025-10-17 11:00:55 -04:00
Benoît Rouleau
89cbfc7b2d
Add optimize option to @tailwindcss/vite plugin (#19131)
Adds an `optimize` option to the Vite plugin that matches the API and
behavior of the PostCSS plugin.

Supports three formats:
- `optimize: false` - disable optimization
- `optimize: true` - enable optimization with minification
- `optimize: { minify: false }` - enable optimization without
minification

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-10-17 15:00:25 +00:00
Robin Malfait
acb27ef9e9
Generalize the walk implementation (#19126)
This PR generalizes the `walk` implementations we have. What's important
here is that we currently have multiple `walk` implementations, one for
the AST, one for the `SelectorParser`, one for the `ValueParser`.

Sometimes, we also need to go up the tree in a depth-first manner. For
that, we have `walkDepth` implementations.

The funny thing is, all these implementations are very very similar,
even the kinds of trees are very similar. They are just objects with
`nodes: []` as children.

So this PR introduces a generic `walk` function that can work on all of
these trees.

There are also some situations where you need to go down and back up the
tree. For this reason, we added an `enter` and `exit` phase:

```ts
walk(ast, {
  enter(node, ctx) {},
  exit(node, ctx) {},
})
```

This means that you don't need to `walk(ast)` and later `walkDepth(ast)`
in case you wanted to do something _after_ visiting all nodes.

The API of these walk functions also slightly changed to fix some
problems we've had before. One is the `replaceWith` function. You could
technically call it multiple times, but that doesn't make sense so
instead you always have to return an explicit `WalkAction`. The
possibilities are:

```ts
// The ones we already had
WalkAction.Continue // Continue walking as normal, the default behavior
WalkAction.Skip // Skip walking the `nodes` of the current node
WalkAction.Stop // Stop the entire walk

// The new ones
WalkAction.Replace(newNode) // Replace the current node, and continue walking the new node(s)
WalkAction.ReplaceSkip(newNode) // Replace the current node, but don't walk the new node(s)
WalkAction.ReplaceStop(newNode) // Replace the current node, but stop the entire walk
```

To make sure that we can walk in both directions, and to make sure we
have proper control over when to walk which nodes, the `walk` function
is implemented in an iterative manner using a stack instead of
recursion.

This also means that a `WalkAction.Stop` or `WalkAction.ReplaceStop`
will immediately stop the walk, without unwinding the entire call stack.

Some notes: 
- The CSS AST does have `context` nodes, for this we can build up the
context lazily when we need it. I added a `cssContext(ctx)` that gives
you an enhanced context including the `context` object that you can read
information from.
- The second argument of the `walk` function can still be a normal
function, which is equivalent to `{ enter: fn }`.

Let's also take a look at some numbers. With this new implementation,
each `walk` is roughly ~1.3-1.5x faster than before. If you look at the
memory usage (especially in Bun) we go from `~2.2GB` peak memory usage,
to `~300mb` peak memory usage.

Some benchmarks on small and big trees (M1 Max):

<img width="2062" height="1438" alt="image"
src="https://github.com/user-attachments/assets/5ec8c22a-9de8-4e08-869a-18c0d30eb7e8"
/>

<img width="2062" height="1246" alt="image"
src="https://github.com/user-attachments/assets/e89d4b8e-29ca-4aee-8fd2-b7c043d3bbf4"
/>

We also ran some benchmarks on @thecrypticace's M3 Max:

<img width="1598" height="1452" alt="image"
src="https://github.com/user-attachments/assets/3b06b6fe-2497-4f24-a428-1a0e2af3896a"
/>

In node the memory difference isn't that big, but the performance itself
is still better:

<img width="2034" height="1586" alt="image"
src="https://github.com/user-attachments/assets/ef28ae14-b53e-4912-9621-531f3b02898f"
/>


In summary:

1. Single `walk` implementation for multiple use cases
2. Support for `enter` and `exit` phases
3. New `WalkAction` possibilities for better control
4. Overall better performance
5. ... and lower memory usage

## Test plan

1. All tests still pass (but had to adjust some of the APIs if `walk`
was used inside tests).
2. Added new tests for the `walk` implementation
3. Ran local benchmarks to verify the performance improvements
2025-10-15 21:28:12 +02:00
depfu[bot]
fc63ce7fbc
Update @vitejs/plugin-react 5.0.3 → 5.0.4 (patch) (#19129)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (5.0.3 → 5.0.4) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)



<details>
<summary>Release Notes</summary>

<h4>5.0.4 (from changelog)</h4>
<blockquote><h3 dir="auto">Perf: use native refresh wrapper plugin in
rolldown-vite (<a
href="https://bounce.depfu.com/github.com/vitejs/vite-plugin-react/pull/881">#881</a>)</h3></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please
let us know.</a></em></p>
</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>
2025-10-15 07:58:31 -04:00
Robin Malfait
de6b54c307
Fix parsing issue when \t is used in at-rules (#19130)
This PR fixes an issue where an at-rule that used `@name\tparams` didn't
properly parse because we didn't properly handle `\t`.

## Test plan

1. Added failing tests
2. Made them pass

Fixes: #19127
2025-10-15 11:50:02 +02:00
Robin Malfait
22858ac4ae
Make TypeScript a bit more happy (#19124)
While working on another PR, I noticed that some files had missing
properties and made TypeScript unhappy. Let's make TypeScript happy
again...
2025-10-14 19:52:46 +00:00
depfu[bot]
c7f6303070 Update semver to version 7.7.3 2025-10-14 18:22:58 +00:00
depfu[bot]
28c72314fc
Update @types/react 19.1.13 → 19.2.2 (minor) (#19116)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.1.13 → 19.2.2) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-10-14 06:41:33 -04:00
Jordan Pittman
f945c3d29f
Don’t index into strings with the theme(…) function (#19111)
Indexing into a string is only ever going to produce a single character
and is almost guaranteed to be a mistake. The legacy keypath notation is
meant to traverse objects and arrays — not strings.

Fixes #19104
2025-10-13 11:44:33 -04:00
Jordan Pittman
2cba8319ce
Clone AST nodes used in staticValues (#19110)
These were getting mutated but they were shared instead of being
re-created for new candidates. Cloning the nodes fixes this so mutation
of the AST nodes doesn’t stick around.

Fixes #19108
2025-10-13 10:23:09 -04:00
depfu[bot]
249bed0916
Update @napi-rs/cli 3.2.0 → 3.3.0 (minor) (#19102)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/cli (3.2.0 → 3.3.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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-10-10 12:44:16 -04:00
Robin Malfait
c67c0c54cf
Canonicalize dimensions (#19101) 2025-10-10 17:44:53 +02:00
depfu[bot]
496a1e9362
Update @types/react-dom 19.1.9 → 19.2.1 (minor) (#19088)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react-dom (19.1.9 → 19.2.1) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-10-09 14:58:37 -04:00
depfu[bot]
53a8d3bc90
Update all of react 19.1.1 → 19.2.0 (minor) (#19087)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ react (19.1.1 → 19.2.0) ·
[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.0">19.2.0</a></h4>

<blockquote><p dir="auto">Below is a list of all new features, APIs, and
bug fixes.</p>
<p dir="auto">Read the <a
href="https://react.dev/blog/2025/10/01/react-19-2">React 19.2 release
post</a> for more information.</p>
<h2 dir="auto">New React Features</h2>
<ul dir="auto">
<li>
<a href="https://react.dev/reference/react/Activity"><code
class="notranslate">&lt;Activity&gt;</code></a>: A new API to hide and
restore the UI and internal state of its children.</li>
<li>
<a href="https://react.dev/reference/react/useEffectEvent"><code
class="notranslate">useEffectEvent</code></a> is a React Hook that lets
you extract non-reactive logic into an <a
href="https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event">Effect
Event</a>.</li>
<li>
<a href="https://react.dev/reference/react/cacheSignal"><code
class="notranslate">cacheSignal</code></a> (for RSCs) lets your know
when the <code class="notranslate">cache()</code> lifetime is over.</li>
<li>
<a
href="https://react.dev/reference/developer-tooling/react-performance-tracks">React
Performance tracks</a> appear on the Performance panel’s timeline in
your browser developer tools</li>
</ul>
<h2 dir="auto">New React DOM Features</h2>
<ul dir="auto">
<li>Added resume APIs for partial pre-rendering with Web Streams:
<ul dir="auto">
<li>
<a href="https://react.dev/reference/react-dom/server/resume"><code
class="notranslate">resume</code></a>: to resume a prerender to a
stream.</li>
<li>
<a
href="https://react.dev/reference/react-dom/static/resumeAndPrerender"><code
class="notranslate">resumeAndPrerender</code></a>: to resume a prerender
to HTML.</li>
</ul>
</li>
<li>Added resume APIs for partial pre-rendering with Node Streams:
<ul dir="auto">
<li>
<a
href="https://react.dev/reference/react-dom/server/resumeToPipeableStream"><code
class="notranslate">resumeToPipeableStream</code></a>: to resume a
prerender to a stream.</li>
<li>
<a
href="https://react.dev/reference/react-dom/static/resumeAndPrerenderToNodeStream"><code
class="notranslate">resumeAndPrerenderToNodeStream</code></a>: to resume
a prerender to HTML.</li>
</ul>
</li>
<li>Updated <a
href="https://react.dev/reference/react-dom/static/prerender"><code
class="notranslate">prerender</code></a> APIs to return a <code
class="notranslate">postponed</code> state that can be passed to the
<code class="notranslate">resume</code> APIs.</li>
</ul>
<h2 dir="auto">Notable changes</h2>
<ul dir="auto">
<li>React DOM now batches suspense boundary reveals, matching the
behavior of client side rendering. This change is especially noticeable
when animating the reveal of Suspense boundaries e.g. with the upcoming
<code class="notranslate">&lt;ViewTransition&gt;</code> Component. React
will batch as much reveals as possible before the first paint while
trying to hit popular first-contentful paint metrics.</li>
<li>Add Node Web Streams (<code class="notranslate">prerender</code>,
<code class="notranslate">renderToReadableStream</code>) to
server-side-rendering APIs for Node.js</li>
<li>Use underscore instead of <code class="notranslate">:</code> IDs
generated by useId</li>
</ul>
<h2 dir="auto">All Changes</h2>
<h3 dir="auto">React</h3>
<ul dir="auto">
<li>
<code class="notranslate">&lt;Activity /&gt;</code> was developed over
many years, starting before <code
class="notranslate">ClassComponent.setState</code> (<a
href="https://bounce.depfu.com/github.com/acdlite">@acdlite</a> <a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
and many others)</li>
<li>Stringify context as "SomeContext" instead of "SomeContext.Provider"
(<a href="https://bounce.depfu.com/github.com/kassens">@kassens</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33507">#33507</a>)</li>
<li>Include stack of cause of React instrumentation errors with <code
class="notranslate">%o</code> placeholder (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34198">#34198</a>)</li>
<li>Fix infinite <code class="notranslate">useDeferredValue</code> loop
in popstate event (<a
href="https://bounce.depfu.com/github.com/acdlite">@acdlite</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32821">#32821</a>)</li>
<li>Fix a bug when an initial value was passed to <code
class="notranslate">useDeferredValue</code> (<a
href="https://bounce.depfu.com/github.com/acdlite">@acdlite</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34376">#34376</a>)</li>
<li>Fix a crash when submitting forms with Client Actions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33055">#33055</a>)</li>
<li>Hide/unhide the content of dehydrated suspense boundaries if they
resuspend (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32900">#32900</a>)</li>
<li>Avoid stack overflow on wide trees during Hot Reload (<a
href="https://bounce.depfu.com/github.com/sophiebits">@sophiebits</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34145">#34145</a>)</li>
<li>Improve Owner and Component stacks in various places (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>,
<a href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a>: <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33629">#33629</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33724">#33724</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32735">#32735</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33723">#33723</a>)</li>
<li>Add <code class="notranslate">cacheSignal</code> (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33557">#33557</a>)</li>
</ul>
<h3 dir="auto">React DOM</h3>
<ul dir="auto">
<li>Block on Suspensey Fonts during reveal of server-side-rendered
content (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33342">#33342</a>)</li>
<li>Use underscore instead of <code class="notranslate">:</code> for IDs
generated by <code class="notranslate">useId</code> (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>,
<a href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a>: <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32001">#32001</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33342">#33342</a><a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33099">#33099</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33422">#33422</a>)</li>
<li>Stop warning when ARIA 1.3 attributes are used (<a
href="https://bounce.depfu.com/github.com/Abdul-Omira">@Abdul-Omira</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34264">#34264</a>)</li>
<li>Allow <code class="notranslate">nonce</code> to be used on hoistable
styles (<a
href="https://bounce.depfu.com/github.com/Andarist">@Andarist</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32461">#32461</a>)</li>
<li>Warn for using a React owned node as a Container if it also has text
content (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32774">#32774</a>)</li>
<li>s/HTML/text for for error messages if text hydration mismatches (<a
href="https://bounce.depfu.com/github.com/rickhanlonii">@rickhanlonii</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32763">#32763</a>)</li>
<li>Fix a bug with <code class="notranslate">React.use</code> inside
<code class="notranslate">React.lazy</code>-ed Component (<a
href="https://bounce.depfu.com/github.com/hi-ogawa">@hi-ogawa</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33941">#33941</a>)</li>
<li>Enable the <code class="notranslate">progressiveChunkSize</code>
option for server-side-rendering APIs (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33027">#33027</a>)</li>
<li>Fix a bug with deeply nested Suspense inside Suspense fallback when
server-side-rendering (<a
href="https://bounce.depfu.com/github.com/gnoff">@gnoff</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33467">#33467</a>)</li>
<li>Avoid hanging when suspending after aborting while rendering (<a
href="https://bounce.depfu.com/github.com/gnoff">@gnoff</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34192">#34192</a>)</li>
<li>Add Node Web Streams to server-side-rendering APIs for Node.js (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33475">#33475</a>)</li>
</ul>
<h3 dir="auto">React Server Components</h3>
<ul dir="auto">
<li>Preload <code class="notranslate">&lt;img&gt;</code> and <code
class="notranslate">&lt;link&gt;</code> using hints before they're
rendered (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34604">#34604</a>)</li>
<li>Log error if production elements are rendered during development (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34189">#34189</a>)</li>
<li>Fix a bug when returning a Temporary reference (e.g. a Client
Reference) from Server Functions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34084">#34084</a>,
<a href="https://bounce.depfu.com/github.com/denk0403">@denk0403</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33761">#33761</a>)</li>
<li>Pass line/column to <code
class="notranslate">filterStackFrame</code> (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33707">#33707</a>)</li>
<li>Support Async Modules in Turbopack Server References (<a
href="https://bounce.depfu.com/github.com/lubieowoce">@lubieowoce</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34531">#34531</a>)</li>
<li>Add support for .mjs file extension in Webpack (<a
href="https://bounce.depfu.com/github.com/jennyscript">@jennyscript</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33028">#33028</a>)</li>
<li>Fix a wrong missing key warning (<a
href="https://bounce.depfu.com/github.com/unstubbable">@unstubbable</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34350">#34350</a>)</li>
<li>Make console log resolve in predictable order (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33665">#33665</a>)</li>
</ul>
<h3 dir="auto">React Reconciler</h3>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L255-L261">createContainer</a>
and <a
href="https://bounce.depfu.com/github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L305-L312">createHydrationContainer</a>
had their parameter order adjusted after <code
class="notranslate">on*</code> handlers to account for upcoming
experimental APIs</li>
</ul>
<h2 dir="auto">eslint-plugin-react-hooks@6.1.0</h2>
<p dir="auto"><strong>Note:</strong> Version 6.0.0 was mistakenly
released and immediately deprecated and untagged on npm. This is the
first official 6.x major release and includes breaking changes.</p>
<ul dir="auto">
<li>
<strong>Breaking:</strong> Require Node.js 18 or newer. (<a
href="https://bounce.depfu.com/github.com/michaelfaith">@michaelfaith</a>
in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32458">#32458</a>)</li>
<li>
<strong>Breaking:</strong> Flat config is now the default <code
class="notranslate">recommended</code> preset. Legacy config moved to
<code class="notranslate">recommended-legacy</code>. (<a
href="https://bounce.depfu.com/github.com/michaelfaith">@michaelfaith</a>
in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32457">#32457</a>)</li>
<li>
<strong>New Violations:</strong> Disallow calling <code
class="notranslate">use</code> within try/catch blocks. (<a
href="https://bounce.depfu.com/github.com/poteto">@poteto</a> in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34040">#34040</a>)</li>
<li>
<strong>New Violations:</strong> Disallow calling <code
class="notranslate">useEffectEvent</code> functions in arbitrary
closures. (<a
href="https://bounce.depfu.com/github.com/jbrown215">@jbrown215</a> in
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33544">#33544</a>)</li>
<li>Handle <code class="notranslate">React.useEffect</code> in addition
to <code class="notranslate">useEffect</code> in rules-of-hooks. (<a
href="https://bounce.depfu.com/github.com/Ayc0">@Ayc0</a> in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34076">#34076</a>)</li>
<li>Added <code class="notranslate">react-hooks</code> settings config
option that to accept <code
class="notranslate">additionalEffectHooks</code> that are used across
exhaustive-deps and rules-of-hooks rules. (<a
href="https://bounce.depfu.com/github.com/jbrown215">@jbrown215</a>) in
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34497">#34497</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="02ef495809...ae74234eae">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</details>




#### ✳️ react-dom (19.1.1 → 19.2.0) ·
[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.0">19.2.0</a></h4>

<blockquote><p dir="auto">Below is a list of all new features, APIs, and
bug fixes.</p>
<p dir="auto">Read the <a
href="https://react.dev/blog/2025/10/01/react-19-2">React 19.2 release
post</a> for more information.</p>
<h2 dir="auto">New React Features</h2>
<ul dir="auto">
<li>
<a href="https://react.dev/reference/react/Activity"><code
class="notranslate">&lt;Activity&gt;</code></a>: A new API to hide and
restore the UI and internal state of its children.</li>
<li>
<a href="https://react.dev/reference/react/useEffectEvent"><code
class="notranslate">useEffectEvent</code></a> is a React Hook that lets
you extract non-reactive logic into an <a
href="https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event">Effect
Event</a>.</li>
<li>
<a href="https://react.dev/reference/react/cacheSignal"><code
class="notranslate">cacheSignal</code></a> (for RSCs) lets your know
when the <code class="notranslate">cache()</code> lifetime is over.</li>
<li>
<a
href="https://react.dev/reference/developer-tooling/react-performance-tracks">React
Performance tracks</a> appear on the Performance panel’s timeline in
your browser developer tools</li>
</ul>
<h2 dir="auto">New React DOM Features</h2>
<ul dir="auto">
<li>Added resume APIs for partial pre-rendering with Web Streams:
<ul dir="auto">
<li>
<a href="https://react.dev/reference/react-dom/server/resume"><code
class="notranslate">resume</code></a>: to resume a prerender to a
stream.</li>
<li>
<a
href="https://react.dev/reference/react-dom/static/resumeAndPrerender"><code
class="notranslate">resumeAndPrerender</code></a>: to resume a prerender
to HTML.</li>
</ul>
</li>
<li>Added resume APIs for partial pre-rendering with Node Streams:
<ul dir="auto">
<li>
<a
href="https://react.dev/reference/react-dom/server/resumeToPipeableStream"><code
class="notranslate">resumeToPipeableStream</code></a>: to resume a
prerender to a stream.</li>
<li>
<a
href="https://react.dev/reference/react-dom/static/resumeAndPrerenderToNodeStream"><code
class="notranslate">resumeAndPrerenderToNodeStream</code></a>: to resume
a prerender to HTML.</li>
</ul>
</li>
<li>Updated <a
href="https://react.dev/reference/react-dom/static/prerender"><code
class="notranslate">prerender</code></a> APIs to return a <code
class="notranslate">postponed</code> state that can be passed to the
<code class="notranslate">resume</code> APIs.</li>
</ul>
<h2 dir="auto">Notable changes</h2>
<ul dir="auto">
<li>React DOM now batches suspense boundary reveals, matching the
behavior of client side rendering. This change is especially noticeable
when animating the reveal of Suspense boundaries e.g. with the upcoming
<code class="notranslate">&lt;ViewTransition&gt;</code> Component. React
will batch as much reveals as possible before the first paint while
trying to hit popular first-contentful paint metrics.</li>
<li>Add Node Web Streams (<code class="notranslate">prerender</code>,
<code class="notranslate">renderToReadableStream</code>) to
server-side-rendering APIs for Node.js</li>
<li>Use underscore instead of <code class="notranslate">:</code> IDs
generated by useId</li>
</ul>
<h2 dir="auto">All Changes</h2>
<h3 dir="auto">React</h3>
<ul dir="auto">
<li>
<code class="notranslate">&lt;Activity /&gt;</code> was developed over
many years, starting before <code
class="notranslate">ClassComponent.setState</code> (<a
href="https://bounce.depfu.com/github.com/acdlite">@acdlite</a> <a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
and many others)</li>
<li>Stringify context as "SomeContext" instead of "SomeContext.Provider"
(<a href="https://bounce.depfu.com/github.com/kassens">@kassens</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33507">#33507</a>)</li>
<li>Include stack of cause of React instrumentation errors with <code
class="notranslate">%o</code> placeholder (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34198">#34198</a>)</li>
<li>Fix infinite <code class="notranslate">useDeferredValue</code> loop
in popstate event (<a
href="https://bounce.depfu.com/github.com/acdlite">@acdlite</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32821">#32821</a>)</li>
<li>Fix a bug when an initial value was passed to <code
class="notranslate">useDeferredValue</code> (<a
href="https://bounce.depfu.com/github.com/acdlite">@acdlite</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34376">#34376</a>)</li>
<li>Fix a crash when submitting forms with Client Actions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33055">#33055</a>)</li>
<li>Hide/unhide the content of dehydrated suspense boundaries if they
resuspend (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32900">#32900</a>)</li>
<li>Avoid stack overflow on wide trees during Hot Reload (<a
href="https://bounce.depfu.com/github.com/sophiebits">@sophiebits</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34145">#34145</a>)</li>
<li>Improve Owner and Component stacks in various places (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>,
<a href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a>: <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33629">#33629</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33724">#33724</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32735">#32735</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33723">#33723</a>)</li>
<li>Add <code class="notranslate">cacheSignal</code> (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33557">#33557</a>)</li>
</ul>
<h3 dir="auto">React DOM</h3>
<ul dir="auto">
<li>Block on Suspensey Fonts during reveal of server-side-rendered
content (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33342">#33342</a>)</li>
<li>Use underscore instead of <code class="notranslate">:</code> for IDs
generated by <code class="notranslate">useId</code> (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>,
<a href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a>: <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32001">#32001</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33342">#33342</a><a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33099">#33099</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33422">#33422</a>)</li>
<li>Stop warning when ARIA 1.3 attributes are used (<a
href="https://bounce.depfu.com/github.com/Abdul-Omira">@Abdul-Omira</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34264">#34264</a>)</li>
<li>Allow <code class="notranslate">nonce</code> to be used on hoistable
styles (<a
href="https://bounce.depfu.com/github.com/Andarist">@Andarist</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32461">#32461</a>)</li>
<li>Warn for using a React owned node as a Container if it also has text
content (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32774">#32774</a>)</li>
<li>s/HTML/text for for error messages if text hydration mismatches (<a
href="https://bounce.depfu.com/github.com/rickhanlonii">@rickhanlonii</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32763">#32763</a>)</li>
<li>Fix a bug with <code class="notranslate">React.use</code> inside
<code class="notranslate">React.lazy</code>-ed Component (<a
href="https://bounce.depfu.com/github.com/hi-ogawa">@hi-ogawa</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33941">#33941</a>)</li>
<li>Enable the <code class="notranslate">progressiveChunkSize</code>
option for server-side-rendering APIs (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33027">#33027</a>)</li>
<li>Fix a bug with deeply nested Suspense inside Suspense fallback when
server-side-rendering (<a
href="https://bounce.depfu.com/github.com/gnoff">@gnoff</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33467">#33467</a>)</li>
<li>Avoid hanging when suspending after aborting while rendering (<a
href="https://bounce.depfu.com/github.com/gnoff">@gnoff</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34192">#34192</a>)</li>
<li>Add Node Web Streams to server-side-rendering APIs for Node.js (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33475">#33475</a>)</li>
</ul>
<h3 dir="auto">React Server Components</h3>
<ul dir="auto">
<li>Preload <code class="notranslate">&lt;img&gt;</code> and <code
class="notranslate">&lt;link&gt;</code> using hints before they're
rendered (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34604">#34604</a>)</li>
<li>Log error if production elements are rendered during development (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34189">#34189</a>)</li>
<li>Fix a bug when returning a Temporary reference (e.g. a Client
Reference) from Server Functions (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34084">#34084</a>,
<a href="https://bounce.depfu.com/github.com/denk0403">@denk0403</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33761">#33761</a>)</li>
<li>Pass line/column to <code
class="notranslate">filterStackFrame</code> (<a
href="https://bounce.depfu.com/github.com/eps1lon">@eps1lon</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33707">#33707</a>)</li>
<li>Support Async Modules in Turbopack Server References (<a
href="https://bounce.depfu.com/github.com/lubieowoce">@lubieowoce</a> <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34531">#34531</a>)</li>
<li>Add support for .mjs file extension in Webpack (<a
href="https://bounce.depfu.com/github.com/jennyscript">@jennyscript</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33028">#33028</a>)</li>
<li>Fix a wrong missing key warning (<a
href="https://bounce.depfu.com/github.com/unstubbable">@unstubbable</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34350">#34350</a>)</li>
<li>Make console log resolve in predictable order (<a
href="https://bounce.depfu.com/github.com/sebmarkbage">@sebmarkbage</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33665">#33665</a>)</li>
</ul>
<h3 dir="auto">React Reconciler</h3>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L255-L261">createContainer</a>
and <a
href="https://bounce.depfu.com/github.com/facebook/react/blob/v19.2.0/packages/react-reconciler/src/ReactFiberReconciler.js#L305-L312">createHydrationContainer</a>
had their parameter order adjusted after <code
class="notranslate">on*</code> handlers to account for upcoming
experimental APIs</li>
</ul>
<h2 dir="auto">eslint-plugin-react-hooks@6.1.0</h2>
<p dir="auto"><strong>Note:</strong> Version 6.0.0 was mistakenly
released and immediately deprecated and untagged on npm. This is the
first official 6.x major release and includes breaking changes.</p>
<ul dir="auto">
<li>
<strong>Breaking:</strong> Require Node.js 18 or newer. (<a
href="https://bounce.depfu.com/github.com/michaelfaith">@michaelfaith</a>
in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32458">#32458</a>)</li>
<li>
<strong>Breaking:</strong> Flat config is now the default <code
class="notranslate">recommended</code> preset. Legacy config moved to
<code class="notranslate">recommended-legacy</code>. (<a
href="https://bounce.depfu.com/github.com/michaelfaith">@michaelfaith</a>
in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32457">#32457</a>)</li>
<li>
<strong>New Violations:</strong> Disallow calling <code
class="notranslate">use</code> within try/catch blocks. (<a
href="https://bounce.depfu.com/github.com/poteto">@poteto</a> in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34040">#34040</a>)</li>
<li>
<strong>New Violations:</strong> Disallow calling <code
class="notranslate">useEffectEvent</code> functions in arbitrary
closures. (<a
href="https://bounce.depfu.com/github.com/jbrown215">@jbrown215</a> in
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33544">#33544</a>)</li>
<li>Handle <code class="notranslate">React.useEffect</code> in addition
to <code class="notranslate">useEffect</code> in rules-of-hooks. (<a
href="https://bounce.depfu.com/github.com/Ayc0">@Ayc0</a> in <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34076">#34076</a>)</li>
<li>Added <code class="notranslate">react-hooks</code> settings config
option that to accept <code
class="notranslate">additionalEffectHooks</code> that are used across
exhaustive-deps and rules-of-hooks rules. (<a
href="https://bounce.depfu.com/github.com/jbrown215">@jbrown215</a>) in
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/34497">#34497</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="02ef495809...ae74234eae">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-10-09 14:33:23 -04:00
Robin Malfait
01d1e98259
Canonicalization constant folding and handling zeros (#19095)
The main goal of this PR was to support canonicalization of zero like
values. We essentially want to canonicalize `-mt-0` as `mt-0`, but also
`mt-[0px]`, `mt-[0rem]`, and other length-like units to just `mt-0`.

To do this, we had to handle 2 things:

1. We introduced some more constant folding, including making `0px` and
`0rem` fold to `0`. We only do this for length units. We also normalize
`-0`, `+0`, `-0.0` and so on to `0`.
2. While pre-computing utilities in our lookup table, we make sure that
we prefer `mt-0` over `-mt-0` if both result in the same signature.

Moved some of the constant folding logic into its own function and added
a bunch of separate tests for it.

## Test plan

Added more unit tests where we normalize different zero-like values to
`0`.

Running the canonicalization logic:
```js
designSystem.canonicalizeCandidates([
  '-m-0',
  '-m-[-0px]',
  '-m-[-0rem]',
  '-m-[0px]',
  '-m-[0rem]',
  'm-0',
  'm-[-0px]',
  'm-[-0rem]',
  'm-[0px]',
  'm-[0rem]',
  'm-[calc(var(--spacing)*0)]',
  'm-[--spacing(0)]',
  'm-[--spacing(0.0)]',
  'm-[+0]',
  'm-[-0]',
  '-m-[-0]',
  '-m-[+0]',
]) // → ['m-0']
```

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-10-09 18:09:29 +00:00
Robin Malfait
3aadba7cc1
Move modifier of not-*, has-*, and in-* variant to sub variant (#19100)
This PR fixes an issue where a compound variant with a modifier such as
`not-group-hover/name:flex` would not generate anything because the
`/name` modifier belongs to the `not` variant, and not the compounded
`group-hover` variant.

This PR is a **workaround** (and definitely not perfect) by special
casing the `not`, `has`, and `in` variants such that their modifiers are
moved internally to the sub variant as-if the `/name` existed on
`group-hover`.

We don't do it for other compound variants such as `group` and `peer`
because then `group-peer-focus/name:underline` would result in a
breaking change:

```diff
- .group-peer-focus\\/name\\:flex:is(:where(.group\\/name):is(:where(.peer):focus ~ *) *)
+ .group-peer-focus\/name\:flex:is(:where(.group):is(:where(.peer\/name):focus ~ *) *) 
```

In case the diff is not clear, the name has moved:
<img width="1219" height="78" alt="image"
src="https://github.com/user-attachments/assets/dce7bc95-9d93-452d-a275-b3891a05a1a4"
/>


This is also a limited workaround, because if you need multiple
modifiers it won't work. I would've loved to special case this _inside_
the `not`, `has`, and `in` code that handles these variants, but we
handle the variants in a depth-first way, so by the time you are
handling the `not` variant, the sub variant was already handled...

In a perfect world, you can use something like `not-group/name-hover`
but then it becomes unambiguous because is `name` the name, is
`name-hover`?

## Test plan

Added a new test that wouldn't generate anything before this fix.

Fixes: #15772
2025-10-09 19:30:01 +02:00
Robin Malfait
0c14df1a15
Fix resolving colors via theme(…) in compat mode with nested objects (#19097)
This PR fixes an issue when loading (nested) colors from a config file
and later referencing it via the `theme(…)` function in CSS.

Given a config like this:

```js
module.exports = {
  theme: {
    colors: {
      foo: 'var(--foo-foo)',
      'foo-bar': 'var(--foo-foo-bar)',
    },
  },
}
```

We internally map this into the design system. The issue here is that
the `foo` and `foo-bar` are overlapping and it behaves more like this:

```js
{
  foo: {
    DEFAULT: 'var(--foo-foo)',
    bar: 'var(--foo-foo-bar)'
  },
}
```

So while we can easily resolve `colors.foo-bar`, the `colors.foo` would
result in the object with a `DEFAULT` key. This PR solves that by using
the `DEFAULT` key if we end up with an object that has it.

If you end up resolving an object (`theme(colors)`) then the behavior is
unchanged.


## Test plan

1. Added a test based on the config in the issue (which failed before
this fix).
2. Also simplified the test case after identifying the problem (with the
`DEFAULT` key).

Fixes: #19091
2025-10-09 17:10:32 +00:00
Robin Malfait
561983d7e5
Suppress warnings when using :deep, :slotted and :global (#19094)
This PR ignores warnings related to `:deep`, `:slotted` and `:global`
used by frameworks like Vue (see:
https://vuejs.org/api/sfc-css-features#deep-selectors).

## Test plan

Used a `:deep()` selector in a test project (Catalyst).

```diff
diff --git a/templates/catalyst/src/tailwind.css b/templates/catalyst/src/tailwind.css
index 79887e67..2acb749c 100644
--- a/templates/catalyst/src/tailwind.css
+++ b/templates/catalyst/src/tailwind.css
@@ -9,3 +9,7 @@
   --font-sans: Inter, sans-serif;
   --font-sans--font-feature-settings: 'cv11';
 }
+
+:deep(.foo) {
+  color: red;
+}
```

Before:

<img width="1625" height="372" alt="image"
src="https://github.com/user-attachments/assets/4b948080-1aeb-41ba-8268-98828da21768"
/>


After:

<img width="717" height="114" alt="image"
src="https://github.com/user-attachments/assets/b8668da2-693e-4010-99fb-de3bc4a47bf9"
/>


Fixes:
https://github.com/tailwindlabs/tailwindcss/pull/18918#issuecomment-3384928613
2025-10-09 11:58:51 +02:00
Robin Malfait
0c8d881f0e
Improve percentage canonicalization (#19072)
This PR improves the canonicalization of percentage values such that
`[.1]`, `[.10]`, `[10%]` and `[10.0%]` are all treated as the same
value.

Right now we're only focusing on percentages. We can likely do this for
all numbers, but I'm a little afraid of places where you can have
multiple numbers separated by multiple dots (think SVGs).

## Test plan

1. Added more tests to cover the new cases.
2. Tested it in a local test project, where you can see the
normalization in action.

<img width="1383" height="117" alt="image"
src="https://github.com/user-attachments/assets/03d99e3a-4404-437b-b458-58f7e8ce60da"
/>
2025-10-07 09:47:22 -04:00
Robin Malfait
efe084b7e7
Improve performance of cloning AST nodes (#19067)
This PR improves the performance of when we need to clone some AST
nodes. We have a few places where we clone `Candidate`, `Variant` and
CSS `AST` nodes.

Right now we use `structuredClone`, which works, but it is a generic
solution. However, we do know the exact structure of these AST nodes, so
we can write specialized clone functions that are much faster.

## Test plan

1. All the tests still pass with this change
2. The performance is better:

```
  cloneCandidate - src/candidate.bench.ts > Candidate cloning
    1.72x faster than cloneCandidate (spread)
    74.03x faster than structuredClone

  cloneAstNode() - src/ast.bench.ts > Cloning AST nodes
    1.15x faster than cloneAstNode (with spread)
    33.54x faster than structuredClone()
```

Ready for review, but should be merged after #19059
2025-10-07 10:46:37 +00:00
Robin Malfait
b77971f754
Introduce canonicalizeCandidates on the internal Design System (#19059)
This PR introduces a new `canonicalizeCandidates` function on the
internal Design System.

The big motivation to moving this to the core `tailwindcss` package is
that we can use this in various places:

- The Raycast extension
- The VS Code extension / language server
- 3rd party tools that use the Tailwind CSS design system APIs

> This PR looks very big, but **I think it's best to go over the changes
commit by commit**. Basically all of these steps already existed in the
upgrade tool, but are now moved to our core `tailwindcss` package.

Here is a list of all the changes:
- Added a new `canonicalizeCandidates` function to the design system
- Moved various migration steps to the core package. I inlined them in
the same file and because of that I noticed a specific pattern (more on
this later).
- Moved `printCandidate` tests to the `tailwindcss` package
- Setup tests for `canonicalizeCandidates` based on the existing tests
in the upgrade tool.

I noticed that all the migrations followed a specific pattern:

1. Parse the raw candidate into a `Candidate[]` AST
2. In a loop, try to migrate the `Candidate` to a new `Candidate` (this
often handled both the `Candidate` and its `Variant[]`)
3. If something changed, print the new `Candidate` back to a string, and
pass it to the next migration step.

While this makes sense in isolation, we are doing a lot of repeated work
by parsing, modifying, and printing the candidate multiple times. This
let me to introduce the `big refactor` commit. This changes the steps
to:

1. Up front, parse the raw candidate into a `Candidate[]` _once_.
2. Strip the variants and the important marker from the candidate. This
means that each migration step only has to deal with the base `utility`
and not care about the variants or the important marker. We can
re-attach these afterwards.
3. Instead of a `rawCandidate: string`, each migration step receives an
actual `Candidate` object (or a `Variant` object).
4. I also split up the migration steps for the `Candidate` and the
`Variant[]`.

All of this means that there is a lot less work that needs to be done.
We can also cache results between migrations. So `[@media_print]:flex`
and `[@media_print]:block` will result in `print:flex` and `print:block`
respectively, but the `[@media_print]` part is only migrated once across
both candidates.

One migration step relied on the `postcss-selector-parser` package to
parse selectors and attribute selectors. I didn't want to introduce a
package just for this, so instead used our own `SelectorParser` in the
migration and wrote a small `AttributeSelectorParser` that can parse the
attribute selector into a little data structure we can work with
instead.

If we want, we can split this PR up into smaller pieces, but since the
biggest chunk is moving existing code around, I think it's fairly doable
to review as long as you go commit by commit.

---

With this new API, we can turn:
```
[
  'bg-red-500',
  'hover:bg-red-500',
  '[@media_print]:bg-red-500',
  'hover:[@media_print]:bg-red-500',
  'bg-red-500/100',
  'hover:bg-red-500/100',
  '[@media_print]:bg-red-500/100',
  'hover:[@media_print]:bg-red-500/100',
  'bg-[var(--color-red-500)]',
  'hover:bg-[var(--color-red-500)]',
  '[@media_print]:bg-[var(--color-red-500)]',
  'hover:[@media_print]:bg-[var(--color-red-500)]',
  'bg-[var(--color-red-500)]/100',
  'hover:bg-[var(--color-red-500)]/100',
  '[@media_print]:bg-[var(--color-red-500)]/100',
  'hover:[@media_print]:bg-[var(--color-red-500)]/100',
  'bg-(--color-red-500)',
  'hover:bg-(--color-red-500)',
  '[@media_print]:bg-(--color-red-500)',
  'hover:[@media_print]:bg-(--color-red-500)',
  'bg-(--color-red-500)/100',
  'hover:bg-(--color-red-500)/100',
  '[@media_print]:bg-(--color-red-500)/100',
  'hover:[@media_print]:bg-(--color-red-500)/100',
  'bg-[color:var(--color-red-500)]',
  'hover:bg-[color:var(--color-red-500)]',
  '[@media_print]:bg-[color:var(--color-red-500)]',
  'hover:[@media_print]:bg-[color:var(--color-red-500)]',
  'bg-[color:var(--color-red-500)]/100',
  'hover:bg-[color:var(--color-red-500)]/100',
  '[@media_print]:bg-[color:var(--color-red-500)]/100',
  'hover:[@media_print]:bg-[color:var(--color-red-500)]/100',
  'bg-(color:--color-red-500)',
  'hover:bg-(color:--color-red-500)',
  '[@media_print]:bg-(color:--color-red-500)',
  'hover:[@media_print]:bg-(color:--color-red-500)',
  'bg-(color:--color-red-500)/100',
  'hover:bg-(color:--color-red-500)/100',
  '[@media_print]:bg-(color:--color-red-500)/100',
  'hover:[@media_print]:bg-(color:--color-red-500)/100',
  '[background-color:var(--color-red-500)]',
  'hover:[background-color:var(--color-red-500)]',
  '[@media_print]:[background-color:var(--color-red-500)]',
  'hover:[@media_print]:[background-color:var(--color-red-500)]',
  '[background-color:var(--color-red-500)]/100',
  'hover:[background-color:var(--color-red-500)]/100',
  '[@media_print]:[background-color:var(--color-red-500)]/100',
  'hover:[@media_print]:[background-color:var(--color-red-500)]/100'
]
```

Into their canonicalized form:
```
[
  'bg-red-500',
  'hover:bg-red-500',
  'print:bg-red-500',
  'hover:print:bg-red-500'
]
```

The list is also unique, so we won't end up with `bg-red-500 bg-red-500`
twice.

While the canonicalization itself is fairly fast, we still pay a **~1s**
startup cost for some migrations (once, and cached for the entire
lifetime of the design system). I would like to keep improving the
performance and the kinds of migrations we do, but I think this is a
good start.

The cost we pay is for:

1. Generating a full list of all possible utilities based on the
`getClassList` suggestions API.
2. Generating a full list of all possible variants.

The canonicalization step for this list takes **~2.9ms** on my machine.

Just for fun, if you use the `getClassList` API for intellisense that
generates all the suggestions and their modifiers, you get a list of
**263788** classes. If you canonicalize all of these, it takes
**~500ms** in total. So roughly **~1.9μs** per candidate.

This new API doesn't result in a performance difference for normal
Tailwind CSS builds.

The other potential concern is file size of the package. The generated
`tailwindcss.tgz` file changed like this:
```diff
- 684652 bytes (684.65 kB)
+ 749169 bytes (749.17 kB)
```
So the package increased by ~65 kB which I don't think is the end of the
world, but it is important for the `@tailwindcss/browser` build which we
don't want to grow unnecessarily. For this reason we remove some of the
code for the design system conditionally such that you don't pay this
cost in an environment where you will never need this API.

The `@tailwindcss/browser` build looks like this:
```shell
`dist/index.global.js 255.14 KB` (main)
`dist/index.global.js 272.61 KB` (before this change)
`dist/index.global.js 252.83 KB` (after this change, even smaller than on `main`)
```
2025-10-07 12:41:12 +02:00
Robin Malfait
73628f69f3
Fix Safari devtools issues because of nested @supports at-rules without normal rule (#19069)
This PR fixes a weird Safari rendering bug in the devtools. This seems
to be happening when using `@supports`, especially nested `@supports`
at-rules.

The issue is that our color-mix fallback generates declarations directly
in `@supports` at-rules which causes the weird rendering bug in Safari.

Adding this intermediate `&` rule seems to fix the issue.

This is a workaround for a browser bug, but the additional 3 characters
shouldn't be the end of the world.

## Test plan

1. Updated the tests with the new `& { }` intermediate rule
2. Other tests still pass as expected

| Before | After |
| --- | --- |
| <img width="450" height="549" alt="image"
src="https://github.com/user-attachments/assets/4b51fb93-8073-4414-8139-dec75e6bc086"
/> | <img width="448" height="548" alt="image"
src="https://github.com/user-attachments/assets/1016af67-c1eb-43dc-9554-158e7e2264c4"
/> |

Fixes: #19065

[ci-all]
2025-10-06 20:04:40 +02:00
Robin Malfait
49948d087e
Fix failing integration tests (#19068)
The vite/nuxt integration tests started failing because one of the
internal dependencies (`nuxi`) was updated from `3.28.0` to `3.29.0`
which includes a newer version of `undici` which in turn relies on
`node:sqlite`.

`node:sqlite` was added in a newer Node version, and we still use Node
v20 in CI.

This PR pins `nuxi` to `3.28.0` until we can upgrade our Node version in
CI.

[ci-all]
2025-10-06 15:46:25 +00:00
Robin Malfait
86e4b8e636
run prettier 2025-10-06 17:15:57 +02:00
Philipp Spiess
231e17b84a Change permissions so issue-triage can run for everyone 2025-10-06 16:20:09 +02:00
Philipp Spiess
4cbdfa6e43 Issue triage tweaks 2025-10-06 12:37:20 +02:00
Philipp Spiess
9ef314e786
Add agentic workflow issue-triage (#19057)
This PR attempts to set up an automatic issue triaging tool using Claude
Code as the agent and GitHub Agentics as the framework:
https://githubnext.github.io/gh-aw/start-here/quick-start/

Based on the `safe-outputs` setting, this agent can only label issues or
write replies. Let's see how useful that is 🤷

You can't run it locally since it does not have a manual trigger and
needs a new github issue instead so my plan is to merge it and then
create some super legit issues to see if it works!

## Test plan

```
α dev/tailwindcss (add-workflow-workflows-issue-triage.md-3051) gh aw status
Workflow Status
Name         | Installed | Up-to-date | Status  | Time Remaining
------------ | --------- | ---------- | ------- | --------------
issue-triage | Yes       | Yes        | Unknown | 29d 22h
```

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-10-06 12:24:51 +02:00
depfu[bot]
6802c685c9
Update globby 14.1.0 → 15.0.0 (major) (#19056)
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?




#### ✳️ globby (14.1.0 → 15.0.0) ·
[Repo](https://github.com/sindresorhus/globby)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/sindresorhus/globby/releases/tag/v15.0.0">15.0.0</a></h4>

<blockquote><h3 dir="auto">Breaking</h3>
<ul dir="auto">
<li>Require Node.js 20 <a
href="4ae42c8bbd"><tt>4ae42c8</tt></a>
</li>
</ul>
<h3 dir="auto">Fixes</h3>
<ul dir="auto">
<li>Fix <code class="notranslate">expandDirectories</code> not working
with globstar patterns <a
href="98d691ad73"><tt>98d691a</tt></a>
</li>
<li>Fix relative paths with gitignore option <a
href="b4d78d8039"><tt>b4d78d8</tt></a>
</li>
<li>Fix gitignore patterns in subdirectories not applying recursively <a
href="2cb6088a0a"><tt>2cb6088</tt></a>
</li>
<li>Fix TypeScript types for <code
class="notranslate">globbyStream</code> to correctly yield strings <a
href="1c7f3ed116"><tt>1c7f3ed</tt></a>
</li>
</ul>
<hr>
<p dir="auto"><a
href="https://bounce.depfu.com/github.com/sindresorhus/globby/compare/v14.1.0...v15.0.0"><tt>v14.1.0...v15.0.0</tt></a></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="60d7de5447...b65747ec17">See
the full diff on Github</a>. The new version differs by 7 commits:</p>
<ul>
<li><a
href="b65747ec17"><code>15.0.0</code></a></li>
<li><a
href="4ae42c8bbd"><code>Require
Node.js 20</code></a></li>
<li><a
href="98d691ad73"><code>Fix
`expandDirectories` not working with globstar patterns</code></a></li>
<li><a
href="b4d78d8039"><code>Fix
relative paths with gitignore option</code></a></li>
<li><a
href="2cb6088a0a"><code>Fix
gitignore patterns in subdirectories not applying
recursively</code></a></li>
<li><a
href="0a4df0eeed"><code>Document
performance implications of `gitignore` option</code></a></li>
<li><a
href="1c7f3ed116"><code>Fix
TypeScript types for `globbyStream` to correctly yield
strings</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>
2025-10-06 11:14:31 +02:00
depfu[bot]
573f633f13
Update eslint 9.35.0 → 9.36.0 (minor) (#19049)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.35.0 → 9.36.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.36.0">9.36.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="47afcf668d"><code
class="notranslate">47afcf6</code></a> feat: correct <code
class="notranslate">preserve-caught-error</code> edge cases (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20109">#20109</a>)
(Francesco Trotta)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="75b74d865d"><code
class="notranslate">75b74d8</code></a> fix: add missing rule option
types (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20127">#20127</a>)
(ntnyq)</li>
<li>
<a
href="1c0d85049e"><code
class="notranslate">1c0d850</code></a> fix: update <code
class="notranslate">eslint-all.js</code> to use <code
class="notranslate">Object.freeze</code> for <code
class="notranslate">rules</code> object (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20116">#20116</a>)
(루밀LuMir)</li>
<li>
<a
href="7d61b7fadc"><code
class="notranslate">7d61b7f</code></a> fix: add missing scope types to
<code class="notranslate">Scope.type</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20110">#20110</a>)
(Pixel998)</li>
<li>
<a
href="7a670c301b"><code
class="notranslate">7a670c3</code></a> fix: correct rule option typings
in <code class="notranslate">rules.d.ts</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20084">#20084</a>)
(Pixel998)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="b73ab12acd"><code
class="notranslate">b73ab12</code></a> docs: update examples to use
<code class="notranslate">defineConfig</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20131">#20131</a>)
(sethamus)</li>
<li>
<a
href="31d9392699"><code
class="notranslate">31d9392</code></a> docs: fix typos (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20118">#20118</a>)
(Pixel998)</li>
<li>
<a
href="c7f861b3f8"><code
class="notranslate">c7f861b</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="6b0c08b106"><code
class="notranslate">6b0c08b</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="91f97c5046"><code
class="notranslate">91f97c5</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="12411e8d45"><code
class="notranslate">12411e8</code></a> chore: upgrade @eslint/js@9.36.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20139">#20139</a>)
(Milos Djermanovic)</li>
<li>
<a
href="488cba6b39"><code
class="notranslate">488cba6</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="bac82a2a9c"><code
class="notranslate">bac82a2</code></a> ci: simplify renovate
configuration (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19907">#19907</a>)
(唯然)</li>
<li>
<a
href="c00bb37d62"><code
class="notranslate">c00bb37</code></a> ci: bump actions/labeler from 5
to 6 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20090">#20090</a>)
(dependabot[bot])</li>
<li>
<a
href="fee751dc8a"><code
class="notranslate">fee751d</code></a> refactor: use <code
class="notranslate">defaultOptions</code> in rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20121">#20121</a>)
(Pixel998)</li>
<li>
<a
href="1ace67d9f7"><code
class="notranslate">1ace67d</code></a> chore: update example to use
<code class="notranslate">defineConfig</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20111">#20111</a>)
(루밀LuMir)</li>
<li>
<a
href="4821963bf7"><code
class="notranslate">4821963</code></a> test: add missing loc information
to error objects in rule tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20112">#20112</a>)
(루밀LuMir)</li>
<li>
<a
href="b42c42e7cd"><code
class="notranslate">b42c42e</code></a> chore: disallow use of deprecated
<code class="notranslate">type</code> property in core rule tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20094">#20094</a>)
(Milos Djermanovic)</li>
<li>
<a
href="7bb498d720"><code
class="notranslate">7bb498d</code></a> test: remove deprecated <code
class="notranslate">type</code> property from core rule tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20093">#20093</a>)
(Pixel998)</li>
<li>
<a
href="e10cf2ab42"><code
class="notranslate">e10cf2a</code></a> ci: bump actions/setup-node from
4 to 5 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20089">#20089</a>)
(dependabot[bot])</li>
<li>
<a
href="5cb0ce48ef"><code
class="notranslate">5cb0ce4</code></a> refactor: use <code
class="notranslate">meta.defaultOptions</code> in <code
class="notranslate">preserve-caught-error</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20080">#20080</a>)
(Pixel998)</li>
<li>
<a
href="f9f7cb578d"><code
class="notranslate">f9f7cb5</code></a> chore: package.json update for
eslint-config-eslint release (Jenkins)</li>
<li>
<a
href="81764b2980"><code
class="notranslate">81764b2</code></a> chore: update <code
class="notranslate">eslint</code> peer dependency in <code
class="notranslate">eslint-config-eslint</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20079">#20079</a>)
(Milos Djermanovic)</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="8401101d1e...b4857e54e5">See
the full diff on Github</a>. The new version differs by 25 commits:</p>
<ul>
<li><a
href="b4857e54e5"><code>9.36.0</code></a></li>
<li><a
href="5878a4243f"><code>Build:
changelog update for 9.36.0</code></a></li>
<li><a
href="12411e8d45"><code>chore:
upgrade @eslint/js@9.36.0 (#20139)</code></a></li>
<li><a
href="488cba6b39"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="b73ab12acd"><code>docs:
update examples to use `defineConfig` (#20131)</code></a></li>
<li><a
href="47afcf668d"><code>feat:
correct `preserve-caught-error` edge cases (#20109)</code></a></li>
<li><a
href="75b74d865d"><code>fix:
add missing rule option types (#20127)</code></a></li>
<li><a
href="bac82a2a9c"><code>ci:
simplify renovate configuration (#19907)</code></a></li>
<li><a
href="1c0d85049e"><code>fix:
update `eslint-all.js` to use `Object.freeze` for `rules` object
(#20116)</code></a></li>
<li><a
href="c00bb37d62"><code>ci:
bump actions/labeler from 5 to 6 (#20090)</code></a></li>
<li><a
href="fee751dc8a"><code>refactor:
use `defaultOptions` in rules (#20121)</code></a></li>
<li><a
href="31d9392699"><code>docs:
fix typos (#20118)</code></a></li>
<li><a
href="7d61b7fadc"><code>fix:
add missing scope types to `Scope.type` (#20110)</code></a></li>
<li><a
href="1ace67d9f7"><code>chore:
update example to use `defineConfig` (#20111)</code></a></li>
<li><a
href="4821963bf7"><code>test:
add missing loc information to error objects in rule tests
(#20112)</code></a></li>
<li><a
href="c7f861b3f8"><code>docs:
Update README</code></a></li>
<li><a
href="6b0c08b106"><code>docs:
Update README</code></a></li>
<li><a
href="7a670c301b"><code>fix:
correct rule option typings in `rules.d.ts` (#20084)</code></a></li>
<li><a
href="b42c42e7cd"><code>chore:
disallow use of deprecated `type` property in core rule tests
(#20094)</code></a></li>
<li><a
href="7bb498d720"><code>test:
remove deprecated `type` property from core rule tests
(#20093)</code></a></li>
<li><a
href="91f97c5046"><code>docs:
Update README</code></a></li>
<li><a
href="e10cf2ab42"><code>ci:
bump actions/setup-node from 4 to 5 (#20089)</code></a></li>
<li><a
href="5cb0ce48ef"><code>refactor:
use `meta.defaultOptions` in `preserve-caught-error`
(#20080)</code></a></li>
<li><a
href="f9f7cb578d"><code>chore:
package.json update for eslint-config-eslint release</code></a></li>
<li><a
href="81764b2980"><code>chore:
update `eslint` peer dependency in `eslint-config-eslint`
(#20079)</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>
2025-10-03 12:35:07 -04:00
Jordan Pittman
b67cbcf6cc
Prepare v4.1.14 release (#19037)
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-10-01 11:48:02 -04:00
depfu[bot]
d96a48e3c1 Update all of nextjs to version 15.5.4 2025-09-30 19:21:14 +00:00
depfu[bot]
2766a49e28
Update tar 7.4.3 → 7.5.1 (minor) (#19035)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ tar (7.4.3 → 7.5.1) · [Repo](https://github.com/isaacs/node-tar)
· [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)




<details>
<summary>Commits</summary>
<p><a
href="206fcf91b0...64728e88cb">See
the full diff on Github</a>. The new version differs by 21 commits:</p>
<ul>
<li><a
href="64728e88cb"><code>7.5.1</code></a></li>
<li><a
href="5330eb04bc"><code>fix:
consistent TOCTOU behavior in sync t.list</code></a></li>
<li><a
href="dcb0287d7d"><code>7.5.0</code></a></li>
<li><a
href="aa1bed9287"><code>changelog
7.5</code></a></li>
<li><a
href="49bf8f9926"><code>feat:
add initial zstd support</code></a></li>
<li><a
href="b35ff94748"><code>ci:
don&#39;t bother testing on node 18</code></a></li>
<li><a
href="181be3a985"><code>update
workflows, tshy configs</code></a></li>
<li><a
href="15d4510393"><code>7.4.4</code></a></li>
<li><a
href="a4744652c3"><code>Fix
some typos</code></a></li>
<li><a
href="65ff5bed15"><code>docs(changelog):
add missing v7 breaking change</code></a></li>
<li><a
href="458d0fd236"><code>docs:
clean up duplicate onwarn options</code></a></li>
<li><a
href="afa8cf5e53"><code>update
all deps</code></a></li>
<li><a
href="bbfcec2e8b"><code>minizlib@3.1.0</code></a></li>
<li><a
href="cdd4f7dced"><code>doc:
fix lowlevel tar.Parser typo</code></a></li>
<li><a
href="eafc4b8454"><code>remove
yallist from parse, but NOT from pack</code></a></li>
<li><a
href="d281312a94"><code>Use
built-in mkdir instead of mkdirp</code></a></li>
<li><a
href="0411fb11c4"><code>format
readme</code></a></li>
<li><a
href="d5f986a516"><code>limit
normalize-unicode cache size</code></a></li>
<li><a
href="e79814eaf3"><code>remove
dirCache from mkdir, unpack</code></a></li>
<li><a
href="d3cd4392d4"><code>remove
extraneous &#39;for example&#39;</code></a></li>
<li><a
href="02f883aa4d"><code>docs
on how to use onWriteEntry</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>
2025-09-30 14:53:29 -04:00
depfu[bot]
5cd49b057b
Update @playwright/test 1.55.0 → 1.55.1 (patch) (#19033)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​playwright/test (1.55.0 → 1.55.1) ·
[Repo](https://github.com/Microsoft/playwright)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.55.1">1.55.1</a></h4>

<blockquote><h3 dir="auto">Highlights</h3>
<p dir="auto"><a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/37479">#37479</a>
- [Bug]: Upgrade Chromium to 140.0.7339.186.<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/37147">#37147</a>
- [Regression]: Internal error: step id not found.<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/37146">#37146</a>
- [Regression]: HTML reporter displays a broken chip link when there are
no projects.<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/pull/37137">#37137</a>
- Revert "fix(a11y): track inert elements as hidden".</p>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 140.0.7339.186</li>
<li>Mozilla Firefox 141.0</li>
<li>WebKit 26.0</li>
</ul>
<p dir="auto">This version was also tested against the following stable
channels:</p>
<ul dir="auto">
<li>Google Chrome 139</li>
<li>Microsoft Edge 139</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="f992162f04...ae51df7a35">See
the full diff on Github</a>. The new version differs by 7 commits:</p>
<ul>
<li><a
href="ae51df7a35"><code>chore:
mark v1.55.1 (#37530)</code></a></li>
<li><a
href="86dde294ce"><code>feat(chromium):
roll to r1193 (#37529)</code></a></li>
<li><a
href="86328bc9f4"><code>chore:
do not use -k option (#37532)</code></a></li>
<li><a
href="63799ba683"><code>cherry-pick(#37214):
docs: fix method names in release notes</code></a></li>
<li><a
href="21e29a42ab"><code>cherry-pick(#37153):
fix(html): don&#39;t display a chip with empty content with no
projects</code></a></li>
<li><a
href="ba62e6ab0d"><code>cherry-pick(#37149):
fix(test): attaching in boxed fixture</code></a></li>
<li><a
href="25bb073f26"><code>cherry-pick(#37137):
Revert &quot;fix(a11y): track inert elements as hidden
(#36947)&quot;</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>
2025-09-30 14:52:43 -04:00
govizlora
47d6a023e3
Ensure first class inside className in React is migrated (#19031)
### 1. Handling React className migration

The PR fixes an issue when migrating React components to tailwind v4
with the migration tool, that the first class after `className="` is
ignored.

For example, when migrating
```JSX
<div className="shadow"></div>
```
`shadow` will not be migrated to `shadow-sm` .

This is because in `is-safe-migration.ts`, it tests the line before
candidate with regex `/(?<!:?class)=['"]$/`. This basically skips the
migration for anything like `foo="shadow"`, with only exception for Vue
(eg. `class="shadow"`).

The PR changes the regex from

```regex
/(?<!:?class)=['"]$/
````
to
```regex
/(?<!:?class|className)=['"]$/
```
which essentially adds a new exception specifically for React's
`className="shadow"` case.

### 2. Removing redundant rules

Besides, I found that several other rules in
`CONDITIONAL_TEMPLATE_SYNTAX` being redundant since they are already
covered by the rule above, so I removed them. If we prefer the previous
explicit approach, I can revert it.

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->

Tests added for both the Vue and React classes to prevent false negative
cases.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-09-30 11:13:14 -04:00
Robin Malfait
9d00662e10
Show version mismatch when running upgrade tool (#19028)
This PR fixes an issue where sometimes people try to run the upgrade
tool, reset the changes and then try again.

If this happens, then the `package.json` and/or your lock file will
point to the old Tailwind CSS v3 version, but the actual installed
version will be v4.

This will also cause the upgrade tool to now upgrade from v4 to v4,
which is not what most people want if they were trying to upgrade from
v3 to v4. This in turn will cause some issues because now we won't try
to migrate the config file, or v3-specific classes that also exist in v4
but are only safe to upgrade from v3 to v4.

This PR uses `npm ls tailwindcss` to determine the actual installed
version. This command already errors if there is a mismatch between the
installed version and the version in `package.json` or the lock file.
This also happens to work in pnpm and bun projects (added integration
tests for these).

If for whatever reason we can't determine the expected version, we fall
back to the old behavior of just upgrading. In this scenario, the
changes introduced in
https://github.com/tailwindlabs/tailwindcss/pull/19026 will at least
give you a hint of what version was actually installed.

### Test plan

1. Tested it in a v3 project where I performed the following steps:
   1. Run the upgrade tool in full (`npx tailwindcss-upgrade`)
   2. Reset the changes (`git reset --hard && git clean -df`)
   1. Run the upgrade tool again

This resulted in the following output: <img width="1059" height="683"
alt="image"
src="https://github.com/user-attachments/assets/1d2ea2d1-b602-4631-958f-cc21eb8a633f"
/>


2. Added some integration tests to make sure this also works in pnpm,
bun and normal npm projects.

[ci-all]
2025-09-29 16:21:44 +02:00
Robin Malfait
b497e1eaf3
Add Upgrading from Tailwind CSS v… when running upgrade tool (#19026)
This PR adds a bit more information when running the upgrade tool to
know what version of Tailwind CSS you're upgrading from. This will help
users and maintainers when things go wrong.

Will have another PR up soon that errors when the Tailwind CSS version
in package.json and node_modules don't match.

### Test plan

Ran this one one of our older projects and saw the version logged
correctly.

<img width="1055" height="363" alt="image"
src="https://github.com/user-attachments/assets/5cbf4c52-ea0f-42c8-bd55-5bae2ed511de"
/>
2025-09-29 15:59:54 +02:00
depfu[bot]
f6bb72cf46
Update jiti 2.5.1 → 2.6.0 (minor) (#19029)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ jiti (2.5.1 → 2.6.0) · [Repo](https://github.com/unjs/jiti) ·
[Changelog](https://github.com/unjs/jiti/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/unjs/jiti/releases/tag/v2.6.0">2.6.0</a></h4>

<blockquote><p dir="auto"><a
href="https://bounce.depfu.com/github.com/unjs/jiti/compare/v2.5.1...v2.6.0">compare
changes</a></p>
<h3 dir="auto">🌟 What is new?</h3>
<p dir="auto">This release fixes minor issues, migrates to Rspack for
dist, and lazily imports the Babel transformer only when needed, which
should noticeably improve startup times.</p>
<ul dir="auto">
<li>Install size reduced from <code class="notranslate">2.03MB</code> to
<code class="notranslate">1.67MB</code>
</li>
<li>Loading times improved <code class="notranslate">150ms</code> =&gt;
<code class="notranslate">22ms</code> (full transform: <code
class="notranslate">180ms</code> =&gt; <code
class="notranslate">115ms</code>)</li>
</ul>
<h3 dir="auto">🔥 Performance</h3>
<ul dir="auto">
<li>Lazy load transformer (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/405">#405</a>)</li>
</ul>
<h3 dir="auto">🩹 Fixes</h3>
<ul dir="auto">
<li>
<strong>cjs-interop:</strong> Handle function default exports (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/396">#396</a>)</li>
<li>Always use native require/import for <code
class="notranslate">node:</code> specifiers (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/392">#392</a>)</li>
</ul>
<h3 dir="auto">📦 Build</h3>
<ul dir="auto">
<li>Migrate to rspack (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/404">#404</a>)</li>
<li>Updated bundled dependencies (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/compare/v2.5.1...v2.6.0#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519">diff</a>)</li>
</ul>
<h3 dir="auto">✅ Tests</h3>
<ul dir="auto">
<li>Update deno and bun native test coverage (<a
href="df844f8">df844f8</a>)</li>
</ul>
<h3 dir="auto">❤️ Contributors</h3>
<ul dir="auto">
<li>Pooya Parsa (<a
href="https://bounce.depfu.com/github.com/pi0">@pi0</a>)</li>
<li>Volodymyr Kolesnykov (<a
href="https://bounce.depfu.com/github.com/sjinks">@sjinks</a>)</li>
<li>Jungwoo LEE (<a
href="https://bounce.depfu.com/github.com/jungwoo3490">@jungwoo3490</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="61fa80358b...82919cff8b">See
the full diff on Github</a>. The new version differs by 11 commits:</p>
<ul>
<li><a
href="82919cff8b"><code>chore(release):
v2.6.0</code></a></li>
<li><a
href="c48a5d5d55"><code>perf:
lazy load babel transform (#405)</code></a></li>
<li><a
href="c3f6ff4fcb"><code>fix:
always use native for `node:` specifiers (#392)</code></a></li>
<li><a
href="7aa365b813"><code>fix(cjs-interop):
handle function default exports (#396)</code></a></li>
<li><a
href="f9d67d2d89"><code>build:
migrate to rspack (#404)</code></a></li>
<li><a
href="64044278c6"><code>test:
new bench script</code></a></li>
<li><a
href="df844f8868"><code>test:
update deno and bun native test ignores</code></a></li>
<li><a
href="5123334eb2"><code>chore:
update deps</code></a></li>
<li><a
href="cdb86cbeeb"><code>chore(deps):
update all non-major dependencies (#393)</code></a></li>
<li><a
href="e615852fb0"><code>chore(deps):
update actions/checkout action to v5 (#394)</code></a></li>
<li><a
href="d2d9aebb8c"><code>chore(deps):
update actions/setup-node action to v5 (#399)</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>
2025-09-29 09:51:45 -04:00
depfu[bot]
9feefacf81
Update bun 1.2.20 → 1.2.22 (patch) (#19007)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ bun (1.2.20 → 1.2.22) · [Repo](https://github.com/oven-sh/bun)





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>
2025-09-29 09:16:06 +00:00
depfu[bot]
0d39788ec9
Update @tailwindcss/typography 0.5.16 → 0.5.19 (minor) (#19014)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​tailwindcss/typography (0.5.16 → 0.5.19) ·
[Repo](https://github.com/tailwindlabs/tailwindcss-typography) ·
[Changelog](https://github.com/tailwindlabs/tailwindcss-typography/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/tailwindlabs/tailwindcss-typography/releases/tag/v0.5.19">0.5.19</a></h4>

<blockquote><h3 dir="auto">Fixed</h3>
<ul dir="auto">
<li>Fixed broken color styles (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-typography/pull/405">#405</a>)</li>
</ul></blockquote>
<h4><a
href="https://github.com/tailwindlabs/tailwindcss-typography/releases/tag/v0.5.18">0.5.18</a></h4>

<blockquote><h3 dir="auto">Fixed</h3>
<ul dir="auto">
<li>Fixed undefined variable error (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-typography/pull/403">#403</a>)</li>
</ul></blockquote>
<h4><a
href="https://github.com/tailwindlabs/tailwindcss-typography/releases/tag/v0.5.17">0.5.17</a></h4>

<blockquote><h3 dir="auto">Added</h3>
<ul dir="auto">
<li>Add modifiers for description list elements (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-typography/pull/357">#357</a>)</li>
<li>Add <code class="notranslate">prose-picture</code> modifier (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-typography/pull/367">#367</a>)</li>
</ul>
<h3 dir="auto">Fixed</h3>
<ul dir="auto">
<li>Include unit in <code class="notranslate">hr</code> border-width
value (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-typography/pull/379">#379</a>)</li>
<li>Ensure <code class="notranslate">&lt;kbd&gt;</code> styles work with
Tailwind CSS v4 (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-typography/pull/387">#387</a>)</li>
</ul>
<h3 dir="auto">Changed</h3>
<ul dir="auto">
<li>Remove lodash dependencies (<a
href="https://bounce.depfu.com/github.com/tailwindlabs/tailwindcss-typography/pull/402">#402</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="39d20e1949...e002ab89ad">See
the full diff on Github</a>. The new version differs by 22 commits:</p>
<ul>
<li><a
href="e002ab89ad"><code>0.5.19</code></a></li>
<li><a
href="bbb1c21099"><code>Fix
bad RGB syntax (#405)</code></a></li>
<li><a
href="b316f958af"><code>0.5.18</code></a></li>
<li><a
href="ed952066e6"><code>Fix
variable declaration in opacity function (#403)</code></a></li>
<li><a
href="7efcb4a499"><code>0.5.17</code></a></li>
<li><a
href="e0ec248baf"><code>chore(ci):
update actions for release insiders</code></a></li>
<li><a
href="511afcb0bd"><code>Add
modifiers for description list elements (#357)</code></a></li>
<li><a
href="042a531528"><code>Add
`prose-picture` modifiers (#367)</code></a></li>
<li><a
href="f822222ae6"><code>Fix
`kbd` shadow colors not being calculated on oklch colors
(#387)</code></a></li>
<li><a
href="ecb7e87a52"><code>Add
Tailwind v4 custom color theme example to README (#396)</code></a></li>
<li><a
href="ecb7d5c435"><code>Remove
lodash dependencies (#402)</code></a></li>
<li><a
href="b7cdf1e1ec"><code>Clarify
&#39;not-prose&#39; usage when using prefixes (#399)</code></a></li>
<li><a
href="25051fbfd7"><code>Fix
syntax highlighting in readme</code></a></li>
<li><a
href="d3c1fbdc60"><code>Include
v3 installation instructions alongside v4 (#388)</code></a></li>
<li><a
href="632970e3ce"><code>Readme:
Remove unused `{theme}` (#385)</code></a></li>
<li><a
href="c92dc1c120"><code>Fix
typo in comments (#378)</code></a></li>
<li><a
href="3e75cb0480"><code>Change
the borderTopWidth value for hr from integer to a pixel value
(#379)</code></a></li>
<li><a
href="1a6972e690"><code>Rectify
variant order Closes #376</code></a></li>
<li><a
href="0ab25dc0ff"><code>Fix
plugin import path in README.md (#382)</code></a></li>
<li><a
href="fb252ece73"><code>Fix
syntax errors in Readme.md (#381)</code></a></li>
<li><a
href="43a4c2c2fa"><code>Update
README for Tailwind CSS v4 (#380)</code></a></li>
<li><a
href="d1e6421d4c"><code>Update
README.md</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>
2025-09-26 16:10:51 -04:00
depfu[bot]
20f830f687
Update prettier-plugin-organize-imports 4.2.0 → 4.3.0 (minor) (#19013)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ prettier-plugin-organize-imports (4.2.0 → 4.3.0) ·
[Repo](https://github.com/simonhaenisch/prettier-plugin-organize-imports)
·
[Changelog](https://github.com/simonhaenisch/prettier-plugin-organize-imports/blob/master/changelog.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/simonhaenisch/prettier-plugin-organize-imports/releases/tag/v4.3.0">4.3.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>feat: allow configuration of <code
class="notranslate">organizeImportsTypeOrder</code>(<a
href="https://bounce.depfu.com/github.com/simonhaenisch/prettier-plugin-organize-imports/pull/152">#152</a>)
- thanks <a
href="https://bounce.depfu.com/github.com/goege64">@goege64</a> for your
first contribution 🎉</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/simonhaenisch/prettier-plugin-organize-imports/compare/v4.2.0...v4.3.0"><tt>v4.2.0...v4.3.0</tt></a></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="93df5019c4...f354c0ef86">See
the full diff on Github</a>. The new version differs by 4 commits:</p>
<ul>
<li><a
href="f354c0ef86"><code>4.3.0</code></a></li>
<li><a
href="98d053aa16"><code>chore:
update dev dependencies</code></a></li>
<li><a
href="bc4d8facb0"><code>feat:
allow configuration of `organizeImportsTypeOrder` (#152)</code></a></li>
<li><a
href="ed9c19a5b1"><code>docs:
update changelog</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>
2025-09-26 16:10:41 -04:00
depfu[bot]
cc8fefc154 Update magic-string to version 0.30.19 2025-09-26 19:12:26 +00:00
depfu[bot]
1f844244cb Update all of nextjs to version 15.5.2 2025-09-26 18:57:13 +00:00
depfu[bot]
210575a6a5
Update dedent 1.6.0 → 1.7.0 (minor) (#19010)
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?




#### ✳️ dedent (1.6.0 → 1.7.0) · [Repo](https://github.com/dmnd/dedent)
· [Changelog](https://github.com/dmnd/dedent/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/dmnd/dedent/releases/tag/v1.7.0">1.7.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>docs: cleaned up README.md badges by <a
href="https://bounce.depfu.com/github.com/JoshuaKGoldberg">@JoshuaKGoldberg</a>
in <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/100">#100</a>
</li>
<li>feat: add alignValues option by <a
href="https://bounce.depfu.com/github.com/PaperStrike">@PaperStrike</a>
in <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/102">#102</a>
</li>
<li>1.7.0 by <a
href="https://bounce.depfu.com/github.com/JoshuaKGoldberg">@JoshuaKGoldberg</a>
in <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/103">#103</a>
</li>
</ul>
<h2 dir="auto">New Contributors</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/PaperStrike">@PaperStrike</a>
made their first contribution in <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/102">#102</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/compare/v1.6.0...v1.7.0"><tt>v1.6.0...v1.7.0</tt></a></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="ab2ce25762...dd15cf5836">See
the full diff on Github</a>. The new version differs by 3 commits:</p>
<ul>
<li><a
href="dd15cf5836"><code>1.7.0
(#103)</code></a></li>
<li><a
href="304d0fc795"><code>feat:
add alignValues option (#102)</code></a></li>
<li><a
href="aab442c691"><code>docs:
cleaned up README.md badges (#100)</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>
2025-09-26 14:42:26 -04:00
depfu[bot]
3a4eb389b6
Update eslint 9.33.0 → 9.35.0 (minor) (#19009)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.33.0 → 9.35.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.35.0">9.35.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="42761fa7c8"><code
class="notranslate">42761fa</code></a> feat: implement suggestions for
no-empty-function (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20057">#20057</a>)
(jaymarvelz)</li>
<li>
<a
href="102f44442a"><code
class="notranslate">102f444</code></a> feat: implement suggestions for
no-empty-static-block (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20056">#20056</a>)
(jaymarvelz)</li>
<li>
<a
href="e51ffff737"><code
class="notranslate">e51ffff</code></a> feat: add <code
class="notranslate">preserve-caught-error</code> rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19913">#19913</a>)
(Amnish Singh Arora)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="10e7ae23e3"><code
class="notranslate">10e7ae2</code></a> fix: update uncloneable options
error message (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20059">#20059</a>)
(soda-sorcery)</li>
<li>
<a
href="bfa46013e7"><code
class="notranslate">bfa4601</code></a> fix: ignore empty switch
statements with comments in no-empty rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20045">#20045</a>)
(jaymarvelz)</li>
<li>
<a
href="dfd11deb24"><code
class="notranslate">dfd11de</code></a> fix: add <code
class="notranslate">before</code> and <code
class="notranslate">after</code> to test case types (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20049">#20049</a>)
(Francesco Trotta)</li>
<li>
<a
href="dabbe95c39"><code
class="notranslate">dabbe95</code></a> fix: correct types for <code
class="notranslate">no-restricted-imports</code> rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20034">#20034</a>)
(Milos Djermanovic)</li>
<li>
<a
href="ea789c7dd2"><code
class="notranslate">ea789c7</code></a> fix: no-loss-of-precision false
positive with uppercase exponent (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20032">#20032</a>)
(sethamus)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="d265515642"><code
class="notranslate">d265515</code></a> docs: improve phrasing - "if" →
"even if" from getting-started section (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20074">#20074</a>)
(jjangga0214)</li>
<li>
<a
href="a355a0e5b2"><code
class="notranslate">a355a0e</code></a> docs: invert comparison logic for
example in <code class="notranslate">no-var</code> doc page (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20064">#20064</a>)
(OTonGitHub)</li>
<li>
<a
href="5082fc206d"><code
class="notranslate">5082fc2</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="99cfd7e056"><code
class="notranslate">99cfd7e</code></a> docs: add missing "the" in rule
deprecation docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20050">#20050</a>)
(Josh Goldberg ✨)</li>
<li>
<a
href="6ad8973e5d"><code
class="notranslate">6ad8973</code></a> docs: update <code
class="notranslate">--no-ignore</code> and <code
class="notranslate">--ignore-pattern</code> documentation (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20036">#20036</a>)
(Francesco Trotta)</li>
<li>
<a
href="8033b19529"><code
class="notranslate">8033b19</code></a> docs: add documentation for <code
class="notranslate">--no-config-lookup</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20033">#20033</a>)
(Francesco Trotta)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="da87f2fe79"><code
class="notranslate">da87f2f</code></a> chore: upgrade @eslint/js@9.35.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20077">#20077</a>)
(Milos Djermanovic)</li>
<li>
<a
href="af2a0870fd"><code
class="notranslate">af2a087</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="70557649e3"><code
class="notranslate">7055764</code></a> test: remove <code
class="notranslate">tests/lib/eslint/eslint.config.js</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20065">#20065</a>)
(Milos Djermanovic)</li>
<li>
<a
href="84ffb9680b"><code
class="notranslate">84ffb96</code></a> chore: update <code
class="notranslate">@eslint-community/eslint-utils</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20069">#20069</a>)
(Francesco Trotta)</li>
<li>
<a
href="d5ef939715"><code
class="notranslate">d5ef939</code></a> refactor: remove deprecated <code
class="notranslate">context.parserOptions</code> usage across rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20060">#20060</a>)
(sethamus)</li>
<li>
<a
href="1b3881d7e8"><code
class="notranslate">1b3881d</code></a> chore: remove redundant word (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20058">#20058</a>)
(pxwanglu)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.34.0">9.34.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="0bb777a82b"><code
class="notranslate">0bb777a</code></a> feat: multithread linting (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19794">#19794</a>)
(Francesco Trotta)</li>
<li>
<a
href="43a5f9e36f"><code
class="notranslate">43a5f9e</code></a> feat: add eslint-plugin-regexp to
eslint-config-eslint base config (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19951">#19951</a>)
(Pixel998)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="9b8990397b"><code
class="notranslate">9b89903</code></a> fix: default value of
accessor-pairs option in rule.d.ts file (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20024">#20024</a>)
(Tanuj Kanti)</li>
<li>
<a
href="6c074206ae"><code
class="notranslate">6c07420</code></a> fix: fix spurious failure in
neostandard integration test (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20023">#20023</a>)
(Kirk Waiblinger)</li>
<li>
<a
href="676f4acaae"><code
class="notranslate">676f4ac</code></a> fix: allow scientific notation
with trailing zeros matching exponent (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20002">#20002</a>)
(Sweta Tanwar)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="0b4a590333"><code
class="notranslate">0b4a590</code></a> docs: make rulesdir deprecation
clearer (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20018">#20018</a>)
(Domenico Gemoli)</li>
<li>
<a
href="327c67256f"><code
class="notranslate">327c672</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="bf2622991f"><code
class="notranslate">bf26229</code></a> docs: Fix typo in
core-concepts/index.md (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20009">#20009</a>)
(Tobias Hernstig)</li>
<li>
<a
href="2309327554"><code
class="notranslate">2309327</code></a> docs: fix typo in the
"Configuring Rules" section (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20001">#20001</a>)
(ghazi-git)</li>
<li>
<a
href="2b87e21321"><code
class="notranslate">2b87e21</code></a> docs: [no-else-return] clarify
sample code. (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19991">#19991</a>)
(Yuki Takada (Yukinosuke Takada))</li>
<li>
<a
href="c36570c665"><code
class="notranslate">c36570c</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="f19ad9493e"><code
class="notranslate">f19ad94</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.34.0</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20030">#20030</a>)
(Francesco Trotta)</li>
<li>
<a
href="b48fa20034"><code
class="notranslate">b48fa20</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="4bce8a2502"><code
class="notranslate">4bce8a2</code></a> chore: package.json update for
eslint-config-eslint release (Jenkins)</li>
<li>
<a
href="0c9999c2a6"><code
class="notranslate">0c9999c</code></a> refactor: prefer default options
in <code class="notranslate">grouped-accessor-pairs</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20028">#20028</a>)
(루밀LuMir)</li>
<li>
<a
href="d503f19813"><code
class="notranslate">d503f19</code></a> ci: fix <code
class="notranslate">stale.yml</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20010">#20010</a>)
(루밀LuMir)</li>
<li>
<a
href="e2dc67d8b0"><code
class="notranslate">e2dc67d</code></a> ci: centralize <code
class="notranslate">stale.yml</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19994">#19994</a>)
(루밀LuMir)</li>
<li>
<a
href="7093cb8f59"><code
class="notranslate">7093cb8</code></a> ci: bump actions/checkout from 4
to 5 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/20005">#20005</a>)
(dependabot[bot])</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="a90d7c4fe5...8401101d1e">See
the full diff on Github</a>. The new version differs by 42 commits:</p>
<ul>
<li><a
href="8401101d1e"><code>9.35.0</code></a></li>
<li><a
href="b80f0254f3"><code>Build:
changelog update for 9.35.0</code></a></li>
<li><a
href="da87f2fe79"><code>chore:
upgrade @eslint/js@9.35.0 (#20077)</code></a></li>
<li><a
href="af2a0870fd"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="d265515642"><code>docs:
improve phrasing - &quot;if&quot; → &quot;even if&quot; from
getting-started section (#20074)</code></a></li>
<li><a
href="70557649e3"><code>test:
remove `tests/lib/eslint/eslint.config.js` (#20065)</code></a></li>
<li><a
href="10e7ae23e3"><code>fix:
update uncloneable options error message (#20059)</code></a></li>
<li><a
href="42761fa7c8"><code>feat:
implement suggestions for no-empty-function (#20057)</code></a></li>
<li><a
href="102f44442a"><code>feat:
implement suggestions for no-empty-static-block (#20056)</code></a></li>
<li><a
href="84ffb9680b"><code>chore:
update `@eslint-community/eslint-utils` (#20069)</code></a></li>
<li><a
href="a355a0e5b2"><code>docs:
invert comparison logic for example in `no-var` doc page
(#20064)</code></a></li>
<li><a
href="e51ffff737"><code>feat:
add `preserve-caught-error` rule (#19913)</code></a></li>
<li><a
href="5082fc206d"><code>docs:
Update README</code></a></li>
<li><a
href="d5ef939715"><code>refactor:
remove deprecated `context.parserOptions` usage across rules
(#20060)</code></a></li>
<li><a
href="1b3881d7e8"><code>chore:
remove redundant word (#20058)</code></a></li>
<li><a
href="99cfd7e056"><code>docs:
add missing &quot;the&quot; in rule deprecation docs
(#20050)</code></a></li>
<li><a
href="bfa46013e7"><code>fix:
ignore empty switch statements with comments in no-empty rule
(#20045)</code></a></li>
<li><a
href="dfd11deb24"><code>fix:
add `before` and `after` to test case types (#20049)</code></a></li>
<li><a
href="6ad8973e5d"><code>docs:
update `--no-ignore` and `--ignore-pattern` documentation
(#20036)</code></a></li>
<li><a
href="dabbe95c39"><code>fix:
correct types for `no-restricted-imports` rule (#20034)</code></a></li>
<li><a
href="8033b19529"><code>docs:
add documentation for `--no-config-lookup` (#20033)</code></a></li>
<li><a
href="ea789c7dd2"><code>fix:
no-loss-of-precision false positive with uppercase exponent
(#20032)</code></a></li>
<li><a
href="b8875f67a7"><code>9.34.0</code></a></li>
<li><a
href="2e455fb433"><code>Build:
changelog update for 9.34.0</code></a></li>
<li><a
href="f19ad9493e"><code>chore:
upgrade to `@eslint/js@9.34.0` (#20030)</code></a></li>
<li><a
href="b48fa20034"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="4bce8a2502"><code>chore:
package.json update for eslint-config-eslint release</code></a></li>
<li><a
href="0c9999c2a6"><code>refactor:
prefer default options in `grouped-accessor-pairs`
(#20028)</code></a></li>
<li><a
href="0b4a590333"><code>docs:
make rulesdir deprecation clearer (#20018)</code></a></li>
<li><a
href="9b8990397b"><code>fix:
default value of accessor-pairs option in rule.d.ts file
(#20024)</code></a></li>
<li><a
href="6c074206ae"><code>fix:
fix spurious failure in neostandard integration test
(#20023)</code></a></li>
<li><a
href="676f4acaae"><code>fix:
allow scientific notation with trailing zeros matching exponent
(#20002)</code></a></li>
<li><a
href="327c67256f"><code>docs:
Update README</code></a></li>
<li><a
href="d503f19813"><code>ci:
fix `stale.yml` (#20010)</code></a></li>
<li><a
href="0bb777a82b"><code>feat:
multithread linting (#19794)</code></a></li>
<li><a
href="bf2622991f"><code>docs:
Fix typo in core-concepts/index.md (#20009)</code></a></li>
<li><a
href="43a5f9e36f"><code>feat:
add eslint-plugin-regexp to eslint-config-eslint base config
(#19951)</code></a></li>
<li><a
href="e2dc67d8b0"><code>ci:
centralize `stale.yml` (#19994)</code></a></li>
<li><a
href="7093cb8f59"><code>ci:
bump actions/checkout from 4 to 5 (#20005)</code></a></li>
<li><a
href="2309327554"><code>docs:
fix typo in the &quot;Configuring Rules&quot; section
(#20001)</code></a></li>
<li><a
href="2b87e21321"><code>docs:
[no-else-return] clarify sample code. (#19991)</code></a></li>
<li><a
href="c36570c665"><code>docs:
Update README</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>
2025-09-26 14:39:44 -04:00
depfu[bot]
de726339e7
Update @types/semver 7.7.0 → 7.7.1 (patch) (#19005)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/semver (7.7.0 → 7.7.1) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-09-26 14:39:32 -04:00
depfu[bot]
d593a2101a
Update @types/react 19.1.9 → 19.1.13 (patch) (#19003)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.1.9 → 19.1.13) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-09-26 14:39:22 -04:00
depfu[bot]
5aa2bf1d90
Update @vitejs/plugin-react 5.0.0 → 5.0.3 (patch) (#19006) 2025-09-26 18:43:05 +02:00
depfu[bot]
a800585478
Update @types/react-dom 19.1.7 → 19.1.9 (patch) (#19004)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react-dom (19.1.7 → 19.1.9) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-09-26 17:47:31 +02:00
depfu[bot]
d6464bda09
Update @types/bun 1.2.20 → 1.2.22 (patch) (#19002)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/bun (1.2.20 → 1.2.22) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-09-26 15:42:15 +00:00
Robin Malfait
85575a41f4
Add Maud templating support (#18988)
This PR adds support for Maud templates in Rust.

We already had some pre-processing for Rust but for Leptos `class:`
syntax. This PR now added a dedicated Rust pre-processor that handles
Leptos and Maud syntax.

We only start pre-processing Maud templates if the Rust file includes
the `html!` macro.

## Test plan

Looking at the extractor, you can see that we now do extract the proper
classes in Maud templates:

<img width="1076" height="1856" alt="image"
src="https://github.com/user-attachments/assets/e649e1de-289e-466f-8fab-44a938a47dd5"
/>


Fixes: #18984
2025-09-24 11:33:01 +00:00
Jordan Pittman
c6e0a55d36
Ensure files with only @theme produce no output when built (#18979)
Closes #18978
2025-09-22 11:59:02 -04:00
Robin Malfait
9a5bae2038
Revert "Update all pnpm dependencies (2025-09-20) (#18971)"
This reverts commit 42eadfd1ee.
2025-09-22 11:34:40 +02:00
depfu[bot]
42eadfd1ee
Update all pnpm dependencies (2025-09-20) (#18971)
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?

✳️ eslint (9.33.0 → 9.35.0, minor) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) ·
[Release](https://github.com/eslint/eslint/releases/tag/v9.35.0) ·
[Diff](a90d7c4fe5...8401101d1e)

✳️ eslint-config-next (15.4.7 → 15.5.2, minor)

✳️ magic-string (0.30.18 → 0.30.19, minor) ·
[Repo](https://github.com/rich-harris/magic-string) ·
[Changelog](https://github.com/Rich-Harris/magic-string/blob/master/CHANGELOG.md)
·
[Diff](0005025c18...bdef7d5ab5)

✳️ @types/bun (1.2.20 → 1.2.21, patch) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)

✳️ @types/react (19.1.9 → 19.1.13, patch) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)

✳️ @types/react-dom (19.1.7 → 19.1.9, patch) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)

✳️ @types/semver (7.7.0 → 7.7.1, patch) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)

✳️ @vitejs/plugin-react (5.0.0 → 5.0.2, patch) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)

✳️ bun (1.2.20 → 1.2.21, patch) · [Repo](https://github.com/oven-sh/bun)

✳️ turbo (2.5.4 → 2.5.6, patch) ·
[Repo](https://github.com/turborepo/turbo)




---
![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>
2025-09-22 11:11:39 +02:00
Philipp Spiess
6c30d5ea3a
Bump napi toolchain to 3.3 (#18947)
This PR bumps all napi related dependencies to their latest version.
Napi 3 has by now officially been released but since we've been using
the preview already, there aren't many changes.

One thing that ChatGPT found is that `--no-const-enum` is the default
behavior now (see default value [in this
table](https://napi.rs/docs/cli/build#options))

## Test plan

- `pnpm install && pnpm build`
- `cd crates/node/npm/wasm32-wasi`
- `pnpm install --ignore-workspace` (This is necessary for some reason
or the bundled dependencies won't work, I have no clue why it's not
necessary on CI)
- `pnpm pack`
- Install dependency in a new npm package with a simple config like
this:
    ```js
    import { Scanner } from "@tailwindcss/oxide-wasm32-wasi";

    let scanner = new Scanner({
      sources: [
        {
base:
"/Users/philipp/dev/tailwindcss/packages/@tailwindcss-postcss/src/fixtures/example-project",
          pattern: "**/*",
          negated: false,
        },
      ],
    });

    console.log(scanner.scan());
    ```
- <img width="904" height="494" alt="CleanShot 2025-09-19 at 14 53
52@2x"
src="https://github.com/user-attachments/assets/93e32c19-6db4-4d00-9fdb-a6fde22fc69c"
/>


I also tested the CI build to make sure the `bundledDependencies` are
properly added.
2025-09-19 17:08:41 +02:00
Jordan Pittman
8d56381012
Detect classes in markdown inline directives (#18967)
Fixes #18071
2025-09-18 16:53:44 -04:00
Jordan Pittman
d0f7f82787
Add plugin option documentation to the postcss plugin readme (#18940)
Closes https://github.com/tailwindlabs/tailwindcss.com/issues/2061

In lieu of finding a place to put this on the docs we can at least
document this in the readme for the plugin.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-09-18 13:00:35 -04:00
Jordan Pittman
5b8136e838
Re-throw errors from PostCSS nodes (#18373)
Fixes #18370

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-09-18 12:03:19 -04:00
Robin Malfait
c2aab49c77
Bump Prettier (#18960)
This PR bumps prettier and solves one of our `- *` formatting issues.
Not all, but a few!
2025-09-18 11:17:29 +02:00
Blake V.
5a94f81e7e
Use default export condition for @tailwindcss/vite (#18948)
## Summary

In `@tailwindcss/vite` 's `package.json`, change the `exports` key from
`include` to `default` since there is no `require` case.

Ran into an issue using the `tsx` package to run a script that has a
sub-dependency that imports from `@tailwindcss/vite`, where `tsx`
converts things to cjs to run, and since there is no `require` case for
this package, it can't find the file. Changing to `default` covers the
cases for both `import` and `require`.

## Test plan

No testing needed. Functionality is the same.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-09-16 15:04:13 -04:00
Robin Malfait
d0a1bd655b
Show Lightning CSS warnings when optimizing/minifying in production (#18918)
This PR improves the DX by showing all the Lightning CSS warnings when
using a "production" build (or using `--optimize` or `--minify` flags
when using the CLI).

Right now Tailwind CSS itself doesn't care about the exact syntax you
are using in the CSS as long as it looks valid. We do this because
otherwise we would have to parse a lot more CSS syntax and validate it
even though it would be valid CSS in 99.99% of the cases.

Even worse, if you want to use newer CSS syntax that Tailwind CSS
doesn't validate yet, then you would get warnings for valid CSS.

Another reason why we don't do this is because the browser already does
a great job at ignoring invalid CSS.

So the linked issue #15872 would still silently fail in development
mode. In this case, everything would work, except the shadow with the
invalid syntax.

But in production mode, you would now get a proper warning from
Lightning CSS, because they try to optimize the CSS and remove invalid
CSS.

One potential issue here is that we run Lightning CSS on the generated
CSS, not on the input CSS. So the current output shows the warnings in
the output CSS not the input CSS. Any thoughts if we would just skip the
line numbers?

## Test plan

1. Everything works as before
2. In production mode, you would get warnings printed to the terminal.
This is done in `@tailwindcss/node` so the CLI/Vite/PostCSS plugins
would all get the same behavior.

Screenshots:

If you have a single issue:
<img width="977" height="441" alt="image"
src="https://github.com/user-attachments/assets/7b061ee9-b74f-4b40-aa05-cff67a21dfcc"
/>


If you have multiple issues:
<img width="2170" height="711" alt="image"
src="https://github.com/user-attachments/assets/a5bc9b0a-964b-465f-80f3-d30dd467e69c"
/>


Fixes: #15872
2025-09-12 12:27:23 +02:00
Robin Malfait
65bad11380
Do not migrate variant = 'outline' during upgrades (#18922)
This PR improves the upgrade tool for shadcn/ui projects where the
`variant = "outline"` is incorrectly migrated to `variant =
"outline-solid"`.

This PR also handles a few more cases:

```ts
// As default argument
function Button({ variant = "outline", ...props }: ButtonProps) { }

// With different kinds of quotes (single, double, backticks)
function Button({ variant = 'outline', ...props }: ButtonProps) { }

// Regardless of whitespace
function Button({ variant="outline", ...props }: ButtonProps) { }

// In JSX
<Button variant="outline" />

// With different quotes and using JavaScript expressions
<Button variant={'outline'} />

// As an object property
buttonVariants({ variant: "outline" })
```
2025-09-12 09:20:18 +00:00
Philipp Spiess
d1fd645beb
Proposal: Allow overwriting static utilities that have a namespace (#18056)
This PR attempts to move static utilities that are overwriteable by a
theme value to be a fallback rather than a conflicting implementation.
The idea is to allow a theme value to take presedence over that static
utility _and cause it not to generate_.

For example, when overwriting the `--radius-full` variant, it should
ensure that the default `rounded-full` no longer emits the
`calc(infinity * 1px)` declaration:

```ts
expect(
  await compileCss(
    css`
      @theme {
        --radius-full: 99999px;
      }
      @tailwind utilities;
    `,
    ['rounded-full'],
  ),
).toMatchInlineSnapshot(`
  ":root, :host {
    --radius-full: 99999px;
  }

  .rounded-full {
    border-radius: var(--radius-full);
  }"
`)
```

This allows anyone who wants `--radius-full` to be a CSS variable to
simply define it in their theme:

```css
@theme {
  /* Make `--radius-full` a CSS variable without the utility generating two CSS classes */
  --radius-full: calc(infinity * 1px);
}
```

The idea is to extend this pattern across all functional utilities that
also have static utilities that can collide with the namespace. This
gives users more control over what they want as CSS variables when the
defaults don't work for them, allowing them to resolve #16639 and #15115
in user space.

You may now find yourself thinking "but Philipp, why would someone want
to be able to overwrite `--animate-none`. `none` surely always will mean
no animation" and I would agree [but it's already possible right now
anyways so this is not a new behavior! This PR just cleans up the
generated output.](https://play.tailwindcss.com/StnQqm4V2e)

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-09-11 12:21:50 +02:00
Robin Malfait
340b59dcde
Do not generate grid-column when configuring grid-column-start or grid-column-end (#18907)
This PR fixes an issue where configuring a custom `--grid-column-start`
or
`--grid-column-end` also generated a `grid-column` utility due to the
overlapping namespace.

```css
@theme {
  --grid-column-start-custom: custom-start;
  --grid-column-end-custom: custom-end;
}
```

Would then generate:
```css
.col-end-custom {
  grid-column: var(--grid-column-end-custom);
}
.col-start-custom {
  grid-column: var(--grid-column-start-custom);
}
.col-start-custom {
  grid-column-start: var(--grid-column-start-custom);
}
.col-end-custom {
  grid-column-end: var(--grid-column-end-custom);
}
```

Instead of the expected:
```css
.col-start-custom {
  grid-column-start: var(--grid-column-start-custom);
}
.col-end-custom {
  grid-column-end: var(--grid-column-end-custom);
}
```

Fixes: #18906
2025-09-10 15:57:29 +02:00
Philipp Spiess
ee1c7a69dc
Fix CLI watcher cleanup race (#18905)
This PR supersets #18559 and fixes the same issue reported by @Gazler.

Upon testing, we noticed that it's possible that two parallel
invocations of file system change events could cause some cleanup
functions to get swallowed.

This happens because we only remember one global cleanup function but it
is possible timing wise that two calls to `createWatcher()` are created
before the old watchers are cleaned and thus only one of the new cleanup
functions get retained.

To fix this, this PR changes `cleanupWatchers` to an array and ensures
that all functions are retained.

In some local testing, I was able to trigger this, based on the
reproduction by @Gazler in
https://github.com/tailwindlabs/tailwindcss/pull/18559, to often call a
cleanup with more than one cleanup function in the array.

I'm going to paste the amazing reproduction from #18559 here as well:


# Requirements

We need a way to stress the CPU to slow down tailwind compilation, for
example stress-ng.

```
stress-ng --cpu 16 --timeout 10
```

It can be install with apt, homebrew or similar.

# Installation

There is a one-liner at the bottom to perform the required setup and run
the tailwindcli.

Create a new directory:

```shell
mkdir twtest && cd twtest
```

Create a package.json with the correct deps.

```shell
cat << 'EOF' > package.json
{
  "dependencies": {
    "@tailwindcss/cli": "^4.1.11",
    "daisyui": "^5.0.46",
    "tailwindcss": "^4.1.11"
  }
}
EOF
```

Create the input css:

```shell
mkdir src
cat << 'EOF' > src/.input.css
@import "tailwindcss" source(none);
@plugin "daisyui";
@source "../core_components.ex";
@source "../home.html.heex";
@source "./input.css";

EOF
```

Install tailwind, daisyui, and some HTML to make tailwind do some work:

```
npm install
wget https://raw.githubusercontent.com/phoenixframework/phoenix/refs/heads/main/installer/templates/phx_web/components/core_components.ex
wget https://github.com/phoenixframework/phoenix/blob/main/installer/templates/phx_web/controllers/page_html/home.html.heex
```

# Usage

This is easiest with 3 terminal windows:

Start a tailwindcli watcher in one terminal:

```shell
npx @tailwindcss/cli -i src/input.css -o src/output.css --watch
```

Start a stress test in another:

```shell
stress-ng --cpu 16 --timeout 30
```

Force repeated compilation in another:

```shell
for i in $(seq 1 50); do touch src/input.css; sleep 0.1; done
```

# Result

Once the stress test has completed, you can run:

```shell
touch src/input.css
```

You should see that there is repeated output, and the duration is in the
multiple seconds.

If this setup doesn't cause the issue, you can also add the `-p` flag
which causes the
CSS to be printed, slowing things down further:

```shell
npx @tailwindcss/cli -i src/input.css -p --watch
```

## One-liner

```shell
mkdir twtest && cd twtest
cat << 'EOF' > package.json
{
  "dependencies": {
    "@tailwindcss/cli": "^4.1.11",
    "daisyui": "^5.0.46",
    "tailwindcss": "^4.1.11"
  }
}
EOF

mkdir src
cat << 'EOF' > src/input.css
@import "tailwindcss" source(none);
@plugin "daisyui";
@source "../core_components.ex";
@source "../home.html.heex";
@source "./input.css";

EOF

npm install
wget https://raw.githubusercontent.com/phoenixframework/phoenix/refs/heads/main/installer/templates/phx_web/components/core_components.ex
wget https://github.com/phoenixframework/phoenix/blob/main/installer/templates/phx_web/controllers/page_html/home.html.heex
npx @tailwindcss/cli -i src/input.css -o src/output.css --watch
```

## Test plan

- Not able to reproduce this with a local build of the CLI after the
patch is applied but was able to reproduce it again once the patch was
reverted.

Co-authored-by: Gary Rennie <gazler@gmail.com>
2025-09-09 17:14:09 +02:00
Robin Malfait
b7c7e48c5d
Add @container-size utility (#18901)
This PR adds a new `@container-size` utility instead of
`@container-[size]`.

The main reason we didn't do this before is because we only have
container width related container queries, and not block based ones so
we never needed `size` and `inline-size` was enough.

However, `@container-size` is still useful if you are using container
query related units such as `cqb` which are using the block size of the
container not the inline size.

I also added a little helper such that `@container-size` is only
available in `insiders` and `4.2.0` (and later) so `4.1.x` releases
won't have this utility yet. This will require some CHANGELOG changes
such that we don't include this when releasing the next minor release.
2025-09-09 12:20:04 +00:00
Robin Malfait
2f1cbbfed2
Merge suggestions when using @utility (#18900)
This PR fixes a bug where custom `@utility` implementations with a name
that match an existing utility would override the existing suggestions
even though we generate both utilities.

With this, we want to make sure that both the custom and the built-in
utilities are suggested. We also want to make sure that we don't get
duplicate suggestions.

E.g.:

- `font-` would suggest:
  - 'font-black'
  - 'font-bold'
  - 'font-extrabold'
  - 'font-extralight'
  - 'font-light'
  - 'font-medium'
  - 'font-mono'
  - 'font-normal'
  - 'font-sans'
  - 'font-semibold'
  - 'font-serif'
  - 'font-thin'

But if you introduce this little custom utility:

```css
@theme {
  --custom-font-weights-foo: 123;
}

@utility font-* {
  --my-weight: --value(--custom-font-weights- *);
}
```

- `font-` would suggest:
  - 'font-foo'

With this fix, we would suggest:

- `font-` would suggest:
  - 'font-black'
  - 'font-bold'
  - 'font-extrabold'
  - 'font-extralight'
  - 'font-foo'          // This is now added
  - 'font-light'
  - 'font-medium'
  - 'font-mono'
  - 'font-normal'
  - 'font-sans'
  - 'font-semibold'
  - 'font-serif'
  - 'font-thin'

We also make sure that they are unique, so if you have a custom utility
that happens to match another existing utility (e.g. `font-bold`), you
won't see `font-bold` twice in the suggestions.

```css
@theme {
  --custom-font-weights-bold: bold;
  --custom-font-weights-normal: normal;
  --custom-font-weights-foo: 1234;
}

@utility font-* {
  --my-weight: --value(--custom-font-weights-*);
}
```

- `font-` would suggest:
  - 'font-black'
  - 'font-bold'          // Overlaps with existing utility
  - 'font-extrabold'
  - 'font-extralight'
  - 'font-foo'           // This is now added
  - 'font-light'
  - 'font-medium'
  - 'font-mono'
  - 'font-normal'        // Overlaps with existing utility
  - 'font-sans'
  - 'font-semibold'
  - 'font-serif'
  - 'font-thin'
2025-09-08 12:18:30 +02:00
Robin Malfait
77b3cb5318
Handle @variant inside @custom-variant (#18885)
This PR fixes an issue where you cannot use `@variant` inside a
`@custom-variant`. While you can use `@variant` in normal CSS, you
cannot inside of `@custom-variant`. Today this silently fails and emits
invalid CSS.
```css
@custom-variant dark {
  @variant data-dark {
    @slot;
  }
}
```
```html
<div class="dark:flex"></div>
```

Would result in:
```css
.dark\:flex {
  @variant data-dark {
    display: flex;
  }
}
```

To solve it we have 3 potential solutions:

1. Consider it user error — but since it generates CSS and you don't
really get an error you could be shipping broken CSS unknowingly.
1. We could try and detect this and not generate CSS for this and
potentially show a warning.
1. We could make it work as expected — which is what this PR does.

Some important notes:

1. The evaluation of the `@custom-variant` only happens when you
actually need it. That means that `@variant` inside `@custom-variant`
will always have the implementation of the last definition of that
variant.

In other words, if you use `@variant hover` inside a `@custom-variant`,
and later you override the `hover` variant, the `@custom-variant` will
use the new implementation.
1. If you happen to introduce a circular dependency, then an error will
be thrown during the build step.

You can consider it a bug fix or a new feature it's a bit of a gray
area. But
one thing that is cool about this is that you can ship a plugin that
looks like
this:
```css
@custom-variant hocus {
  @variant hover {
    @slot;
  }

  @variant focus {
    @slot;
  }
}
```

And it will use the implementation of `hover` and `focus` that the user
has defined. So if they have a custom `hover` or `focus` variant it will
just work.

By default `hocus:underline` would generate:
```css
@media (hover: hover) {
  .hocus\:underline:hover {
    text-decoration-line: underline;
  }
}

.hocus\:underline:focus {
  text-decoration-line: underline;
}
```

But if you have a custom `hover` variant like:
```css
@custom-variant hover (&:hover);
```

Then `hocus:underline` would generate:
```css
.hocus\:underline:hover, .hocus\:underline:focus {
  text-decoration-line: underline;
}
```

### Test plan

1. Existing tests pass
2. Added tests with this new functionality handled
3. Made sure to add a test for circular dependencies + error message
4. Made sure that if you "fix" the circular dependency (by overriding a
variant) that everything is generated as expected.

Fixes: https://github.com/tailwindlabs/tailwindcss/issues/18524
2025-09-05 12:24:11 +00:00
Robin Malfait
274be93fd8
Handle ' syntax in ClojureScript when extracting classes (#18888)
This PR fixes an issue where the `'` syntax in ClojureScript was not
handled properly, resulting in missing extracted classes.

This PR now supports the following ClojureScript syntaxes:

```cljs
; Keyword
(print 'text-red-500)

; List
(print '(flex flex-col underline))

; Vector
(print '[flex flex-col underline])
```

### Test plan

1. Added regression tests
2. Verified that we extract classes correctly now in various scenarios:

Top is before, bottom is with this PR:

<img width="1335" height="1862" alt="image"
src="https://github.com/user-attachments/assets/746aa073-25f8-41f8-b71c-ba83a33065aa"
/>


Fixes: #18882
2025-09-05 14:17:07 +02:00
Jordan Pittman
1334c99db8
Prepare v4.1.13 release (#18868) 2025-09-04 13:18:25 -04:00
Robin Malfait
65dc530f05
Do not allow variants to end with - or _ (#18872)
This PR is a followup of #18867, but this time we won't allow
`@custom-variant` to end with `-` or `_`.

The same reasoning applies here where Oxide doesn't pick this up but
Intellisense and Tailwind CSS' core does.

---------

Co-authored-by: Jordan Pittman <thecrypticace@gmail.com>
2025-09-03 15:30:54 +00:00
Robin Malfait
54c3f308e9
Do not allow variants to start with - (#18867)
This PR fixes an issue where custom variants with just `-` in the name
were allowed but weren't actually picked up by Oxide so you couldn't use
them anyway.

The reason we allow `-` is for `kebab-style-variants`, which is very
common, but you shouldn't use `-`, `--` or more in a variant name.

It doesn't really solve the issue (#18863), but it fixes the
inconsistencies in that exist today.

Inconsistencies:
| &nbsp; | `-:flex` | `--:flex` |
| --: | :--: | :--: |
| Oxide | ❌ | ❌ |
| Tailwind Play | ✅ | ❌ |
| Intellisense | ✅ | ✅ |

- Oxide already had the correct rules setup, so this is expected
- Tailwind Play uses Tailwind's core compile step, but it considers
candidates that start with `--` as a CSS variable instead of a utility.
This means that the `--:flex` was considered a CSS variable and skipped
during compilation.
- Intellisense uses the same APIs than Tailwind's core, but it didn't
have the CSS variable check which resulted in the `--:flex` being
"correct".

With this PR, the matrix looks like this now:
| &nbsp; | `-:flex` | `--:flex` |
| --: | :--: | :--: |
| Oxide | ❌ | ❌ |
| Tailwind Play | ❌ | ❌ |
| Intellisense | ❌ | ❌ |


This should not be considered a breaking change because Oxide didn't
pick up candidates with variants that start with a `-`. CSS for these
candidates was never generated before.

Closes: #18863

---------

Co-authored-by: Jordan Pittman <thecrypticace@gmail.com>
2025-09-03 14:32:18 +00:00
Robin Malfait
494051ca08
Consider variants starting with @- to be invalid (e.g. @-2xl:flex) (#18869)
This PR fixes a small parsing issue where variants such as `@-2xl:flex`
would parse, but were handled as-if they were `@2xl:flex` instead.

Noticed this while working on: #18867 

This is because when we parse normal variants like `data-foo` then we
want to have a `data` root and a `foo` value, not a `-foo` value.

If you are now using `@-2xl:flex`, then no CSS will be generated for
this anymore. If you were relying on this for some reason, you should
use `@2xl:flex` instead.

## Test plan

Before:

<img width="862" height="586" alt="image"
src="https://github.com/user-attachments/assets/b5993ca6-f907-49af-b5bd-b7206c8300e1"
/>

After:

<img width="862" height="586" alt="image"
src="https://github.com/user-attachments/assets/351f45e4-4cd3-451c-ae2a-c52c3e770629"
/>

---------

Co-authored-by: Jordan Pittman <thecrypticace@gmail.com>
2025-09-03 14:07:32 +00:00
Robin Malfait
4637069087
update changelog 2025-09-02 11:26:17 +02:00
Niklas Mischkulnig
664f2e36da
Add '.vercel' to ignored content directories (#18855)
Just like `.next` is ignored, ignore `.vercel` as well.
2025-09-02 09:26:06 +00:00
hustrust
c318329a1e
chore: remove redundant words (#18853) 2025-09-02 08:13:15 +00:00
Robin Malfait
ddc84b079b
update test after prettier change 2025-09-01 11:57:54 +02:00
Robin Malfait
f1331a857a
run prettier 2025-09-01 11:42:37 +02:00
okonomi
e5513b6c75
Fix missing code block delimiters in comment blocks (#18837)
## Summary

I fixed some code blocks inside comment blocks that were missing
delimiters.
2025-09-01 09:36:43 +00:00
depfu[bot]
fed6c6ab72
🚨 [security] Update all of nextjs 15.4.4 → 15.4.7 (patch) (#18838)
<hr>

🚨 <b>Your current dependencies have known security vulnerabilities</b> 🚨

This dependency update fixes known security vulnerabilities. Please see
the details below and assess their impact carefully. We recommend to
merge and deploy this as soon as possible!
<hr>



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

### What changed?




#### ✳️ eslint-config-next (15.4.4 → 15.4.7)





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



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


<details>

<summary>Security Advisories 🚨</summary>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-xv57-4mr9-wg8v">🚨
Next.js Content Injection Vulnerability for Image Optimization</a></h4>
<blockquote><p dir="auto">A vulnerability in <strong>Next.js Image
Optimization</strong> has been fixed in <strong>v15.4.5</strong> and
<strong>v14.2.31</strong>. The issue allowed attacker-controlled
external image sources to trigger file downloads with arbitrary content
and filenames under specific configurations. This behavior could be
abused for phishing or malicious file delivery.</p>
<p dir="auto">All users relying on <code
class="notranslate">images.domains</code> or <code
class="notranslate">images.remotePatterns</code> are encouraged to
upgrade and verify that external image sources are strictly
validated.</p>
<p dir="auto">More details at <a
href="https://vercel.com/changelog/cve-2025-55173">Vercel
Changelog</a></p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-g5qg-72qw-gw5v">🚨
Next.js Affected by Cache Key Confusion for Image Optimization API
Routes</a></h4>
<blockquote><p dir="auto">A vulnerability in Next.js Image Optimization
has been fixed in v15.4.5 and v14.2.31. When images returned from API
routes vary based on request headers (such as <code
class="notranslate">Cookie</code> or <code
class="notranslate">Authorization</code>), these responses could be
incorrectly cached and served to unauthorized users due to a cache key
confusion bug.</p>
<p dir="auto">All users are encouraged to upgrade if they use API routes
to serve images that depend on request headers and have image
optimization enabled.</p>
<p dir="auto">More details at <a
href="https://vercel.com/changelog/cve-2025-57752">Vercel
Changelog</a></p></blockquote>
<h4><a
href="https://bounce.depfu.com/github.com/vercel/next.js/security/advisories/GHSA-4342-x723-ch2f">🚨
Next.js Improper Middleware Redirect Handling Leads to SSRF</a></h4>
<blockquote><p dir="auto">A vulnerability in <strong>Next.js
Middleware</strong> has been fixed in <strong>v14.2.32</strong> and
<strong>v15.4.7</strong>. The issue occurred when request headers were
directly passed into <code
class="notranslate">NextResponse.next()</code>. In self-hosted
applications, this could allow Server-Side Request Forgery (SSRF) if
certain sensitive headers from the incoming request were reflected back
into the response.</p>
<p dir="auto">All users implementing custom middleware logic in
self-hosted environments are strongly encouraged to upgrade and verify
correct usage of the <code class="notranslate">next()</code>
function.</p>
<p dir="auto">More details at <a
href="https://vercel.com/changelog/cve-2025-57822">Vercel
Changelog</a></p></blockquote>
</details>

<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.4.7">15.4.7</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg 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>fix router handling when setting a location response header <a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82588">#82588</a>
</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/ztanner">@ztanner</a> for
helping!</p></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.4.6">15.4.6</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg 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>fix: <code class="notranslate">_error</code> page's <code
class="notranslate">req.url</code> can be overwritten to dynamic param
on minimal mode (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82347">#82347</a>)</li>
<li>fix: add <code class="notranslate">?dpl</code> to fonts in <code
class="notranslate">/_next/static/media</code> (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82384">#82384</a>)</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/devjiwonchoi">@devjiwonchoi</a>,
<a href="https://bounce.depfu.com/github.com/ijjk">@ijjk</a>, and <a
href="https://bounce.depfu.com/github.com/styfle">@styfle</a> for
helping!</p></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.4.5">15.4.5</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg 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>Fix API stripping JSON incorrectly (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82062">#82062</a>)</li>
<li>Fix i18n fallback: false collision (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82158">#82158</a>)</li>
<li>Revert "Fix tracing of server actions imported by client components
(<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82167">#82167</a>)</li>
<li>Ensure setAssetPrefix updates config instance (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82165">#82165</a>)</li>
<li>Turbopack: update mimalloc (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82166">#82166</a>)</li>
<li>fix(next/image): fix image-optimizer.ts headers (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82175">#82175</a>)</li>
<li>fix(next/image): improve and simplify detect-content-type (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/82174">#82174</a>)</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/ijjk">@ijjk</a>, <a
href="https://bounce.depfu.com/github.com/sokra">@sokra</a>, and <a
href="https://bounce.depfu.com/github.com/styfle">@styfle</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="fe5db65859...f30d815859">See
the full diff on Github</a>. The new version differs by 14 commits:</p>
<ul>
<li><a
href="f30d815859"><code>v15.4.7</code></a></li>
<li><a
href="1a026e338d"><code>fix
router handling when setting a location response header
(#82588)</code></a></li>
<li><a
href="be4aafd4b7"><code>v15.4.6</code></a></li>
<li><a
href="91e5b6b84f"><code>Backport
&quot;fix: add `?dpl` to fonts in `/_next/static/media` (#82384)&quot;
(#82421)</code></a></li>
<li><a
href="f1629d9395"><code>Backport
&quot;[Pages] fix: `_error` page&#39;s `req.url` can be overwritten t…
(#82377)</code></a></li>
<li><a
href="b9aab5dbe9"><code>v15.4.5</code></a></li>
<li><a
href="a8c93c49dd"><code>Disable
test new tests jobs</code></a></li>
<li><a
href="ed2a6c7548"><code>[backport]:
fix(next/image): improve and simplify detect-content-type (#82118)
(#82174)</code></a></li>
<li><a
href="f00fcc9011"><code>[backport]:
fix(next/image): fix image-optimizer.ts headers (#82114)
(#82175)</code></a></li>
<li><a
href="55a7568e9d"><code>Backport:
Turbopack: update mimalloc (#81993) (#82166)</code></a></li>
<li><a
href="5bc4b368e5"><code>[backport]
Ensure setAssetPrefix updates config instance (#82165)</code></a></li>
<li><a
href="717dfb6ec9"><code>[Backport]
Revert &quot;Fix tracing of server actions imported by client components
(#78968) (#82167)</code></a></li>
<li><a
href="6372ba03e8"><code>[backport]
Fix i18n fallback: false collision (#82158)</code></a></li>
<li><a
href="1e2c3792f8"><code>Fix
API stripping JSON incorrectly (#82062)</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>
2025-09-01 11:24:56 +02:00
Jordan Pittman
5e2a160d8b
Drop exact duplicate declarations from output CSS within a style rule (#18809)
Fixes #18178

When someone writes a utility like `after:content-['foo']` it'll produce
duplicate `content: var(--tw-content)` declarations. I thought about
special casing these but we already have an optimization pass where we
perform a full walk of the AST, flattening some rules (with the `&`
selector), analyzing declarations, etc… We can utilize that existing
spot in core to analyze and remove duplicate declarations within rules
across the AST.

The implementation does this by keeping track of declarations within a
style rule and keeps the last one for any *exact duplicate* which is a
tuple of `(property, value, important)`. This does require some
additional loops but preseving the *last* declaration is important for
correctness with regards to CSS nesting.

For example take this nested CSS:
```css
.foo {
  color: red;
  & .bar {
    color: green;
  }
  color: red;
}
```

It expands to this:
```css
.foo {
  color: red;
}
.foo.bar {
  color: green;
}
.foo {
  color: red;
}
```

If you remove the *last* rule then a `<div class="foo bar">…</div>` will
have green text when its supposed to be red. Since that would affect
behavior we have to always preserve the last declaration for a given
property.

We could go further and eliminate multiple declarations for the same
property *but* this presents a problem: every property and value must be
understood and combined with browser targets to understand whether or
not that property may act as a "fallback" or whether definitely
overwrites its previous value in all cases. This is a much more
complicated task that is much more suited to something light Lighting
CSS.
2025-08-29 11:01:56 -04:00
Jordan Pittman
b1fb02a2d7
Hide internal fields from completions in matchUtilities (#18820)
The `__CSS_VALUES__` field is an internal field we use to transport data
about theme options from CSS throug hte JS plugin API. It wasn’t
supposed to show up in suggestions but we forgot to remove it from them.

Fixes #18812
2025-08-29 11:01:20 -04:00
depfu[bot]
1602e7866d
Update magic-string 0.30.17 → 0.30.18 (minor) (#18821)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ magic-string (0.30.17 → 0.30.18) ·
[Repo](https://github.com/rich-harris/magic-string) ·
[Changelog](https://github.com/Rich-Harris/magic-string/blob/master/CHANGELOG.md)




<details>
<summary>Commits</summary>
<p><a
href="5ce04aa19d...0005025c18">See
the full diff on Github</a>. The new version differs by 5 commits:</p>
<ul>
<li><a
href="0005025c18"><code>chore:
release v0.30.18</code></a></li>
<li><a
href="376bafcb30"><code>chore:
update package.json meta</code></a></li>
<li><a
href="e59c925eb8"><code>chore:
update deps</code></a></li>
<li><a
href="0fd6253e3e"><code>fix:
prevent infinite loop on empty input (#302)</code></a></li>
<li><a
href="a8ee7b79c0"><code>chore:
update eslint config</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>
2025-08-29 14:22:19 +00:00
depfu[bot]
5ca129099c
Update @playwright/test 1.54.2 → 1.55.0 (minor) (#18823)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​playwright/test (1.54.2 → 1.55.0) ·
[Repo](https://github.com/Microsoft/playwright)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.55.0">1.55.0</a></h4>

<blockquote><h2 dir="auto">New APIs</h2>
<ul dir="auto">
<li>New Property <a
href="https://playwright.dev/docs/api/class-teststepinfo#test-step-info-title-path">testStepInfo.titlePath</a>
Returns the full title path starting from the test file, including test
and step titles.</li>
</ul>
<h2 dir="auto">Codegen</h2>
<ul dir="auto">
<li>Automatic <code class="notranslate">toBeVisible()</code> assertions:
Codegen can now generate automatic <code
class="notranslate">toBeVisible()</code> assertions for common UI
interactions. This feature can be enabled in the Codegen settings
UI.</li>
</ul>
<h2 dir="auto">Breaking Changes</h2>
<ul dir="auto">
<li>
<g-emoji class="g-emoji" alias="warning">⚠️</g-emoji> Dropped support
for Chromium extension manifest v2.</li>
</ul>
<h2 dir="auto">Miscellaneous</h2>
<ul dir="auto">
<li>Added support for Debian 13 "Trixie".</li>
</ul>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 140.0.7339.16</li>
<li>Mozilla Firefox 141.0</li>
<li>WebKit 26.0</li>
</ul>
<p dir="auto">This version was also tested against the following stable
channels:</p>
<ul dir="auto">
<li>Google Chrome 139</li>
<li>Microsoft Edge 139</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="00ce6a8b72...f992162f04">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
2025-08-29 16:16:39 +02:00
Mateusz Bronis
7b03aca53f
Replace deprecated clip with clip-path in sr-only (#18769)
## Summary

This PR replaces the deprecated `clip` property used in the `sr-only`
utility with `clip-path`, and updates the corresponding reset in
`not-sr-only`.

- Closes
[tailwindlabs/tailwindcss#18768](https://github.com/tailwindlabs/tailwindcss/issues/18768)
- Replaces `clip: rect(0, 0, 0, 0);` with `clip-path: inset(50%);` in
`sr-only`
- Replaces `clip: auto;` with `clip-path: none;` in `not-sr-only`
- Updates unit test snapshots to reflect the new CSS output

Rationale:

- `clip` is deprecated and flagged by modern linters; `clip-path` is the
recommended modern alternative while preserving the intended
visually-hidden behavior.

Before:

```css
.sr-only {
  clip: rect(0, 0, 0, 0);
}

.not-sr-only {
  clip: auto;
}
```

After:

```css
.sr-only {
  clip-path: inset(50%);
}

.not-sr-only {
  clip-path: none;
}
```

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-08-28 11:05:38 -04:00
Robin Malfait
e578238da5
Migrate supports theme keys (#18817)
This PR is a follow up of #18815 and #18816, but this time let's migrate
the `supports` theme keys.

Let's imagine you have the following Tailwind CSS v3 configuration:
```ts
export default {
  content: ['./src/**/*.html'],
  theme: {
    extend: {
      supports: {
        // Automatically handled by bare values (using CSS variable as the value)
        foo: 'foo: var(--foo)', // parentheses are optional
        bar: '(bar: var(--bar))',

        // Not automatically handled because names differ
        baz: 'qux: var(--foo)',
   //   ^^^   ^^^       ← different names

        // Custom
        grid: 'display: grid',
      },
    },
  },
}
```

Then we would generate the following Tailwind CSS v4 CSS:

```css
@custom-variant supports-baz {
  @supports (qux: var(--foo)) {
    @slot;
  }
}
@custom-variant supports-grid {
  @supports (display: grid) {
    @slot;
  }
}
```

Notice how we didn't generate a custom variant for `data-foo` or
`data-bar` because those are automatically handled by bare values.

I also went with the longer form of `@custom-variant`, we could use the
single selector approach, but that felt less clear to me.

```css
@custom-variant supports-baz (@supports (qux: var(--foo)));
@custom-variant supports-grid (@supports (display: grid));
```

---------

Co-authored-by: Jordan Pittman <thecrypticace@gmail.com>
2025-08-28 14:50:48 +00:00
Robin Malfait
82034ec327
Migrate data theme keys (#18816)
This PR is similar to and a follow up of #18815, but this time to
migrate the `data` theme keys.

Let's imagine you have the following Tailwind CSS v3 configuration:
```ts
export default {
  content: ['./src/**/*.html'],
  theme: {
    extend: {
      data: {
        // Automatically handled by bare values
        foo: 'foo',
    //  ^^^   ^^^       ← same names

        // Not automatically handled by bare values
        bar: 'baz',
    //  ^^^   ^^^       ← different names

        // Completely custom
        checked: 'ui~="checked"',
      },
    },
  },
}
```

Then we would generate the following Tailwind CSS v4 CSS:

```css
@custom-variant data-bar (&[data-baz]);
@custom-variant data-checked (&[data-ui~="checked"]);
```

Notice how we didn't generate a custom variant for `data-foo` because
those are automatically handled by bare values.
2025-08-28 14:45:18 +00:00
Robin Malfait
9e498a3e78
Migrate aria theme keys (#18815)
This PR migrates `aria` theme keys when migrating from Tailwind CSS v3
to v4.

While working on improving some of the error messages to get more
insights into why migrating the JS file changed
(https://github.com/tailwindlabs/tailwindcss/pull/18808), I ran into an
issue where I couldn't think of a good comment to why `aria` theme keys
were not being migrated. (Internally we have `aria` "blocked").

So instead of figuring out a good error message..., I just went ahead
and added the migration for `aria` theme keys.


Let's imagine you have the following Tailwind CSS v3 configuration:
```ts
export default {
  content: ['./src/**/*.html'],
  theme: {
    extend: {
      aria: {
        // Built-in (not really, but visible because of intellisense)
        busy: 'busy="true"',

        // Automatically handled by bare values
        foo: 'foo="true"',
    //  ^^^   ^^^            ← same names

        // Not automatically handled by bare values because names differ
        bar: 'baz="true"',
    //  ^^^   ^^^            ← different names

        // Completely custom
        asc: 'sort="ascending"',
        desc: 'sort="descending"',
      },
    },
  },
}
```

Then we would generate the following Tailwind CSS v4 CSS:

```css
@custom-variant aria-bar (&[aria-baz="true"]);
@custom-variant aria-asc (&[aria-sort="ascending"]);
@custom-variant aria-desc (&[aria-sort="descending"]);
```

Notice how we didn't generate a custom variant for `aria-busy` or
`aria-foo` because those are automatically handled by bare values.

We could also emit a comment near the CSS to warn about the fact that
`@custom-variant` will always be sorted _after_ any other built-in
variants.

This could result in slightly different behavior, or different order of
classes when using `prettier-plugin-tailwindcss`.

I don't know how important this is, because before this PR we would just
use `@config './tailwind.config.js';`.
Edit: when using the `@config` we override `aria` and extend it, which
means that it would be in the expected order 🤔

---------

Co-authored-by: Jordan Pittman <thecrypticace@gmail.com>
2025-08-28 16:40:38 +02:00
Jordan Pittman
8165e04564
Show suggestions for known matchVariant values (#18798)
Given this variant:
```js
matchVariant(
  "foo",
  (value) => `&:is([data-foo='${value}'])`,
  {
    values: {
      DEFAULT: "",
      bar: "bar",
      baz: "bar",
    },
  }
)
```

We weren't listing `foo-bar` and `foo-baz` in IntelliSense. This PR
fixes that.
2025-08-26 14:25:35 +00:00
Jordan Pittman
ee987e3f6a
Discard matchVariant matches with unknown named values (#18799)
This PR fixes two issues:
- When a variant is defined by `matchVariant` it could match unknown
values but not apply the variant (because it's unknown). This would
result in a utility being output that is the _same_ as a bare utility
without variants but a longer name. These were intended to be discarded
but weren't done so correctly.
- Similarly, when we encounter a known value but its not a string the
same thing would happen where we'd output a utility without applying the
variant. This was also intended to be discarded.

Basically given this code:
```js
matchVariant(
  "foo",
  (value) => `&:is([data-foo='${value}'])`,
  {
    values: {
      DEFAULT: "",
      bar: "bar",
      obj: { some: "object" },
    },
  }
)
```

And this HTML:
```html
<div class="foo-bar:bg-none foo-[baz]:bg-none foo-baz:bg-none foo-obj:bg-none"></div>
```

This CSS would be produced:
```css
@layer utilities {
  .foo-bar\:bg-none {
    &:is([data-foo='bar']) {
      background-image: none;
    }
  }
  /* this one shouldn't be here */
  .foo-baz\:bg-none {
    background-image: none;
  }
  /* this one shouldn't be here */
  .foo-obj\:bg-none {
    background-image: none;
  }
  .foo-\[baz\]\:bg-none {
    &:is([data-foo='baz']) {
      background-image: none;
    }
  }
}
```
2025-08-26 10:21:15 -04:00
Jordan Pittman
ce9b290b6b
Don't transition visibility when using transition (#18795)
We introduced an accidental breaking change a few months ago in 4.1.5
with #17812.

We added `visibility` to the property list in `transition` which
unfortunately only applies its change instantly when going from
invisible -> visible.

I've checked `display`, `content-visibility`, and `pointer-events` and
they apply their change instantly (as best I can tell) when
transitioning by default. And `overlay` only "applies" for discrete
transitions so it can stay as well.

The spec has this to say about [animating
`visibility`](https://www.w3.org/TR/web-animations-1/#animating-visibility):
> For the visibility property, visible is interpolated as a discrete
step where values of p between 0 and 1 map to visible and other values
of p map to the closer endpoint; if neither value is visible then
discrete animation is used.

This means that for visible (t=0) -> hidden (t=1) the timeline looks
like this:
- t=0.0: visible
- t=0.5: visible
- t=0.999…8: visible
- t=1.0: invisible

This means that for invisible (t=0) -> visible (t=1) the timeline looks
like this:
- t=0.0: invisible
- t=0.000…1: visible
- t=0.5: visible
- t=1.0: visible

So the value *is* instantly applied if the element is initially
invisible but when going the other direction this is not the case. This
happens whether or not the transition type is discrete.

While the spec calls out [`display` as working
similarly](https://drafts.csswg.org/css-display-4/#display-animation) in
practice this is only the case when `transition-behavior` is explicitly
set to `allow-discrete` otherwise the change is instant for both
directions.

Fixes #18793
2025-08-25 14:30:56 -04:00
depfu[bot]
e4c0255e3a
Update eslint 9.32.0 → 9.33.0 (minor) (#18779)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.32.0 → 9.33.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.33.0">9.33.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="e07820e66f"><code
class="notranslate">e07820e</code></a> feat: add global object access
detection to no-restricted-globals (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19939">#19939</a>)
(sethamus)</li>
<li>
<a
href="90b050ec11"><code
class="notranslate">90b050e</code></a> feat: support explicit resource
management in <code class="notranslate">one-var</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19941">#19941</a>)
(Sweta Tanwar)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="732433c4fb"><code
class="notranslate">732433c</code></a> fix: allow any type for <code
class="notranslate">meta.docs.recommended</code> in custom rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19995">#19995</a>)
(Francesco Trotta)</li>
<li>
<a
href="e8a6914a24"><code
class="notranslate">e8a6914</code></a> fix: Fixed potential bug in
check-emfile-handling.js (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19975">#19975</a>)
(諏訪原慶斗)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="34f0723e2d"><code
class="notranslate">34f0723</code></a> docs: playground button for
TypeScript code example (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19671">#19671</a>)
(Tanuj Kanti)</li>
<li>
<a
href="dc942a47da"><code
class="notranslate">dc942a4</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="5a4b6f7432"><code
class="notranslate">5a4b6f7</code></a> docs: Update no-multi-assign.md
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19979">#19979</a>)
(Yuki Takada (Yukinosuke Takada))</li>
<li>
<a
href="247e15698e"><code
class="notranslate">247e156</code></a> docs: add missing let
declarations in <code class="notranslate">no-plusplus</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19980">#19980</a>)
(Yuki Takada (Yukinosuke Takada))</li>
<li>
<a
href="0d17242b3c"><code
class="notranslate">0d17242</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="fa20b9db8f"><code
class="notranslate">fa20b9d</code></a> docs: Clarify when to open an
issue for a PR (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19974">#19974</a>)
(Nicholas C. Zakas)</li>
</ul>
<h2 dir="auto">Build Related</h2>
<ul dir="auto">
<li>
<a
href="27fa86551b"><code
class="notranslate">27fa865</code></a> build: use <code
class="notranslate">ESLint</code> class to generate formatter examples
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19972">#19972</a>)
(Milos Djermanovic)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="425804602e"><code
class="notranslate">4258046</code></a> chore: update dependency
@eslint/js to v9.33.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19998">#19998</a>)
(renovate[bot])</li>
<li>
<a
href="ad283717ed"><code
class="notranslate">ad28371</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="06a22f154c"><code
class="notranslate">06a22f1</code></a> test: resolve flakiness in --mcp
flag test (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19993">#19993</a>)
(Pixel998)</li>
<li>
<a
href="54920ed229"><code
class="notranslate">54920ed</code></a> test: switch to <code
class="notranslate">Linter.Config</code> in <code
class="notranslate">ESLintRules</code> type tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19977">#19977</a>)
(Francesco Trotta)</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="2364031090...a90d7c4fe5">See
the full diff on Github</a>. The new version differs by 17 commits:</p>
<ul>
<li><a
href="a90d7c4fe5"><code>9.33.0</code></a></li>
<li><a
href="9534b55372"><code>Build:
changelog update for 9.33.0</code></a></li>
<li><a
href="425804602e"><code>chore:
update dependency @eslint/js to v9.33.0 (#19998)</code></a></li>
<li><a
href="ad283717ed"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="06a22f154c"><code>test:
resolve flakiness in --mcp flag test (#19993)</code></a></li>
<li><a
href="732433c4fb"><code>fix:
allow any type for `meta.docs.recommended` in custom rules
(#19995)</code></a></li>
<li><a
href="34f0723e2d"><code>docs:
playground button for TypeScript code example (#19671)</code></a></li>
<li><a
href="dc942a47da"><code>docs:
Update README</code></a></li>
<li><a
href="5a4b6f7432"><code>docs:
Update no-multi-assign.md (#19979)</code></a></li>
<li><a
href="247e15698e"><code>docs:
add missing let declarations in `no-plusplus` (#19980)</code></a></li>
<li><a
href="0d17242b3c"><code>docs:
Update README</code></a></li>
<li><a
href="e07820e66f"><code>feat:
add global object access detection to no-restricted-globals
(#19939)</code></a></li>
<li><a
href="fa20b9db8f"><code>docs:
Clarify when to open an issue for a PR (#19974)</code></a></li>
<li><a
href="54920ed229"><code>test:
switch to `Linter.Config` in `ESLintRules` type tests
(#19977)</code></a></li>
<li><a
href="e8a6914a24"><code>fix:
Fixed potential bug in check-emfile-handling.js (#19975)</code></a></li>
<li><a
href="90b050ec11"><code>feat:
support explicit resource management in `one-var`
(#19941)</code></a></li>
<li><a
href="27fa86551b"><code>build:
use `ESLint` class to generate formatter examples
(#19972)</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>
2025-08-22 12:47:01 -04:00
depfu[bot]
7779d3d080
Update @vitejs/plugin-react 4.7.0 → 5.0.0 (major) (#18738)
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?




#### ✳️ @​vitejs/plugin-react (4.7.0 → 5.0.0) ·
[Repo](https://github.com/vitejs/vite-plugin-react) ·
[Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
















---
![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>
2025-08-15 07:37:08 -04:00
zhe he
48f66dc835
Drop warning from browser build (#18732)
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-08-14 12:20:19 -04:00
Robin Malfait
6791e8133c
Prepare v4.1.12 release (#18728)
Co-authored-by: Jordan Pittman <thecrypticace@gmail.com>
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-08-14 14:35:49 +02:00
Robin Malfait
88a8234c8a
Mark .hdr and .exr as binary extensions (#18734)
This PR fixes an issue where `.hdr` files were scanned for candidates
even though it's a binary file.

As a workaround today, you could use:

```css
@source not "**/*.hdr";
```

To ignore `.hdr` files, but let's bake it in by default instead.

Fixes: #18733
2025-08-14 14:32:30 +02:00
Bill Criswell
1855d68cd7
Add --border-color to divide theme keys (#18704)
## Summary

In Tailwind 3 the border colors were able to be used with `divide`
utilities. I made it so that's true for Tailwind 4.

## Test plan

Just used `pnpm run tdd` and making it fails then making sure it passes.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-08-13 16:44:44 +02:00
depfu[bot]
42d2433ab8
Update enhanced-resolve 5.18.2 → 5.18.3 (patch) (#18726)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ enhanced-resolve (5.18.2 → 5.18.3) ·
[Repo](https://github.com/webpack/enhanced-resolve)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/webpack/enhanced-resolve/releases/tag/v5.18.3">5.18.3</a></h4>

<blockquote><h3 dir="auto">Fixes</h3>
<ul dir="auto">
<li>Fixed nonsensible intersection in types</li>
</ul>
<h3 dir="auto">Performance</h3>
<ul dir="auto">
<li>Decreased initial loading time</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="0bf45033f4...52b61d0f03">See
the full diff on Github</a>. The new version differs by 5 commits:</p>
<ul>
<li><a
href="52b61d0f03"><code>chore(release):
5.18.3</code></a></li>
<li><a
href="ec38ca9851"><code>perf:
decrease initial loading time (#458)</code></a></li>
<li><a
href="5f74295eac"><code>refactor:
update eslint config (#457)</code></a></li>
<li><a
href="86ff2125e9"><code>fix(types):
fix nonsensible intersection</code></a></li>
<li><a
href="367d0f65e6"><code>chore(deps):
bump form-data from 3.0.3 to 3.0.4 (#455)</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>
2025-08-13 10:30:50 -04:00
Jordan Pittman
6bfdb7c60e
Bump Bun (#18723)
Fixes #18695

Was waiting for 1.2.20 b/c of some build failures. Hopefully fixed now.
Also appears to fix the above linked bug about Windows symlinks.

[ci-all]
2025-08-13 16:29:52 +02:00
depfu[bot]
1bbbe41885
Update all of react 19.1.0 → 19.1.1 (patch) (#18669)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ react (19.1.0 → 19.1.1) ·
[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.1.1">19.1.1</a></h4>

<blockquote><h3 dir="auto">React</h3>
<ul dir="auto">
<li>Fixed Owner Stacks to work with ES2015 function.name semantics (<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33680">#33680</a>
by <a href="https://bounce.depfu.com/github.com/hoxyq">@hoxyq</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="4a9df08157...02ef495809">See
the full diff on Github</a>. The new version differs by 11 commits:</p>
<ul>
<li><a
href="02ef495809"><code>Reset
packages we are not releasing to currently published
versions</code></a></li>
<li><a
href="3f178f55fc"><code>[Release]
Update build script to properly set React Native&#39;s renderers version
(#33972)</code></a></li>
<li><a
href="87e33ca2b7"><code>Set
release versions to 19.1.1</code></a></li>
<li><a
href="52cf381c72"><code>[eprh]
Bump stable version (#32978)</code></a></li>
<li><a
href="b793948e15"><code>Bump
next prerelease version numbers (#32782)</code></a></li>
<li><a
href="73e4ba42cd"><code>Allow
runtime_build_and_test action to trigger manually
(#33796)</code></a></li>
<li><a
href="5a1eb6f61a"><code>fix:
rename bottom stack frame (#33680)</code></a></li>
<li><a
href="01eae200bf"><code>[DevTools]
Get source location from structured callsites in prepareStackTrace
(#33143)</code></a></li>
<li><a
href="0e6781a06b"><code>Enable
the `enableEagerAlternateStateNodeCleanup` Feature Flag
(#33447)</code></a></li>
<li><a
href="2cd3c424ea"><code>Add
eager alternate.stateNode cleanup (#33161)</code></a></li>
<li><a
href="a24654e65b"><code>Ship
enableFabricCompleteRootInCommitPhase (#33064)</code></a></li>
</ul>
</details>




#### ✳️ react-dom (19.1.0 → 19.1.1) ·
[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.1.1">19.1.1</a></h4>

<blockquote><h3 dir="auto">React</h3>
<ul dir="auto">
<li>Fixed Owner Stacks to work with ES2015 function.name semantics (<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/33680">#33680</a>
by <a href="https://bounce.depfu.com/github.com/hoxyq">@hoxyq</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="4a9df08157...02ef495809">See
the full diff on Github</a>. The new version differs by 11 commits:</p>
<ul>
<li><a
href="02ef495809"><code>Reset
packages we are not releasing to currently published
versions</code></a></li>
<li><a
href="3f178f55fc"><code>[Release]
Update build script to properly set React Native&#39;s renderers version
(#33972)</code></a></li>
<li><a
href="87e33ca2b7"><code>Set
release versions to 19.1.1</code></a></li>
<li><a
href="52cf381c72"><code>[eprh]
Bump stable version (#32978)</code></a></li>
<li><a
href="b793948e15"><code>Bump
next prerelease version numbers (#32782)</code></a></li>
<li><a
href="73e4ba42cd"><code>Allow
runtime_build_and_test action to trigger manually
(#33796)</code></a></li>
<li><a
href="5a1eb6f61a"><code>fix:
rename bottom stack frame (#33680)</code></a></li>
<li><a
href="01eae200bf"><code>[DevTools]
Get source location from structured callsites in prepareStackTrace
(#33143)</code></a></li>
<li><a
href="0e6781a06b"><code>Enable
the `enableEagerAlternateStateNodeCleanup` Feature Flag
(#33447)</code></a></li>
<li><a
href="2cd3c424ea"><code>Add
eager alternate.stateNode cleanup (#33161)</code></a></li>
<li><a
href="a24654e65b"><code>Ship
enableFabricCompleteRootInCommitPhase (#33064)</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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-08-12 13:59:07 +00:00
Michaël De Boey
8e8a2d6efc
update @ampproject/remapping to @jridgewell/remapping (#18716)
Even though
[`@ampproject/remapping`](https://npm.im/@ampproject/remapping) isn't
deprecated on npm (yet), it's
[repo](https://github.com/ampproject/remapping) is archived, so people
should move to
[`@jridgewell/remapping`](https://npm.im/@jridgewell/remapping)

> Development moved to
[monorepo](https://github.com/jridgewell/sourcemaps)
> See
https://github.com/jridgewell/sourcemaps/tree/main/packages/remapping
for latest code.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-08-12 09:52:35 -04:00
Robin Malfait
30be24b29f
Fix false-positive migrations in addEventListener and JavaScript variable names (#18718)
This PR fixes 2 false-positives when running the upgrade tool on a
Tailwind CSS v3 project converting it to a Tailwind CSS v4 project.

The issue occurs around migrations with short simple names that have a
meaning outside if Tailwind CSS, e.g. `blur` and `outline`.

This PR fixes 2 such cases:


1. The `addEventListener` case:

   ```js
   document.addEventListener('blur', handleBlur)
   ```

We do this by special casing the `addEventListener(` case and making
sure the first argument to `addEventListener` is never migrated.

2. A JavaScript variable with default value:

   ```js
   function foo({ foo = "bar", outline = true, baz = "qux" }) {
     // ...
   }
   ```

The bug is relatively subtle here, but it has actually nothing to do
with `outline` itself, but rather the fact that some quote character
came before and after it on the same line...

One of our heuristics for determining if a migration on these small
words is safe, is to ensure that the candidate is inside of a string.
Since we didn't do any kind of quote balancing, we would consider the
`outline` to be inside of a string, even though it is not.

So to actually solve this, we do some form of quote balancing to ensure
that it's _not_ inside of a string in this case.

Additionally, this PR also introduces a small refactor to the
`is-safe-migration.test.ts` file where we now use a `test.each` to
ensure that failing tests in the middle don't prevent the rest of the
tests from running.

### Test plan

1. Added dedicated tests for the cases mentioned in the issue (#18675).
2. Added a few more tests with various forms of whitespace.

Fixes: #18675
2025-08-12 09:48:35 -04:00
depfu[bot]
3468bcface
Update @emnapi/core 1.4.4 → 1.4.5 (patch) (#18668)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/core (1.4.4 → 1.4.5) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.4.5">1.4.5</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>fix(wasm32-wasip1-threads): process never exit if trap in threads
(<a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/156">#156</a>)</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.4.4...v1.4.5"><tt>v1.4.4...v1.4.5</tt></a></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="87bb86c3a5...4afb0e72e2">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="4afb0e72e2"><code>1.4.5</code></a></li>
<li><a
href="c2ffebf277"><code>fix(wasm32-wasip1-threads):
process never exit if trap in threads (#156)</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>
2025-08-11 14:30:04 +00:00
depfu[bot]
4f5c820894
Update emnapi 1.4.4 → 1.4.5 (patch) (#18667)
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?




#### ✳️ emnapi (1.4.4 → 1.4.5) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.4.5">1.4.5</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>fix(wasm32-wasip1-threads): process never exit if trap in threads
(<a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/156">#156</a>)</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.4.4...v1.4.5"><tt>v1.4.4...v1.4.5</tt></a></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="87bb86c3a5...4afb0e72e2">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="4afb0e72e2"><code>1.4.5</code></a></li>
<li><a
href="c2ffebf277"><code>fix(wasm32-wasip1-threads):
process never exit if trap in threads (#156)</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>
2025-08-11 14:24:38 +00:00
depfu[bot]
3333d40249
Update @types/react 19.1.8 → 19.1.9 (patch) (#18673)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.1.8 → 19.1.9) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-08-11 14:17:07 +00:00
depfu[bot]
ea17bff7fe Update @playwright/test to version 1.54.2 2025-08-11 14:16:39 +00:00
depfu[bot]
ffb1d72016
Update @emnapi/runtime 1.4.4 → 1.4.5 (patch) (#18666)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/runtime (1.4.4 → 1.4.5) ·
[Repo](https://github.com/toyobayashi/emnapi)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/toyobayashi/emnapi/releases/tag/v1.4.5">1.4.5</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>fix(wasm32-wasip1-threads): process never exit if trap in threads
(<a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/pull/156">#156</a>)</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/toyobayashi/emnapi/compare/v1.4.4...v1.4.5"><tt>v1.4.4...v1.4.5</tt></a></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="87bb86c3a5...4afb0e72e2">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="4afb0e72e2"><code>1.4.5</code></a></li>
<li><a
href="c2ffebf277"><code>fix(wasm32-wasip1-threads):
process never exit if trap in threads (#156)</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>
2025-08-11 10:15:53 -04:00
depfu[bot]
ebb2d4395a
Update h3 1.15.3 → 1.15.4 (patch) (#18680)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ h3 (1.15.3 → 1.15.4) · [Repo](https://github.com/h3js/h3) ·
[Changelog](https://github.com/h3js/h3/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/h3js/h3/releases/tag/v1.15.4">1.15.4</a></h4>

<blockquote><p dir="auto"><a
href="https://bounce.depfu.com/github.com/h3js/h3/compare/v1.15.2...v1.15.4">compare
changes</a></p>
<h3 dir="auto">🩹 Fixes</h3>
<ul dir="auto">
<li>
<strong>getRequestHost:</strong> Return first host from <code
class="notranslate">x-forwarded-host</code> (<a
href="https://bounce.depfu.com/github.com/h3js/h3/pull/1175">#1175</a>)</li>
</ul>
<h3 dir="auto">💅 Refactors</h3>
<ul dir="auto">
<li>
<strong>useSession:</strong> Backport <code
class="notranslate">SessionManager</code> interface to fix types (<a
href="https://bounce.depfu.com/github.com/h3js/h3/pull/1058">#1058</a>)</li>
</ul>
<h3 dir="auto">🏡 Chore</h3>
<ul dir="auto">
<li>
<strong>docs:</strong> Fix typos (<a
href="https://bounce.depfu.com/github.com/h3js/h3/pull/1108">#1108</a>)</li>
</ul>
<h3 dir="auto">❤️ Contributors</h3>
<ul dir="auto">
<li>Pooya Parsa (<a
href="https://bounce.depfu.com/github.com/pi0">@pi0</a>)</li>
<li>Kricsleo (<a
href="https://bounce.depfu.com/github.com/kricsleo">@kricsleo</a>)</li>
<li>Izoukhai (<a
href="https://bounce.depfu.com/github.com/izoukhai">@izoukhai</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="5bd27a52d7...ec77d6bc14">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-08-11 10:12:46 -04:00
depfu[bot]
f2b5ed47ba
Update @types/react-dom 19.1.6 → 19.1.7 (patch) (#18674)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react-dom (19.1.6 → 19.1.7) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-08-11 10:02:48 -04:00
depfu[bot]
4d37043fc4
Update eslint 9.30.1 → 9.32.0 (minor) (#18656)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.30.1 → 9.32.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.32.0">9.32.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="1245000c5a"><code
class="notranslate">1245000</code></a> feat: support explicit resource
management in core rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19828">#19828</a>)
(fnx)</li>
<li>
<a
href="0e957a7b55"><code
class="notranslate">0e957a7</code></a> feat: support typescript types in
accessor rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19882">#19882</a>)
(fnx)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="960fd40dfd"><code
class="notranslate">960fd40</code></a> fix: Upgrade @eslint/js (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19971">#19971</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="bbf23fa2f1"><code
class="notranslate">bbf23fa</code></a> fix: Refactor reporting into
FileReport (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19877">#19877</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="d4988872f3"><code
class="notranslate">d498887</code></a> fix: bump @eslint/plugin-kit to
0.3.4 to resolve vulnerability (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19965">#19965</a>)
(Milos Djermanovic)</li>
<li>
<a
href="f46fc6c137"><code
class="notranslate">f46fc6c</code></a> fix: report only global
references in no-implied-eval (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19932">#19932</a>)
(Nitin Kumar)</li>
<li>
<a
href="7863d26b7c"><code
class="notranslate">7863d26</code></a> fix: remove outdated types in
<code class="notranslate">ParserOptions.ecmaFeatures</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19944">#19944</a>)
(ntnyq)</li>
<li>
<a
href="317330552e"><code
class="notranslate">3173305</code></a> fix: update execScript message in
no-implied-eval rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19937">#19937</a>)
(TKDev7)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="86e7426e44"><code
class="notranslate">86e7426</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="50de1ced9d"><code
class="notranslate">50de1ce</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="74f01a3f59"><code
class="notranslate">74f01a3</code></a> ci: unpin <code
class="notranslate">jiti</code> to version <code
class="notranslate">^2.5.1</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19970">#19970</a>)
(루밀LuMir)</li>
<li>
<a
href="2ab13813a7"><code
class="notranslate">2ab1381</code></a> ci: pin <code
class="notranslate">jiti</code> to version 2.4.2 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19964">#19964</a>)
(Francesco Trotta)</li>
<li>
<a
href="b7f7545469"><code
class="notranslate">b7f7545</code></a> test: switch to flat config mode
in <code class="notranslate">SourceCode</code> tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19953">#19953</a>)
(Milos Djermanovic)</li>
<li>
<a
href="f5a35e3b7c"><code
class="notranslate">f5a35e3</code></a> test: switch to flat config mode
in eslint-fuzzer (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19960">#19960</a>)
(Milos Djermanovic)</li>
<li>
<a
href="e22af8c42d"><code
class="notranslate">e22af8c</code></a> refactor: use <code
class="notranslate">CustomRuleDefinitionType</code> in <code
class="notranslate">JSRuleDefinition</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19949">#19949</a>)
(Francesco Trotta)</li>
<li>
<a
href="e85571730f"><code
class="notranslate">e855717</code></a> chore: switch performance tests
to hyperfine (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19919">#19919</a>)
(Francesco Trotta)</li>
<li>
<a
href="2f73a23655"><code
class="notranslate">2f73a23</code></a> test: switch to flat config mode
in <code class="notranslate">ast-utils</code> tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19948">#19948</a>)
(Milos Djermanovic)</li>
<li>
<a
href="c565a530f5"><code
class="notranslate">c565a53</code></a> chore: exclude <code
class="notranslate">further_reading_links.json</code> from Prettier
formatting (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19943">#19943</a>)
(Milos Djermanovic)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.31.0">9.31.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="35cf44c22e"><code
class="notranslate">35cf44c</code></a> feat: output full actual location
in rule tester if different (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19904">#19904</a>)
(ST-DDT)</li>
<li>
<a
href="a6a63259de"><code
class="notranslate">a6a6325</code></a> feat: support explicit resource
management in <code class="notranslate">no-loop-func</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19895">#19895</a>)
(Milos Djermanovic)</li>
<li>
<a
href="4682cdc696"><code
class="notranslate">4682cdc</code></a> feat: support explicit resource
management in <code class="notranslate">no-undef-init</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19894">#19894</a>)
(Milos Djermanovic)</li>
<li>
<a
href="58482165ea"><code
class="notranslate">5848216</code></a> feat: support explicit resource
management in <code class="notranslate">init-declarations</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19893">#19893</a>)
(Milos Djermanovic)</li>
<li>
<a
href="bb370b8e79"><code
class="notranslate">bb370b8</code></a> feat: support explicit resource
management in <code class="notranslate">no-const-assign</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19892">#19892</a>)
(Milos Djermanovic)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="07fac6cafa"><code
class="notranslate">07fac6c</code></a> fix: retry on EMFILE when writing
autofix results (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19926">#19926</a>)
(TKDev7)</li>
<li>
<a
href="28cc7abbb7"><code
class="notranslate">28cc7ab</code></a> fix: Remove incorrect RuleContext
types (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19910">#19910</a>)
(Nicholas C. Zakas)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="664cb44ab0"><code
class="notranslate">664cb44</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="40dbe2a43f"><code
class="notranslate">40dbe2a</code></a> docs: fix mismatch between <code
class="notranslate">globalIgnores()</code> code and text (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19914">#19914</a>)
(MaoShizhong)</li>
<li>
<a
href="5a0069d608"><code
class="notranslate">5a0069d</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="fef04b5c7f"><code
class="notranslate">fef04b5</code></a> docs: Update working on issues
info (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19902">#19902</a>)
(Nicholas C. Zakas)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="3ddd454c1c"><code
class="notranslate">3ddd454</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.31.0</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19935">#19935</a>)
(Francesco Trotta)</li>
<li>
<a
href="d5054e5454"><code
class="notranslate">d5054e5</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="0f4a3781fe"><code
class="notranslate">0f4a378</code></a> chore: update eslint (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19933">#19933</a>)
(renovate[bot])</li>
<li>
<a
href="76c2340c36"><code
class="notranslate">76c2340</code></a> chore: bump mocha to v11 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19917">#19917</a>)
(루밀LuMir)</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="6769b5fa11...2364031090">See
the full diff on Github</a>. The new version differs by 37 commits:</p>
<ul>
<li><a
href="2364031090"><code>9.32.0</code></a></li>
<li><a
href="a0e62e2739"><code>Build:
changelog update for 9.32.0</code></a></li>
<li><a
href="960fd40dfd"><code>fix:
Upgrade @eslint/js (#19971)</code></a></li>
<li><a
href="50de1ced9d"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="bbf23fa2f1"><code>fix:
Refactor reporting into FileReport (#19877)</code></a></li>
<li><a
href="74f01a3f59"><code>ci:
unpin `jiti` to version `^2.5.1` (#19970)</code></a></li>
<li><a
href="d4988872f3"><code>fix:
bump @eslint/plugin-kit to 0.3.4 to resolve vulnerability
(#19965)</code></a></li>
<li><a
href="2ab13813a7"><code>ci:
pin `jiti` to version 2.4.2 (#19964)</code></a></li>
<li><a
href="b7f7545469"><code>test:
switch to flat config mode in `SourceCode` tests
(#19953)</code></a></li>
<li><a
href="f5a35e3b7c"><code>test:
switch to flat config mode in eslint-fuzzer (#19960)</code></a></li>
<li><a
href="f46fc6c137"><code>fix:
report only global references in no-implied-eval
(#19932)</code></a></li>
<li><a
href="86e7426e44"><code>docs:
Update README</code></a></li>
<li><a
href="e22af8c42d"><code>refactor:
use `CustomRuleDefinitionType` in `JSRuleDefinition`
(#19949)</code></a></li>
<li><a
href="1245000c5a"><code>feat:
support explicit resource management in core rules
(#19828)</code></a></li>
<li><a
href="e85571730f"><code>chore:
switch performance tests to hyperfine (#19919)</code></a></li>
<li><a
href="0e957a7b55"><code>feat:
support typescript types in accessor rules (#19882)</code></a></li>
<li><a
href="2f73a23655"><code>test:
switch to flat config mode in `ast-utils` tests (#19948)</code></a></li>
<li><a
href="7863d26b7c"><code>fix:
remove outdated types in `ParserOptions.ecmaFeatures`
(#19944)</code></a></li>
<li><a
href="c565a530f5"><code>chore:
exclude `further_reading_links.json` from Prettier formatting
(#19943)</code></a></li>
<li><a
href="317330552e"><code>fix:
update execScript message in no-implied-eval rule
(#19937)</code></a></li>
<li><a
href="14053edc64"><code>9.31.0</code></a></li>
<li><a
href="2b77bd05dc"><code>Build:
changelog update for 9.31.0</code></a></li>
<li><a
href="3ddd454c1c"><code>chore:
upgrade to `@eslint/js@9.31.0` (#19935)</code></a></li>
<li><a
href="d5054e5454"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="0f4a3781fe"><code>chore:
update eslint (#19933)</code></a></li>
<li><a
href="664cb44ab0"><code>docs:
Update README</code></a></li>
<li><a
href="07fac6cafa"><code>fix:
retry on EMFILE when writing autofix results (#19926)</code></a></li>
<li><a
href="35cf44c22e"><code>feat:
output full actual location in rule tester if different
(#19904)</code></a></li>
<li><a
href="40dbe2a43f"><code>docs:
fix mismatch between `globalIgnores()` code and text
(#19914)</code></a></li>
<li><a
href="76c2340c36"><code>chore:
bump mocha to v11 (#19917)</code></a></li>
<li><a
href="28cc7abbb7"><code>fix:
Remove incorrect RuleContext types (#19910)</code></a></li>
<li><a
href="a6a63259de"><code>feat:
support explicit resource management in `no-loop-func`
(#19895)</code></a></li>
<li><a
href="4682cdc696"><code>feat:
support explicit resource management in `no-undef-init`
(#19894)</code></a></li>
<li><a
href="58482165ea"><code>feat:
support explicit resource management in `init-declarations`
(#19893)</code></a></li>
<li><a
href="bb370b8e79"><code>feat:
support explicit resource management in `no-const-assign`
(#19892)</code></a></li>
<li><a
href="5a0069d608"><code>docs:
Update README</code></a></li>
<li><a
href="fef04b5c7f"><code>docs:
Update working on issues info (#19902)</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>
2025-08-01 12:55:57 -04:00
depfu[bot]
7010a9b79b
Update jiti 2.4.2 → 2.5.1 (minor) (#18651)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ jiti (2.4.2 → 2.5.1) · [Repo](https://github.com/unjs/jiti) ·
[Changelog](https://github.com/unjs/jiti/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/unjs/jiti/releases/tag/v2.5.1">2.5.1</a></h4>

<blockquote><p dir="auto"><a
href="https://bounce.depfu.com/github.com/unjs/jiti/compare/v2.5.0...v2.5.1">compare
changes</a></p>
<h3 dir="auto">🩹 Fixes</h3>
<ul dir="auto">
<li>
<strong>interop:</strong> Passthrough module if it is a promise (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/389">#389</a>)</li>
</ul></blockquote>
<h4><a
href="https://github.com/unjs/jiti/releases/tag/v2.5.0">2.5.0</a></h4>

<blockquote><p dir="auto"><a
href="https://bounce.depfu.com/github.com/unjs/jiti/compare/v2.4.2...v2.5.0">compare
changes</a></p>
<h3 dir="auto">🚀 Enhancements</h3>
<ul dir="auto">
<li>Use <code class="notranslate">sha256</code> for cache entries in
FIPS mode (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/375">#375</a>)</li>
<li>Support <code class="notranslate">rebuildFsCache</code> ( <code
class="notranslate">JITI_REBUILD_FS_CACHE</code>) (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/379">#379</a>)</li>
</ul>
<h3 dir="auto">🩹 Fixes</h3>
<ul dir="auto">
<li>Interop modules with null/undefined default export (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/377">#377</a>)</li>
<li>Handle <code class="notranslate">require(&lt;json&gt;)</code> in
register mode (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/374">#374</a>)</li>
</ul>
<h3 dir="auto">📦 Dependencies</h3>
<ul dir="auto">
<li>Updated bundled dependencies (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/compare/v2.4.2...v2.5.0">compare
changes</a>)</li>
</ul>
<h3 dir="auto">📖 Docs</h3>
<ul dir="auto">
<li>Add defaults in JSDocs (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/365">#365</a>)</li>
</ul>
<h3 dir="auto">✅ Tests</h3>
<ul dir="auto">
<li>Only include src for coverage report (<a
href="https://bounce.depfu.com/github.com/unjs/jiti/pull/372">#372</a>)</li>
</ul>
<h3 dir="auto">❤️ Contributors</h3>
<ul dir="auto">
<li>Kricsleo (<a
href="https://bounce.depfu.com/github.com/kricsleo">@kricsleo</a>)
🌟</li>
<li>Pooya Parsa (<a
href="https://bounce.depfu.com/github.com/pi0">@pi0</a>)</li>
<li>Kanon (<a
href="https://bounce.depfu.com/github.com/ysknsid25">@ysknsid25</a>)</li>
<li>Arya Emami (<a
href="https://bounce.depfu.com/github.com/aryaemami59">@aryaemami59</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="340e2a733c...61fa80358b">See
the full diff on Github</a>. The new version differs by 17 commits:</p>
<ul>
<li><a
href="61fa80358b"><code>chore(release):
v2.5.1</code></a></li>
<li><a
href="ad268df55b"><code>fix(interop):
passthrough module if it is a promise (#389)</code></a></li>
<li><a
href="62eac09b15"><code>chore(release):
v2.5.0</code></a></li>
<li><a
href="54461d6061"><code>fix(register):
handle `require(&lt;json&gt;)` (#374)</code></a></li>
<li><a
href="ea2340d1d0"><code>fix:
interop modules with nil default export (#377)</code></a></li>
<li><a
href="2e5a282f4c"><code>feat:
`rebuildFsCache` ( `JITI_REBUILD_FS_CACHE`) (#379)</code></a></li>
<li><a
href="63e907fe35"><code>feat:
use `sha256` for cache entries in fips mode (#375)</code></a></li>
<li><a
href="c567a37af6"><code>chore:
update snapshot</code></a></li>
<li><a
href="fb2da19654"><code>test:
only include src for coverage report (#372)</code></a></li>
<li><a
href="c9fb9fc020"><code>chore(deps):
update autofix-ci/action digest to 635ffb0 (#385)</code></a></li>
<li><a
href="dde7c823fa"><code>chore:
lint</code></a></li>
<li><a
href="35a6a61860"><code>chore:
update deps</code></a></li>
<li><a
href="b396aec458"><code>chore:
add defaults in JSDocs (#365)</code></a></li>
<li><a
href="aa541a64ae"><code>chore(deps):
update autofix-ci/action digest to 551dded (#358)</code></a></li>
<li><a
href="fb2b903cc2"><code>chore:
update deps</code></a></li>
<li><a
href="c7cfeedbd2"><code>test:
update snapshot</code></a></li>
<li><a
href="6b7fe8b7e6"><code>chore:
update ci</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>
2025-07-31 11:48:58 -04:00
Jordan Pittman
68a79b159c
Suggest bare values for flex-* utilities (#18642)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1426

We weren't suggesting things like `flex-1`. This adds that. I'm not sure
if we want to suggest more `flex-<number>` values though. I've added
1–12 but am open to changing this.
2025-07-30 10:48:26 -04:00
Jordan Pittman
492304212f
Allow users to disable url rewriting in the PostCSS plugin (#18321)
Since we (optionally) support source maps now it's possible that a later
PostCSS plugin that *still* does url rewriting might fail to do so
correctly because nodes will have preserved source locations in dev
builds where before we "pretended" that everything came from the
original file.

But, because we can't know if such a plugin is present, disabling this
behavior when source maps are enabled could cause issues *and* would be
a breaking change.

I wish everything *could just work* here but realistically we can't know
what plugins have run before our PostCSS plugin or what plugins will run
after so the best option (I think) we can offer here is to allow users
to disable url rewriting at the plugin level.

Fixes #16700
2025-07-30 10:35:10 -04:00
depfu[bot]
afbfdebf06
Update all of nextjs 15.3.4 → 15.4.4 (minor) (#18636)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint-config-next (15.3.4 → 15.4.4)





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



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



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.4.4">15.4.4</a></h4>

<blockquote><em>More info than we can show here.</em></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.4.3">15.4.3</a></h4>

<blockquote><em>More info than we can show here.</em></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.4.2">15.4.2</a></h4>

<blockquote><em>More info than we can show here.</em></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.4.1">15.4.1</a></h4>

<blockquote><em>More info than we can show here.</em></blockquote>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.3.5">15.3.5</a></h4>

<blockquote><em>More info than we can show here.</em></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="f98040047e...fe5db65859">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
2025-07-30 10:32:16 -04:00
depfu[bot]
b89d9c3702
Update prettier-plugin-organize-imports 4.0.0 → 4.2.0 (minor) (#18634)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ prettier-plugin-organize-imports (4.0.0 → 4.2.0) ·
[Repo](https://github.com/simonhaenisch/prettier-plugin-organize-imports)
·
[Changelog](https://github.com/simonhaenisch/prettier-plugin-organize-imports/blob/master/changelog.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/simonhaenisch/prettier-plugin-organize-imports/releases/tag/v4.1.0">4.1.0</a></h4>

<blockquote><p dir="auto">Bumped the peer dependency range for <code
class="notranslate">vue-tsc</code> to <code
class="notranslate">^2.1.0</code> because there was a breaking change in
its API. If you're using Vue support, upgrade both packages
simultaneously, e.g. <code class="notranslate">npm i -D
prettier-plugin-organize-imports vue-tsc</code>.</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="8ae2dddcc5...93df5019c4">See
the full diff on Github</a>. The new version differs by 14 commits:</p>
<ul>
<li><a
href="93df5019c4"><code>4.2.0</code></a></li>
<li><a
href="253d1bffcb"><code>chore:
update all dev dependencies</code></a></li>
<li><a
href="858cbbea84"><code>fix:
use `getDefaultCompilerOptions` from vue-tsc instead of the deprecated
`resolveVueCompilerOptions`</code></a></li>
<li><a
href="c777cd8c2a"><code>feat:
allow vue-tsc 3 as peer dependency</code></a></li>
<li><a
href="e79ed64e5e"><code>chore(ci):
remove `check-types` step (part of `test` now)</code></a></li>
<li><a
href="b7c15ad9f5"><code>4.1.0</code></a></li>
<li><a
href="2e9c480b72"><code>chore:
update dev dependencies</code></a></li>
<li><a
href="fb7e872d64"><code>docs:
update changelog</code></a></li>
<li><a
href="3fb452e7df"><code>feat:
support vue-tsc v2.1 (#136)</code></a></li>
<li><a
href="4678129bd0"><code>chore(ci):
run on node 22</code></a></li>
<li><a
href="fa997242e4"><code>chore(ci):
fix run-script invocation</code></a></li>
<li><a
href="3ea4a8151f"><code>chore:
use tsc to type-check the project</code></a></li>
<li><a
href="59c5329c46"><code>chore(tests):
upgrade ava from v3 to v6</code></a></li>
<li><a
href="b253038883"><code>fix:
drop `@vue/language-plugin-pug` as peer dependency</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>
2025-07-29 14:01:37 -04:00
depfu[bot]
e73637d372
Update @playwright/test 1.53.2 → 1.54.1 (minor) (#18631)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​playwright/test (1.53.2 → 1.54.1) ·
[Repo](https://github.com/Microsoft/playwright)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.54.1">1.54.1</a></h4>

<blockquote><h3 dir="auto">Highlights</h3>
<p dir="auto"><a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/36650">#36650</a>
- [Regression]: 1.54.0 breaks downloading browsers when an HTTP(S) proxy
is used</p>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 139.0.7258.5</li>
<li>Mozilla Firefox 140.0.2</li>
<li>WebKit 26.0</li>
</ul>
<p dir="auto">This version was also tested against the following stable
channels:</p>
<ul dir="auto">
<li>Google Chrome 140</li>
<li>Microsoft Edge 140</li>
</ul></blockquote>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.54.0">1.54.0</a></h4>

<blockquote><h2 dir="auto">Highlights</h2>
<ul dir="auto">
<li>
<p dir="auto">New cookie property <code
class="notranslate">partitionKey</code> in <a
href="https://playwright.dev/docs/api/class-browsercontext#browser-context-cookies">browserContext.cookies()</a>
and <a
href="https://playwright.dev/docs/api/class-browsercontext#browser-context-add-cookies">browserContext.addCookies()</a>.
This property allows to save and restore partitioned cookies. See <a
href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies">CHIPS
MDN article</a> for more information. Note that browsers have different
support and defaults for cookie partitioning.</p>
</li>
<li>
<p dir="auto">New option <code class="notranslate">noSnippets</code> to
disable code snippets in the html report.</p>
<div class="highlight highlight-source-js" dir="auto"><pre
class="notranslate"><span class="pl-k">import</span> <span
class="pl-kos">{</span> <span class="pl-s1">defineConfig</span> <span
class="pl-kos">}</span> <span class="pl-k">from</span> <span
class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">default</span> <span
class="pl-en">defineConfig</span><span class="pl-kos">(</span><span
class="pl-kos">{</span>
<span class="pl-c1">reporter</span>: <span class="pl-kos">[</span><span
class="pl-kos">[</span><span class="pl-s">'html'</span><span
class="pl-kos">,</span> <span class="pl-kos">{</span> <span
class="pl-c1">noSnippets</span>: <span class="pl-c1">true</span> <span
class="pl-kos">}</span><span class="pl-kos">]</span><span
class="pl-kos">]</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span
class="pl-kos">;</span></pre></div>
</li>
<li>
<p dir="auto">New property <code class="notranslate">location</code> in
test annotations, for example in <a
href="https://playwright.dev/docs/api/class-testresult#test-result-annotations">testResult.annotations</a>
and <a
href="https://playwright.dev/docs/api/class-testinfo#test-info-annotations">testInfo.annotations</a>.
It shows where the annotation like <code
class="notranslate">test.skip</code> or <code
class="notranslate">test.fixme</code> was added.</p>
</li>
</ul>
<h2 dir="auto">Command Line</h2>
<ul dir="auto">
<li>
<p dir="auto">New option <code
class="notranslate">--user-data-dir</code> in multiple commands. You can
specify the same user data dir to reuse browsing state, like
authentication, between sessions.</p>
<div class="highlight highlight-source-shell" dir="auto"><pre
class="notranslate">npx playwright codegen
--user-data-dir=./user-data</pre></div>
</li>
<li>
<p dir="auto">Option <code class="notranslate">-gv</code> has been
removed from the <code class="notranslate">npx playwright test</code>
command. Use <code class="notranslate">--grep-invert</code> instead.</p>
</li>
<li>
<p dir="auto"><code class="notranslate">npx playwright open</code> does
not open the test recorder anymore. Use <code class="notranslate">npx
playwright codegen</code> instead.</p>
</li>
</ul>
<h2 dir="auto">Miscellaneous</h2>
<ul dir="auto">
<li>Support for Node.js 16 has been removed.</li>
<li>Support for Node.js 18 has been deprecated, and will be removed in
the future.</li>
</ul>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 139.0.7258.5</li>
<li>Mozilla Firefox 140.0.2</li>
<li>WebKit 26.0</li>
</ul>
<p dir="auto">This version was also tested against the following stable
channels:</p>
<ul dir="auto">
<li>Google Chrome 140</li>
<li>Microsoft Edge 140</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="8c38de4d13...97b6b881b7">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
2025-07-28 16:29:06 -04:00
Jordan Pittman
06007b1eec
Fix Windows CI failures (#18611)
Seems like updating Turbo caused bun to break but only on Windows. No
idea why.
2025-07-25 07:28:50 -04:00
depfu[bot]
67022aa5f4
Update @vitejs/plugin-react 4.6.0 → 4.7.0 (minor) (#18608)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (4.6.0 → 4.7.0) ·
[Repo](https://github.com/vitejs/vite) ·
[Changelog](https://github.com/vitejs/vite/blob/main/packages/plugin-react/CHANGELOG.md)
















---
![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>
2025-07-25 07:13:25 -04:00
depfu[bot]
db2770a3f6
Update @emnapi/wasi-threads 1.0.3 → 1.0.4 (patch) (#18609)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/wasi-threads (1.0.3 → 1.0.4) ·
[Repo](https://github.com/toyobayashi/emnapi)





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>
2025-07-25 07:07:46 -04:00
depfu[bot]
6314c8fb36
Update turbo 2.5.4 → 2.5.5 (patch) (#18595)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ turbo (2.5.4 → 2.5.5) ·
[Repo](https://github.com/turborepo/turbo)





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>
2025-07-23 20:22:41 -04:00
Jordan Pittman
fa3f45f02c
Don’t output CSS objects with false or undefined in the AST (#18571)
Fixes https://github.com/tailwindlabs/tailwindcss-typography/issues/384

Basically when addUtilities/addComponents/matchUtilities/matchComponents
saw a value of `false` it was being output instead of being discarded
like it was in v3.

The types really require these to be strings but for things like the
typography plugin this isn't really carried through from its theme
config so it was easy to put anything in there and not realize it
doesn't match the expected types.

Basically this:
```js
addUtilities({
  '.foo': {
    a: 'red',
    'z-index': 0,
    '.bar': false,
    '.baz': null, // this one already worked
    '.qux': undefined,
  },
})
```

Now works like it did in v3 and omits `.bar`, `.baz`, and `.qux`
2025-07-21 15:39:34 -04:00
depfu[bot]
847ed1e4d7 Update @types/bun to version 1.2.18 2025-07-21 14:20:28 +00:00
depfu[bot]
203073f0b8
Update bun 1.2.17 → 1.2.18 (patch) (#18574)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ bun (1.2.17 → 1.2.18) · [Repo](https://github.com/oven-sh/bun)





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>
2025-07-21 10:14:43 -04:00
Jordan Pittman
939fda6f4e
Show more specific error for functional-like but invalid utility names (#18568)
Fixes #18566

The behavior is by design but the error message could definitely be
improved.
2025-07-18 12:31:19 -04:00
Hugo Parente Lima
298ad08653
Add support for Slang template (#18565)
## Summary

Slang is basically a Slim template language for Crystal language, so the
very same Slim parser works fine.

Slim template: https://github.com/slim-template/slim
Slang template: https://github.com/jeromegn/slang

## Test plan

Create a simple slang file with some tailwind-css and check if the CSS
is being extracted:
```slim
doctype html
html
  head
    title This is a title
  body.min-h-screen
    header.stick.top-0.z-10
      section.max-w-4xl.mx-auto.p-4.flex.items-center.justify-between
        h1.text-3xl.font-medium This is a slang file
``` 
To test it, get any slim template, rename the extension to .slang 

Fixes #17851

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-07-17 14:10:19 -04:00
depfu[bot]
03aff9e613
Update @napi-rs/wasm-runtime 0.2.11 → 0.2.12 (minor) (#18562)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/wasm-runtime (0.2.11 → 0.2.12) ·
[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>
2025-07-17 11:00:59 -04:00
Jordan Pittman
88b9f15b65
Center the dropdown icon added to an input with a paired datalist in Chrome (#18511)
This PR tweaks the dropdown arrow added to an input by Chrome when it
has a `list` attribute pointing to a `<datalist>`.

Right now the arrow isn't centered vertically:

<img width="227" height="58" alt="Screenshot 2025-07-14 at 15 41 50"
src="https://github.com/user-attachments/assets/b354a5e8-432d-432d-bfe4-f7b6f6683548"
/>

The cause of this is the line height being inherited into the pseudo
element which controls how the marker is positioned. I *think* this is
because it's being drawn with unicode symbols but I'm not sure. It could
just be from the `list-item` display.

After this PR changes the line height its centered again:

<img width="227" height="58" alt="Screenshot 2025-07-14 at 15 42 05"
src="https://github.com/user-attachments/assets/1afa1f33-cc28-4b1f-9e04-e546f6848f57"
/>

Some notes:

This only affects Chrome and also does not appear to cause issues for
date/time inputs. While weird that this pseudo is the one used for a
`<datalist>` marker it is indeed correct.

Fixes #18499

Can use this Play to test the change:
https://play.tailwindcss.com/jzT35CRpr0

---------

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
2025-07-15 14:41:04 -04:00
depfu[bot]
ce0e775212
Update @tybys/wasm-util 0.9.0 → 0.10.0 (major) (#18541)
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?




#### ✳️ @​tybys/wasm-util (0.9.0 → 0.10.0) ·
[Repo](https://github.com/toyobayashi/wasm-util)





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>
2025-07-15 10:14:57 -04:00
depfu[bot]
2a4b0b59fd
Update @emnapi/core 1.4.3 → 1.4.4 (patch) (#18535)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/core (1.4.3 → 1.4.4) ·
[Repo](https://github.com/toyobayashi/emnapi)




<details>
<summary>Commits</summary>
<p><a
href="90c4c1b135...87bb86c3a5">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="87bb86c3a5"><code>1.4.4</code></a></li>
<li><a
href="92d5bba8c7"><code>fix:
`worker.onerror` may receive an `Event`</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>
2025-07-14 11:59:50 -04:00
depfu[bot]
9cfd20b19b
Update emnapi 1.4.3 → 1.4.4 (patch) (#18534)
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?




#### ✳️ emnapi (1.4.3 → 1.4.4) ·
[Repo](https://github.com/toyobayashi/emnapi)




<details>
<summary>Commits</summary>
<p><a
href="90c4c1b135...87bb86c3a5">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="87bb86c3a5"><code>1.4.4</code></a></li>
<li><a
href="92d5bba8c7"><code>fix:
`worker.onerror` may receive an `Event`</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>
2025-07-14 11:59:39 -04:00
Jordan Pittman
798a7bf905
Ignore consecutive semicolons in the CSS parser (#18532)
Fixes #18523

I swear I'd already landed this but apparently not.
2025-07-14 11:44:31 -04:00
depfu[bot]
e0eac192ff Update @emnapi/runtime to version 1.4.4 2025-07-14 15:11:29 +00:00
depfu[bot]
cbe06694cb
Update @emnapi/wasi-threads 1.0.2 → 1.0.3 (patch) (#18531)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​emnapi/wasi-threads (1.0.2 → 1.0.3) ·
[Repo](https://github.com/toyobayashi/emnapi)





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>
2025-07-14 11:08:42 -04:00
depfu[bot]
d56564152e
Update eslint 9.29.0 → 9.30.1 (minor) (#18513)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.29.0 → 9.30.1) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.30.1">9.30.1</a></h4>

<blockquote><h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="e91bb870f8"><code
class="notranslate">e91bb87</code></a> fix: allow separate default and
named type imports (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19899">#19899</a>)
(xbinaryx)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="ab7c62598a"><code
class="notranslate">ab7c625</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="dae1e5bb27"><code
class="notranslate">dae1e5b</code></a> docs: update jsdoc's link (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19896">#19896</a>)
(JamesVanWaza)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="b035f747c6"><code
class="notranslate">b035f74</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.30.1</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19906">#19906</a>)
(Francesco Trotta)</li>
<li>
<a
href="b3dbc16563"><code
class="notranslate">b3dbc16</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.30.0">9.30.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="52a5fcaa4e"><code
class="notranslate">52a5fca</code></a> feat: Support <code
class="notranslate">basePath</code> property in config objects (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19879">#19879</a>)
(Milos Djermanovic)</li>
<li>
<a
href="4ab44823df"><code
class="notranslate">4ab4482</code></a> feat: add <code
class="notranslate">allowSeparateTypeImports</code> option to <code
class="notranslate">no-duplicate-imports</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19872">#19872</a>)
(sethamus)</li>
<li>
<a
href="b8a7e7aeb5"><code
class="notranslate">b8a7e7a</code></a> feat: throw error when column is
negative in <code class="notranslate">getIndexFromLoc</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19831">#19831</a>)
(루밀LuMir)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="6a0f164543"><code
class="notranslate">6a0f164</code></a> fix: handle <code
class="notranslate">null</code> type <code
class="notranslate">loc</code> in <code
class="notranslate">getIndexFromLoc</code> method (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19862">#19862</a>)
(루밀LuMir)</li>
<li>
<a
href="3fbcd704a0"><code
class="notranslate">3fbcd70</code></a> fix: update error message for
<code class="notranslate">no-restricted-properties</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19855">#19855</a>)
(Tanuj Kanti)</li>
<li>
<a
href="7ef4cf7661"><code
class="notranslate">7ef4cf7</code></a> fix: remove unnecessary semicolon
from fixes (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19857">#19857</a>)
(Francesco Trotta)</li>
<li>
<a
href="7dabc38a84"><code
class="notranslate">7dabc38</code></a> fix: use <code
class="notranslate">process.version</code> in <code
class="notranslate">--env-info</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19865">#19865</a>)
(TKDev7)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="8662ed1f6d"><code
class="notranslate">8662ed1</code></a> docs: adopt eslint-stylistic sub
packages related changes (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19887">#19887</a>)
(ntnyq)</li>
<li>
<a
href="20158b09db"><code
class="notranslate">20158b0</code></a> docs: typo in comment for unused
variables handling (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19870">#19870</a>)
(leopardracer)</li>
<li>
<a
href="ebfb5b4613"><code
class="notranslate">ebfb5b4</code></a> docs: Fixed Typo in
configuration-files.md (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19873">#19873</a>)
(0-20)</li>
<li>
<a
href="4112fd0953"><code
class="notranslate">4112fd0</code></a> docs: clarify that boolean is
still allowed for rule <code class="notranslate">meta.deprecated</code>
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19866">#19866</a>)
(Bryan Mishkin)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="2b6491cd4b"><code
class="notranslate">2b6491c</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.30.0</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19889">#19889</a>)
(Francesco Trotta)</li>
<li>
<a
href="5a5d526103"><code
class="notranslate">5a5d526</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="eaf8a418af"><code
class="notranslate">eaf8a41</code></a> chore: Correct typos in linter
tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19878">#19878</a>)
(kilavvy)</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="edf232b680...6769b5fa11">See
the full diff on Github</a>. The new version differs by 23 commits:</p>
<ul>
<li><a
href="6769b5fa11"><code>9.30.1</code></a></li>
<li><a
href="b2950ace77"><code>Build:
changelog update for 9.30.1</code></a></li>
<li><a
href="b035f747c6"><code>chore:
upgrade to `@eslint/js@9.30.1` (#19906)</code></a></li>
<li><a
href="b3dbc16563"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="e91bb870f8"><code>fix:
allow separate default and named type imports (#19899)</code></a></li>
<li><a
href="ab7c62598a"><code>docs:
Update README</code></a></li>
<li><a
href="dae1e5bb27"><code>docs:
update jsdoc&#39;s link (#19896)</code></a></li>
<li><a
href="ad1d639517"><code>9.30.0</code></a></li>
<li><a
href="c02d70b718"><code>Build:
changelog update for 9.30.0</code></a></li>
<li><a
href="2b6491cd4b"><code>chore:
upgrade to `@eslint/js@9.30.0` (#19889)</code></a></li>
<li><a
href="5a5d526103"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="52a5fcaa4e"><code>feat:
Support `basePath` property in config objects (#19879)</code></a></li>
<li><a
href="6a0f164543"><code>fix:
handle `null` type `loc` in `getIndexFromLoc` method
(#19862)</code></a></li>
<li><a
href="8662ed1f6d"><code>docs:
adopt eslint-stylistic sub packages related changes
(#19887)</code></a></li>
<li><a
href="eaf8a418af"><code>chore:
Correct typos in linter tests (#19878)</code></a></li>
<li><a
href="4ab44823df"><code>feat:
add `allowSeparateTypeImports` option to `no-duplicate-imports`
(#19872)</code></a></li>
<li><a
href="3fbcd704a0"><code>fix:
update error message for `no-restricted-properties`
(#19855)</code></a></li>
<li><a
href="20158b09db"><code>docs:
typo in comment for unused variables handling (#19870)</code></a></li>
<li><a
href="ebfb5b4613"><code>docs:
Fixed Typo in configuration-files.md (#19873)</code></a></li>
<li><a
href="b8a7e7aeb5"><code>feat:
throw error when column is negative in `getIndexFromLoc`
(#19831)</code></a></li>
<li><a
href="7ef4cf7661"><code>fix:
remove unnecessary semicolon from fixes (#19857)</code></a></li>
<li><a
href="7dabc38a84"><code>fix:
use `process.version` in `--env-info` (#19865)</code></a></li>
<li><a
href="4112fd0953"><code>docs:
clarify that boolean is still allowed for rule `meta.deprecated`
(#19866)</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>
2025-07-14 10:06:09 -04:00
Jelf
fac8cd9def
fix: allow process.env.DEBUG to be a boolean in @tailwindcss/node (#18485)
TanStack Start build to `cloudflare-module`, `debug` value type is
boolean.
reference:
https://developers.cloudflare.com/workers/framework-guides/web-apps/tanstack/

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-07-11 11:51:50 -04:00
depfu[bot]
c3e2335308
Update @playwright/test 1.53.0 → 1.53.2 (patch) (#18487)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​playwright/test (1.53.0 → 1.53.2) ·
[Repo](https://github.com/Microsoft/playwright)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.53.2">1.53.2</a></h4>

<blockquote><h3 dir="auto">Highlights</h3>
<p dir="auto"><a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/36317">#36317</a>
- [Regression]: Merging pre-1.53 blob reports loses attachments<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/pull/36357">#36357</a>
- [Regression (Chromium)]: CDP missing trailing slash<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/36292">#36292</a>
- [Bug (MSEdge)]: Edge fails to launch when using <code
class="notranslate">msRelaunchNoCompatLayer</code></p>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 138.0.7204.23</li>
<li>Mozilla Firefox 139.0</li>
<li>WebKit 18.5</li>
</ul>
<p dir="auto">This version was also tested against the following stable
channels:</p>
<ul dir="auto">
<li>Google Chrome 137</li>
<li>Microsoft Edge 137</li>
</ul></blockquote>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.53.1">1.53.1</a></h4>

<blockquote><h3 dir="auto">Highlights</h3>
<p dir="auto"><a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/36339">#36339</a>
- [Regression]: Click can fail when scrolling required<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/36307">#36307</a>
- [Regression (Chromium)]: Under some scenarios filling a <code
class="notranslate">textarea</code> doesn't fill<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/issues/36294">#36294</a>
- [Regression (Firefox)]: <code
class="notranslate">setViewportSize</code> times out<br>
<a
href="https://bounce.depfu.com/github.com/microsoft/playwright/pull/36350">#36350</a>
- [Fix]: Display HTTP method for fetch trace entries</p>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 138.0.7204.23</li>
<li>Mozilla Firefox 139.0</li>
<li>WebKit 18.5</li>
</ul>
<p dir="auto">This version was also tested against the following stable
channels:</p>
<ul dir="auto">
<li>Google Chrome 137</li>
<li>Microsoft Edge 137</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="682277722c...8c38de4d13">See
the full diff on Github</a>. The new version differs by 10 commits:</p>
<ul>
<li><a
href="8c38de4d13"><code>chore:
mark v1.53.2 (#36502)</code></a></li>
<li><a
href="50d76d7910"><code>(#36462):
fix(chromium): fix compatibility with Edge msRelaunchNoCompatLayer
feature</code></a></li>
<li><a
href="48be646aa4"><code>cherry-pick(#36443):
fix(blob): correctly type pre-1.53 onTestEnd event for attachments
(#36476)</code></a></li>
<li><a
href="dc1555648b"><code>cherry-pick(#36377):
chore: follow-up to connectOverCDP fetch logic</code></a></li>
<li><a
href="4d0938cb2e"><code>cherry-pick(#36357):
fix: adding trialing slash detection logic back in
urlToWSEndpoint</code></a></li>
<li><a
href="3c60cb4766"><code>chore:
mark v1.53.1 (#36355)</code></a></li>
<li><a
href="b7032c23bf"><code>cherry-pick(#36350):
fix(trace): include method into &quot;Fetch&quot; action title
(#36352)</code></a></li>
<li><a
href="e0d1741710"><code>cherry-pick(#36346):
Revert &quot;chore: reduce scrolling during clicks
(#36175)&quot;</code></a></li>
<li><a
href="c568980885"><code>cherry-pick(#36340):
feat(firefox): roll to r1488</code></a></li>
<li><a
href="e773cddd24"><code>cherry-pick(#36301):
feat(chromium): roll to r1179 (#36301)</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>
2025-07-08 16:21:02 -04:00
depfu[bot]
bd9cf929d4
Update @vitejs/plugin-react 4.5.2 → 4.6.0 (minor) (#18477)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (4.5.2 → 4.6.0) ·
[Repo](https://github.com/vitejs/vite) ·
[Changelog](https://github.com/vitejs/vite/blob/main/packages/plugin-react/CHANGELOG.md)
















---
![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>
2025-07-08 08:17:58 -04:00
Jordan Pittman
2941a7b5c2
Track source locations through @plugin and @config (#18329)
Fixes https://github.com/tailwindlabs/tailwindcss-forms/issues/182

Inside JS plugins and configs we weren't tracking source location info
so using things like `addBase(…)` could result in warnings inside Vite's
url rewriting PostCSS plugin because not all declarations had a source
location.

The goal here is for calls to `addBase` to generate CSS that points back
to the `@plugin` or `@config` that resulted in it being called.
2025-07-03 11:20:54 -04:00
Robin Malfait
9169d73aad
update READMEs
Co-Authored-By: Adam Wathan <adam.wathan@gmail.com>
Co-Authored-By: Jonathan Reinink <jonathan@reinink.ca>
2025-07-02 22:49:47 +02:00
Adam Wathan
d2fdddf8d3
Update README.md (#18434)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

## Summary

<!--

Provide a summary of the issue and the changes you're making. How does
your change solve the problem?

-->

## Test plan

<!--

Explain how you tested your changes. Include the exact commands that you
used to verify the change works and include screenshots/screen
recordings of the update behavior in the browser if applicable.

-->
2025-07-02 16:40:30 -04:00
Jordan Pittman
8c8033017d
Detect classes inside Elixir charlist, word list, and string sigils (#18432)
Fixes #18431

The `~W(…)` syntax in Elixir is a _sigil_. It's basically another way to
write strings, arrays of strings, etc… The syntax lets you use a handful
of surrounding brackets like `~W(…)`, `~W[…]`, `~W{…}`, `~W"…"`, etc… to
let you write lists without necessarily having to escape characters.

In v3 our extractor was able to pick these up but in v4 Oxide does not.
I've added a preprocessor for Elixir files so we can modify the code
before our main extractor sees it.

Now things like this: `~W(text-white bg-gray-600)` will get turned into
` ~W text-white bg-gray-600 ` which can easily be processed by our
extractor.

The sigils we support are:
- `~s` / `~S` (strings)
- `~w` / `~W` (word lists)
- `~c` / `~C` (charlists)

We're specifically detecting the use of `(…)`, `[…]`, and `{…}` as using
quotes already works today.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-07-02 16:01:13 -04:00
Jonathan Reinink
3cab801761
Create FUNDING.yml 2025-07-02 13:22:16 -04:00
depfu[bot]
f307c31d45
Update enhanced-resolve 5.18.1 → 5.18.2 (patch) (#18423)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ enhanced-resolve (5.18.1 → 5.18.2) ·
[Repo](https://github.com/webpack/enhanced-resolve)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/webpack/enhanced-resolve/releases/tag/v5.18.2">5.18.2</a></h4>

<blockquote><h3 dir="auto">Fixes</h3>
<ul dir="auto">
<li>[Types] FileSystem type</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="9436f4d6d9...0bf45033f4">See
the full diff on Github</a>. The new version differs by 14 commits:</p>
<ul>
<li><a
href="0bf45033f4"><code>chore(release):
5.18.2</code></a></li>
<li><a
href="b2441769bd"><code>fix:
types</code></a></li>
<li><a
href="775f2fb8ed"><code>chore:
migrate to eslint-config-webpack (#453)</code></a></li>
<li><a
href="6df312e9a6"><code>chore:
fix tsconfig (#452)</code></a></li>
<li><a
href="b059bff8ce"><code>ci:
show report</code></a></li>
<li><a
href="c974464f46"><code>chore:
fix</code></a></li>
<li><a
href="29f9405129"><code>chore:
fix small stuff</code></a></li>
<li><a
href="01a04fd898"><code>chore:
refactor dev env</code></a></li>
<li><a
href="66a745681a"><code>ci:
show report</code></a></li>
<li><a
href="3bf44c7a6e"><code>ci:
node v24</code></a></li>
<li><a
href="bbbf6ab5b0"><code>ci:
node v24</code></a></li>
<li><a
href="38e9fd9acb"><code>feat:
export `SyncFileSystem` and `BaseFileSystem` types</code></a></li>
<li><a
href="3c9d4b6d51"><code>chore:
fix generation</code></a></li>
<li><a
href="4918c5b3a4"><code>feat:
export type SyncFileSystem and type BaseFileSystem</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>
2025-07-01 09:23:44 -04:00
Jordan Pittman
98158fd372 Fix failing test 2025-06-30 12:23:56 -04:00
Henrik Eneroth
05b65d59b5
Fix trailing ) from interfering with extraction in Clojure keywords (#18345)
## Summary

In a form like, 

```clojure 
(if condition :bg-white :bg-black)
```

`:bg-black` will fail to extract, while `:bg-white` is extracted as
expected. This PR fixes this case, implements more comprehensive
candidate filtering, and supersedes a previous PR.

Having recently submitted a PR for handling another special case with
Clojure keywords (the presence of `:` inside of keywords), I thought it
best to invert the previous strategy: Instead of handling special cases
one by one, consume keywords according to the Clojure reader spec.
Consume nothing else, other than strings.

Because of this, this PR is a tad more invasive rather than additive,
for which I apologize. The strategy is this:
- Strings begin with a `"` and ends with an unescaped `"`. Consume
everything between these delimiters (existing case).
- Keywords begin with `:`, and end with whitespace, or one out of a
small set of specific reserved characters. Everything else is a valid
character in a keyword. Consume everything between these delimiters, and
apply the class splitting previously contained in the outer loop. My
previous special case handling of `:` inside of keywords in #18338 is
now redundant (and is removed), as this is a more general solution.
- Discard _everything else_. 

I'm hoping that a strategy that is based on Clojure's definition of
strings and keywords will pre-empt any further issues with edge cases.

Closes #18344.

## Test plan
- Added failing tests.
- `cargo test` -> failure
- Added fix
- `cargo test` -> success

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-06-30 12:23:38 -04:00
depfu[bot]
7946db05ef
Update @types/bun 1.2.15 → 1.2.17 (patch) (#18409)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/bun (1.2.15 → 1.2.17) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-06-30 13:40:00 +00:00
depfu[bot]
ca7caae502
Update bun 1.2.15 → 1.2.17 (patch) (#18408)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ bun (1.2.15 → 1.2.17) · [Repo](https://github.com/oven-sh/bun)





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>
2025-06-30 09:31:26 -04:00
Jordan Pittman
2030e942bc
Don't consider the global important state in @apply (#18404)
Fixes #18400

In v3 when you used `important: true` it did not affect `@apply`.
However, in v4 it does and there's no way to make it *not*. This is
definitely a bug and would be unexpected for users coming from v3 who
use `@apply` and `important` together.

Basically, the following code, along with the detected utility `flex` in
source files…

```css
@import 'tailwindcss/utilities' important;
.flex-explicitly-important {
  @apply flex!;
}
.flex-not-important {
  @apply flex;
}
```

… would output this:
```css
.flex {
  display: flex !important;
}
.flex-explicitly-important  {
  display: flex !important;
}
.flex-not-important {
  display: flex !important;
}
```

But it's expected that `@apply` doesn't consider the "global" important
state. This PR addresss this problem and now the output is this:

```css
.flex {
  display: flex !important;
}
.flex-explicitly-important  {
  display: flex !important;
}
.flex-not-important {
  display: flex; /* this line changed */
}
```

If you want to mark a utility as important in `@apply` you can still use
`!` after the utility to do so as shown above.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-06-27 11:55:15 -04:00
depfu[bot]
b716d10129
Update all of nextjs 15.3.3 → 15.3.4 (patch) (#18401)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint-config-next (15.3.3 → 15.3.4)





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



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



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.3.4">15.3.4</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg 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>[metadata] render streaming metadata on the top level (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/80566">#80566</a>)</li>
<li>[fix] clone the config module to avoid mutation (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/80573">#80573</a>)</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/huozhi">@huozhi</a> for
helping!</p></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please let us
know.</a></em></p>
</details>


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>
2025-06-26 15:44:28 -04:00
Robin Malfait
b24457a9f4
Prepare 4.1.11 (#18397) 2025-06-26 12:43:17 +02:00
depfu[bot]
6ad26de66b
Update postcss 8.5.4 → 8.5.6 (patch) (#18395)
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?




#### ✳️ postcss (8.5.4 → 8.5.6) ·
[Repo](https://github.com/postcss/postcss) ·
[Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.6">8.5.6</a></h4>

<blockquote><ul dir="auto">
<li>Fixed <code class="notranslate">ContainerWithChildren</code> type
discriminating (by <a
href="https://bounce.depfu.com/github.com/Goodwine">@Goodwine</a>).</li>
</ul></blockquote>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.5">8.5.5</a></h4>

<blockquote><ul dir="auto">
<li>Fixed <code class="notranslate">package.json</code>→<code
class="notranslate">exports</code> compatibility with some tools (by <a
href="https://bounce.depfu.com/github.com/JounQin">@JounQin</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="6cb4a6673f...91d6eb5c3d">See
the full diff on Github</a>. The new version differs by 7 commits:</p>
<ul>
<li><a
href="91d6eb5c3d"><code>Release
8.5.6 version</code></a></li>
<li><a
href="65ffc55117"><code>Update
dependencies</code></a></li>
<li><a
href="ecd20eb7f9"><code>Fix
ContainerWithChildren to allow discriminating the node type by comparing
its type property (#2049)</code></a></li>
<li><a
href="c18159719e"><code>Release
8.5.5 version</code></a></li>
<li><a
href="c5523fbec5"><code>Update
dependencies</code></a></li>
<li><a
href="2e3450c55f"><code>refactor:
`import` should be listed before `require` (#2052)</code></a></li>
<li><a
href="4d720bd01a"><code>Update
EM text</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>
2025-06-26 11:28:01 +02:00
Rózsa Zoltán
aa859314d9
feat: add Vite 7 support to the @tailwindcss/vite plugin (#18384)
Closes #18381 

* [Changelog for Vite 7.0.0
(2025-06-24)](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#700-2025-06-24)

Starting from Vite 7, Node 18 support will be dropped, which doesn't
really affect Tailwind. It might be worth mentioning in the
documentation that the recommended minimum Node versions are 20.19 and
22.12.

Vite 7 is only available in ESM format, which is also not an issue.

Vite's browser support aligns with the v4 guidelines:
```
Chrome 87 → 107       (tw: 111)
Edge 88 → 107         (tw: 111)
Firefox 78 → 104      (tw: 128)
Safari 14.0 → 16.0    (tw: 16.4)
```
* [Vite 7 - Browser
Support](https://vite.dev/guide/migration.html#default-browser-target-change)
* [Tailwind CSS v4 - Browser
Support](https://tailwindcss.com/docs/compatibility#browser-support)

So, at first glance, there's nothing more to do except enabling support
for these versions.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-06-24 12:31:17 -04:00
depfu[bot]
b9007dd9f8
Update @types/react 19.1.6 → 19.1.8 (patch) (#18378)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.1.6 → 19.1.8) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-06-24 09:57:55 -04:00
depfu[bot]
767a60a195
Update postcss-import 16.1.0 → 16.1.1 (patch) (#18376)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ postcss-import (16.1.0 → 16.1.1) ·
[Repo](https://github.com/postcss/postcss-import) ·
[Changelog](https://github.com/postcss/postcss-import/blob/master/CHANGELOG.md)



<details>
<summary>Release Notes</summary>

<h4>16.1.1 (from changelog)</h4>
<blockquote><ul dir="auto">
<li>Fix incorrect cascade layer order when some resources can not be
inlined (<a
href="https://bounce.depfu.com/github.com/postcss/postcss-import/issues/567">#567</a>,
<a
href="https://bounce.depfu.com/github.com/postcss/postcss-import/pull/574">#574</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="9217ec361b...4ae9894edc">See
the full diff on Github</a>. The new version differs by 10 commits:</p>
<ul>
<li><a
href="4ae9894edc"><code>16.1.1</code></a></li>
<li><a
href="a3f38897da"><code>Test
on modern Node versions (#577)</code></a></li>
<li><a
href="10325fc024"><code>Upgrade
eslint &amp; config; use flat config (#576)</code></a></li>
<li><a
href="9227642040"><code>Migrate
config renovate.json (#575)</code></a></li>
<li><a
href="2544155491"><code>Update
dependency prettier to ~3.5.0 (#572)</code></a></li>
<li><a
href="83108aa207"><code>Fix
incorrect cascade layer order when some resources can not be inlined
(#574)</code></a></li>
<li><a
href="cad00220bf"><code>Update
dependency sugarss to v5 (#568)</code></a></li>
<li><a
href="32deb082c6"><code>Update
dependency c8 to v10 (#565)</code></a></li>
<li><a
href="d43ca1506d"><code>Update
dependency prettier to ~3.4.0 (#569)</code></a></li>
<li><a
href="9af465e598"><code>Update
dependency prettier to ~3.3.0 (#564)</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>
2025-06-24 09:57:35 -04:00
kilavvy
f0e2f5bb7a
Fix: Correct typo in comment (#18361)
This pull request contains a couple of minor documentation fixes.

- Corrected a typo from `predicable` to `predictable` in a comment for
`DarkModeStrategy`.
- Applied minor formatting to a comment in a test file.

These changes help improve code clarity and maintainability.
2025-06-21 19:40:11 +00:00
depfu[bot]
d788657774
Update eslint 9.27.0 → 9.29.0 (minor) (#18360)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.27.0 → 9.29.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.29.0">9.29.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="f686fcb51e"><code
class="notranslate">f686fcb</code></a> feat: add <code
class="notranslate">ecmaVersion: 2026</code>, parsing <code
class="notranslate">using</code> and <code class="notranslate">await
using</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19832">#19832</a>)
(Milos Djermanovic)</li>
<li>
<a
href="19cdd226bb"><code
class="notranslate">19cdd22</code></a> feat: prune suppressions for
non-existent files (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19825">#19825</a>)
(TKDev7)</li>
<li>
<a
href="b3d720f82f"><code
class="notranslate">b3d720f</code></a> feat: add ES2025 globals (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19835">#19835</a>)
(fisker Cheung)</li>
<li>
<a
href="677a2837a1"><code
class="notranslate">677a283</code></a> feat: add auto-accessor fields
support to class-methods-use-this (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19789">#19789</a>)
(sethamus)</li>
<li>
<a
href="dbba0589f5"><code
class="notranslate">dbba058</code></a> feat: allow global type
declaration in <code class="notranslate">no-var</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19714">#19714</a>)
(Remco Haszing)</li>
<li>
<a
href="342bd29e1a"><code
class="notranslate">342bd29</code></a> feat: ignore type annotations in
no-restricted-globals (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19781">#19781</a>)
(sethamus)</li>
<li>
<a
href="786bcd1365"><code
class="notranslate">786bcd1</code></a> feat: add allowProperties option
to no-restricted-properties (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19772">#19772</a>)
(sethamus)</li>
<li>
<a
href="05b66d05bd"><code
class="notranslate">05b66d0</code></a> feat: add <code
class="notranslate">sourceCode.isGlobalReference(node)</code> method (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19695">#19695</a>)
(Nitin Kumar)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="85c082c54b"><code
class="notranslate">85c082c</code></a> fix: explicit matching behavior
with negated patterns and arrays (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19845">#19845</a>)
(Milos Djermanovic)</li>
<li>
<a
href="9bda4a9bf1"><code
class="notranslate">9bda4a9</code></a> fix: fix <code
class="notranslate">LintOptions.filterCodeBlock</code> types (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19837">#19837</a>)
(ntnyq)</li>
<li>
<a
href="7ab77a2c76"><code
class="notranslate">7ab77a2</code></a> fix: correct breaking deprecation
of FlatConfig type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19826">#19826</a>)
(Logicer)</li>
<li>
<a
href="1ba33181ab"><code
class="notranslate">1ba3318</code></a> fix: add <code
class="notranslate">language</code> and <code
class="notranslate">dialects</code> to <code
class="notranslate">no-use-before-define</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19808">#19808</a>)
(Francesco Trotta)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="00e3e6ad13"><code
class="notranslate">00e3e6a</code></a> docs: add support for custom name
parameter to <code class="notranslate">includeIgnoreFile</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19795">#19795</a>)
(루밀LuMir)</li>
<li>
<a
href="3aed0756ed"><code
class="notranslate">3aed075</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="a2f888d679"><code
class="notranslate">a2f888d</code></a> docs: enhance documentation with
links and fix typos (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19761">#19761</a>)
(루밀LuMir)</li>
<li>
<a
href="53c3235ba1"><code
class="notranslate">53c3235</code></a> docs: update to clarify prompt
usage (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19748">#19748</a>)
(Jennifer Davis)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="5c114c962f"><code
class="notranslate">5c114c9</code></a> chore: upgrade @eslint/js@9.29.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19851">#19851</a>)
(Milos Djermanovic)</li>
<li>
<a
href="acf2201a06"><code
class="notranslate">acf2201</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="a806994263"><code
class="notranslate">a806994</code></a> refactor: Remove eslintrc from
flat config functionality (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19833">#19833</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="152ed51329"><code
class="notranslate">152ed51</code></a> test: switch to flat config mode
in code path analysis tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19824">#19824</a>)
(Milos Djermanovic)</li>
<li>
<a
href="b647239272"><code
class="notranslate">b647239</code></a> chore: Update first-party
dependencies faster with Renovate (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19822">#19822</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="7abe42e2de"><code
class="notranslate">7abe42e</code></a> refactor: SafeEmitter -&gt;
SourceCodeVisitor (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19708">#19708</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="e392895967"><code
class="notranslate">e392895</code></a> perf: improve time complexity of
<code class="notranslate">getLocFromIndex</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19782">#19782</a>)
(루밀LuMir)</li>
<li>
<a
href="0ed289c5ce"><code
class="notranslate">0ed289c</code></a> chore: remove accidentally
committed file (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19807">#19807</a>)
(Francesco Trotta)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.28.0">9.28.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="b0674be94e"><code
class="notranslate">b0674be</code></a> feat: Customization of
serialization for languageOptions (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19760">#19760</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="a95721f106"><code
class="notranslate">a95721f</code></a> feat: Add <code
class="notranslate">--pass-on-unpruned-suppressions</code> CLI option
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19773">#19773</a>)
(Milos Djermanovic)</li>
<li>
<a
href="bfd0e7a395"><code
class="notranslate">bfd0e7a</code></a> feat: support TypeScript syntax
in <code class="notranslate">no-use-before-define</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19566">#19566</a>)
(Tanuj Kanti)</li>
<li>
<a
href="68c61c093a"><code
class="notranslate">68c61c0</code></a> feat: support TS syntax in <code
class="notranslate">no-shadow</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19565">#19565</a>)
(Nitin Kumar)</li>
<li>
<a
href="0f773ef248"><code
class="notranslate">0f773ef</code></a> feat: support TS syntax in <code
class="notranslate">no-magic-numbers</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19561">#19561</a>)
(Nitin Kumar)</li>
<li>
<a
href="c4a6b60518"><code
class="notranslate">c4a6b60</code></a> feat: add allowTypeAnnotation to
func-style (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19754">#19754</a>)
(sethamus)</li>
<li>
<a
href="b03ad176f1"><code
class="notranslate">b03ad17</code></a> feat: add TypeScript support to
<code class="notranslate">prefer-arrow-callback</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19678">#19678</a>)
(Tanuj Kanti)</li>
<li>
<a
href="bc3c3313ce"><code
class="notranslate">bc3c331</code></a> feat: ignore overloaded function
declarations in func-style rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19755">#19755</a>)
(sethamus)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="eea3e7eb1c"><code
class="notranslate">eea3e7e</code></a> fix: Remove configured global
variables from <code class="notranslate">GlobalScope#implicit</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19779">#19779</a>)
(Milos Djermanovic)</li>
<li>
<a
href="a467de39f6"><code
class="notranslate">a467de3</code></a> fix: update context.report types
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19751">#19751</a>)
(Nitin Kumar)</li>
<li>
<a
href="fd467bb892"><code
class="notranslate">fd467bb</code></a> fix: remove interopDefault to use
jiti's default (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19697">#19697</a>)
(sethamus)</li>
<li>
<a
href="72d16e3066"><code
class="notranslate">72d16e3</code></a> fix: avoid false positive in
<code class="notranslate">no-unassigned-vars</code> for declare module
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19746">#19746</a>)
(Azat S.)</li>
<li>
<a
href="81c3c93626"><code
class="notranslate">81c3c93</code></a> fix: curly types (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19750">#19750</a>)
(Eli)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="3ec208233f"><code
class="notranslate">3ec2082</code></a> docs: Nested arrays in files
config entry (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19799">#19799</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="89a65b07f6"><code
class="notranslate">89a65b0</code></a> docs: clarify how config arrays
can apply to subsets of files (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19788">#19788</a>)
(Shais Ch)</li>
<li>
<a
href="2ba8a0d75c"><code
class="notranslate">2ba8a0d</code></a> docs: Add description of
meta.namespace to plugin docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19798">#19798</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="59dd7e6b28"><code
class="notranslate">59dd7e6</code></a> docs: update <code
class="notranslate">func-style</code> with examples (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19793">#19793</a>)
(Tanuj Kanti)</li>
<li>
<a
href="e9129e0799"><code
class="notranslate">e9129e0</code></a> docs: add global scope's <code
class="notranslate">implicit</code> field to Scope Manager docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19770">#19770</a>)
(Milos Djermanovic)</li>
<li>
<a
href="52f5b7a0af"><code
class="notranslate">52f5b7a</code></a> docs: fix minor typos and add
links (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19743">#19743</a>)
(루밀LuMir)</li>
<li>
<a
href="00716a339e"><code
class="notranslate">00716a3</code></a> docs: upfront recommend against
using the no-return-await rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19727">#19727</a>)
(Mike DiDomizio)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="175b7b83fc"><code
class="notranslate">175b7b8</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.28.0</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19802">#19802</a>)
(Francesco Trotta)</li>
<li>
<a
href="844f5a69dc"><code
class="notranslate">844f5a6</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="62b1c1bc79"><code
class="notranslate">62b1c1b</code></a> chore: update globals to v16 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19791">#19791</a>)
(Nitin Kumar)</li>
<li>
<a
href="e8a1cb8f7f"><code
class="notranslate">e8a1cb8</code></a> chore: ignore jiti-v2.0 &amp;
jiti-v2.1 for renovate (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19786">#19786</a>)
(Nitin Kumar)</li>
<li>
<a
href="43d39754b6"><code
class="notranslate">43d3975</code></a> chore: Add Copilot Instructions
file (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19753">#19753</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="2dfb5ebef4"><code
class="notranslate">2dfb5eb</code></a> test: update <code
class="notranslate">SourceCodeTraverser</code> tests (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19763">#19763</a>)
(Milos Djermanovic)</li>
<li>
<a
href="5bc21f9e8e"><code
class="notranslate">5bc21f9</code></a> chore: add <code
class="notranslate">*.code-workspace</code> to <code
class="notranslate">.gitignore</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19771">#19771</a>)
(루밀LuMir)</li>
<li>
<a
href="f4fa40eb4b"><code
class="notranslate">f4fa40e</code></a> refactor: NodeEventGenerator
-&gt; SourceCodeTraverser (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19679">#19679</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="0f49329b4a"><code
class="notranslate">0f49329</code></a> refactor: use a service to emit
warnings (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19725">#19725</a>)
(Francesco Trotta)</li>
<li>
<a
href="20a9e59438"><code
class="notranslate">20a9e59</code></a> chore: update dependency shelljs
to ^0.10.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19740">#19740</a>)
(renovate[bot])</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="b9080cf28d...edf232b680">See
the full diff on Github</a>. The new version differs by 58 commits:</p>
<ul>
<li><a
href="edf232b680"><code>9.29.0</code></a></li>
<li><a
href="c2414b647b"><code>Build:
changelog update for 9.29.0</code></a></li>
<li><a
href="5c114c962f"><code>chore:
upgrade @eslint/js@9.29.0 (#19851)</code></a></li>
<li><a
href="acf2201a06"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="f686fcb51e"><code>feat:
add `ecmaVersion: 2026`, parsing `using` and `await using`
(#19832)</code></a></li>
<li><a
href="85c082c54b"><code>fix:
explicit matching behavior with negated patterns and arrays
(#19845)</code></a></li>
<li><a
href="00e3e6ad13"><code>docs:
add support for custom name parameter to `includeIgnoreFile`
(#19795)</code></a></li>
<li><a
href="9bda4a9bf1"><code>fix:
fix `LintOptions.filterCodeBlock` types (#19837)</code></a></li>
<li><a
href="a806994263"><code>refactor:
Remove eslintrc from flat config functionality (#19833)</code></a></li>
<li><a
href="19cdd226bb"><code>feat:
prune suppressions for non-existent files (#19825)</code></a></li>
<li><a
href="b3d720f82f"><code>feat:
add ES2025 globals (#19835)</code></a></li>
<li><a
href="677a2837a1"><code>feat:
add auto-accessor fields support to class-methods-use-this
(#19789)</code></a></li>
<li><a
href="3aed0756ed"><code>docs:
Update README</code></a></li>
<li><a
href="7ab77a2c76"><code>fix:
correct breaking deprecation of FlatConfig type (#19826)</code></a></li>
<li><a
href="a2f888d679"><code>docs:
enhance documentation with links and fix typos (#19761)</code></a></li>
<li><a
href="dbba0589f5"><code>feat:
allow global type declaration in `no-var` (#19714)</code></a></li>
<li><a
href="152ed51329"><code>test:
switch to flat config mode in code path analysis tests
(#19824)</code></a></li>
<li><a
href="b647239272"><code>chore:
Update first-party dependencies faster with Renovate
(#19822)</code></a></li>
<li><a
href="7abe42e2de"><code>refactor:
SafeEmitter -&gt; SourceCodeVisitor (#19708)</code></a></li>
<li><a
href="342bd29e1a"><code>feat:
ignore type annotations in no-restricted-globals
(#19781)</code></a></li>
<li><a
href="e392895967"><code>perf:
improve time complexity of `getLocFromIndex` (#19782)</code></a></li>
<li><a
href="1ba33181ab"><code>fix:
add `language` and `dialects` to `no-use-before-define`
(#19808)</code></a></li>
<li><a
href="786bcd1365"><code>feat:
add allowProperties option to no-restricted-properties
(#19772)</code></a></li>
<li><a
href="05b66d05bd"><code>feat:
add `sourceCode.isGlobalReference(node)` method (#19695)</code></a></li>
<li><a
href="53c3235ba1"><code>docs:
update to clarify prompt usage (#19748)</code></a></li>
<li><a
href="0ed289c5ce"><code>chore:
remove accidentally committed file (#19807)</code></a></li>
<li><a
href="f341f21e02"><code>9.28.0</code></a></li>
<li><a
href="779dda93a2"><code>Build:
changelog update for 9.28.0</code></a></li>
<li><a
href="175b7b83fc"><code>chore:
upgrade to `@eslint/js@9.28.0` (#19802)</code></a></li>
<li><a
href="844f5a69dc"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="b0674be94e"><code>feat:
Customization of serialization for languageOptions
(#19760)</code></a></li>
<li><a
href="3ec208233f"><code>docs:
Nested arrays in files config entry (#19799)</code></a></li>
<li><a
href="89a65b07f6"><code>docs:
clarify how config arrays can apply to subsets of files
(#19788)</code></a></li>
<li><a
href="2ba8a0d75c"><code>docs:
Add description of meta.namespace to plugin docs
(#19798)</code></a></li>
<li><a
href="eea3e7eb1c"><code>fix:
Remove configured global variables from `GlobalScope#implicit`
(#19779)</code></a></li>
<li><a
href="a95721f106"><code>feat:
Add `--pass-on-unpruned-suppressions` CLI option
(#19773)</code></a></li>
<li><a
href="a467de39f6"><code>fix:
update context.report types (#19751)</code></a></li>
<li><a
href="59dd7e6b28"><code>docs:
update `func-style` with examples (#19793)</code></a></li>
<li><a
href="62b1c1bc79"><code>chore:
update globals to v16 (#19791)</code></a></li>
<li><a
href="bfd0e7a395"><code>feat:
support TypeScript syntax in `no-use-before-define`
(#19566)</code></a></li>
<li><a
href="68c61c093a"><code>feat:
support TS syntax in `no-shadow` (#19565)</code></a></li>
<li><a
href="e8a1cb8f7f"><code>chore:
ignore jiti-v2.0 &amp; jiti-v2.1 for renovate (#19786)</code></a></li>
<li><a
href="0f773ef248"><code>feat:
support TS syntax in `no-magic-numbers` (#19561)</code></a></li>
<li><a
href="43d39754b6"><code>chore:
Add Copilot Instructions file (#19753)</code></a></li>
<li><a
href="c4a6b60518"><code>feat:
add allowTypeAnnotation to func-style (#19754)</code></a></li>
<li><a
href="fd467bb892"><code>fix:
remove interopDefault to use jiti&#39;s default (#19697)</code></a></li>
<li><a
href="2dfb5ebef4"><code>test:
update `SourceCodeTraverser` tests (#19763)</code></a></li>
<li><a
href="b03ad176f1"><code>feat:
add TypeScript support to `prefer-arrow-callback`
(#19678)</code></a></li>
<li><a
href="e9129e0799"><code>docs:
add global scope&#39;s `implicit` field to Scope Manager docs
(#19770)</code></a></li>
<li><a
href="bc3c3313ce"><code>feat:
ignore overloaded function declarations in func-style rule
(#19755)</code></a></li>
<li><a
href="5bc21f9e8e"><code>chore:
add `*.code-workspace` to `.gitignore` (#19771)</code></a></li>
<li><a
href="72d16e3066"><code>fix:
avoid false positive in `no-unassigned-vars` for declare module
(#19746)</code></a></li>
<li><a
href="f4fa40eb4b"><code>refactor:
NodeEventGenerator -&gt; SourceCodeTraverser (#19679)</code></a></li>
<li><a
href="81c3c93626"><code>fix:
curly types (#19750)</code></a></li>
<li><a
href="52f5b7a0af"><code>docs:
fix minor typos and add links (#19743)</code></a></li>
<li><a
href="0f49329b4a"><code>refactor:
use a service to emit warnings (#19725)</code></a></li>
<li><a
href="20a9e59438"><code>chore:
update dependency shelljs to ^0.10.0 (#19740)</code></a></li>
<li><a
href="00716a339e"><code>docs:
upfront recommend against using the no-return-await rule
(#19727)</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>
2025-06-20 14:03:07 -04:00
depfu[bot]
1a35fbce1a
Update @vitejs/plugin-react 4.5.0 → 4.5.2 (patch) (#18352)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (4.5.0 → 4.5.2) ·
[Repo](https://github.com/vitejs/vite) ·
[Changelog](https://github.com/vitejs/vite/blob/main/packages/plugin-react/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vitejs/vite/releases/tag/v4.5.2">4.5.2</a></h4>

<blockquote><p dir="auto">Please refer to <a
href="https://bounce.depfu.com/github.com/vitejs/vite/blob/v4.5.2/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p></blockquote>
<h4><a
href="https://github.com/vitejs/vite/releases/tag/v4.5.1">4.5.1</a></h4>

<blockquote><p dir="auto">Please refer to <a
href="https://bounce.depfu.com/github.com/vitejs/vite/blob/v4.5.1/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please
let us know.</a></em></p>
</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>
2025-06-20 06:23:57 -04:00
Jordan Pittman
c5a997cbdb
Document --watch=always in the CLI (#18337)
Closes https://github.com/tailwindlabs/tailwindcss.com/issues/1835

We had this in v3 as an undocumented option. We still have it in v4 but
it's not documented under the CLI usage but should be. This PR adds
this.

**before**
<img width="803" alt="Screenshot 2025-06-18 at 09 41 40"
src="https://github.com/user-attachments/assets/c3becf11-e31d-4355-9d23-bddd0b2fc4a6"
/>

**after**
<img width="1152" alt="Screenshot 2025-06-18 at 09 41 20"
src="https://github.com/user-attachments/assets/4f61a156-680d-4f39-b92d-7f0f63270689"
/>
2025-06-19 14:52:47 -04:00
Jordan Pittman
75cbfc251d Update changelog 2025-06-18 12:38:54 -04:00
Henrik Eneroth
f4a7eea6cc
Extract candidates with variants in Clojure/ClojureScript keywords (#18338)
## Summary

Taking a shot at fixing my own complaint from #18336. 

Added failing test and fix for extracting classes containing a
pseudo-class from Clojure keywords. Eg., `:hover:text` ->
`"hover:text"`. This would previously produce `["hover", "text"]`.

## Test plan

- Add a failing test. 
- Verify that it fails with `cargo test`. 
- Add fix. 
- Verify that `cargo test` has no further complaints. 

ATT: @RobinMalfait

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-06-18 12:38:27 -04:00
depfu[bot]
63b5d7bdab
Update @playwright/test 1.52.0 → 1.53.0 (minor) (#18332)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​playwright/test (1.52.0 → 1.53.0) ·
[Repo](https://github.com/Microsoft/playwright)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/microsoft/playwright/releases/tag/v1.53.0">1.53.0</a></h4>

<blockquote><h2 dir="auto">Trace Viewer and HTML Reporter Updates</h2>
<ul dir="auto">
<li>
<p dir="auto">New Steps in Trace Viewer and HTML reporter:<br>
<a
href="https://private-user-images.githubusercontent.com/17984549/451416137-1963ff7d-4070-41be-a79b-4333176921a2.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDk2NzA4MjAsIm5iZiI6MTc0OTY3MDUyMCwicGF0aCI6Ii8xNzk4NDU0OS80NTE0MTYxMzctMTk2M2ZmN2QtNDA3MC00MWJlLWE3OWItNDMzMzE3NjkyMWEyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA2MTElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNjExVDE5MzUyMFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ5YjQzYWVjZGQ5Y2E4NjgwNWY4NWJmNzg0ODA4YmNlMTg2ZjdmNzliODllNmU3YmEzOGIzM2JlMjg2Y2ExODcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.tF0t1fNEXfC-WalH0bVazGMGwZ_FnsBkxJgn7Dk6za0"><img
src="https://private-user-images.githubusercontent.com/17984549/451416137-1963ff7d-4070-41be-a79b-4333176921a2.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NDk2NzA4MjAsIm5iZiI6MTc0OTY3MDUyMCwicGF0aCI6Ii8xNzk4NDU0OS80NTE0MTYxMzctMTk2M2ZmN2QtNDA3MC00MWJlLWE3OWItNDMzMzE3NjkyMWEyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTA2MTElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwNjExVDE5MzUyMFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTQ5YjQzYWVjZGQ5Y2E4NjgwNWY4NWJmNzg0ODA4YmNlMTg2ZjdmNzliODllNmU3YmEzOGIzM2JlMjg2Y2ExODcmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.tF0t1fNEXfC-WalH0bVazGMGwZ_FnsBkxJgn7Dk6za0"
alt="New Trace Viewer Steps" height="500" style="max-width:
100%;"></a></p>
</li>
<li>
<p dir="auto">New option in <code class="notranslate">'html'</code>
reporter to set the title of a specific test run:</p>
<div class="highlight highlight-source-js" dir="auto"><pre
class="notranslate"><span class="pl-k">import</span> <span
class="pl-kos">{</span> <span class="pl-s1">defineConfig</span> <span
class="pl-kos">}</span> <span class="pl-k">from</span> <span
class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">default</span> <span
class="pl-en">defineConfig</span><span class="pl-kos">(</span><span
class="pl-kos">{</span>
<span class="pl-c1">reporter</span>: <span class="pl-kos">[</span><span
class="pl-kos">[</span><span class="pl-s">'html'</span><span
class="pl-kos">,</span> <span class="pl-kos">{</span> <span
class="pl-c1">title</span>: <span class="pl-s">'Custom test run
#1028'</span> <span class="pl-kos">}</span><span
class="pl-kos">]</span><span class="pl-kos">]</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span
class="pl-kos">;</span></pre></div>
</li>
</ul>
<h2 dir="auto">Miscellaneous</h2>
<ul dir="auto">
<li>
<p dir="auto">New option <a
href="https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-path-option-kind"><code
class="notranslate">kind</code></a> in <a
href="https://playwright.dev/docs/api/class-testinfo#test-info-snapshot-path">testInfo.snapshotPath()</a>
controls which snapshot path template is used.</p>
</li>
<li>
<p dir="auto">New method <a
href="https://playwright.dev/docs/api/class-locator#locator-describe">locator.describe()</a>
to describe a locator. Used for trace viewer and reports.</p>
<div class="highlight highlight-source-js" dir="auto"><pre
class="notranslate"><span class="pl-k">const</span> <span
class="pl-s1">button</span> <span class="pl-c1">=</span> <span
class="pl-s1">page</span><span class="pl-kos">.</span><span
class="pl-en">getByTestId</span><span class="pl-kos">(</span><span
class="pl-s">'btn-sub'</span><span class="pl-kos">)</span><span
class="pl-kos">.</span><span class="pl-en">describe</span><span
class="pl-kos">(</span><span class="pl-s">'Subscribe button'</span><span
class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-k">await</span> <span class="pl-s1">button</span><span
class="pl-kos">.</span><span class="pl-en">click</span><span
class="pl-kos">(</span><span class="pl-kos">)</span><span
class="pl-kos">;</span></pre></div>
</li>
<li>
<p dir="auto"><code class="notranslate">npx playwright install
--list</code> will now list all installed browsers, versions and
locations.</p>
</li>
</ul>
<h2 dir="auto">Browser Versions</h2>
<ul dir="auto">
<li>Chromium 138.0.7204.4</li>
<li>Mozilla Firefox 139.0</li>
<li>WebKit 18.5</li>
</ul>
<p dir="auto">This version was also tested against the following stable
channels:</p>
<ul dir="auto">
<li>Google Chrome 137</li>
<li>Microsoft Edge 137</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="471930b1ce...682277722c">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
2025-06-17 14:48:33 -04:00
Jordan Pittman
5fc6698b7b
Add heuristic to skip candidate migrations inside emit(…) (#18330)
Fixes #18318
2025-06-17 12:49:33 -04:00
leopardracer
44534963c3
Small Typo Fixes and Comment Improvements (#18328)
Description:
This pull request corrects minor typos in comments and improves clarity
in two files:
- Fixes a typo in a comment within migrate-js-config.ts ("migrateable" →
"migratable").
- Refines a comment in wasm.test.ts for better readability.

No functional code changes are included.
2025-06-17 10:46:08 -04:00
depfu[bot]
bab16aeb8c
Update @napi-rs/wasm-runtime 0.2.10 → 0.2.11 (minor) (#18316)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/wasm-runtime (0.2.10 → 0.2.11) ·
[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>
2025-06-16 10:27:00 -04:00
Robin Malfait
7f97179b66
update changelog
drop the `:`
2025-06-11 23:21:57 +02:00
Robin Malfait
2ebaff214a
make changelog entry consistent 2025-06-11 23:07:12 +02:00
Robin Malfait
d06bbb80da
Prepare v4.1.10 (#18290) 2025-06-11 22:26:51 +02:00
Robin Malfait
ddb0beff66
Fix missing space around - when using % regression (#18289)
This PR fixes a regression we shipped in v4.1.9, when using arbitrary
values and injecting spaces around operator.

When you use `w-[calc(100%-var(--foo))]`, you expect that this generates
valid CSS:

```css
width: calc(100% - var(--foo));
```

But due to a regression, we generated:

```css
width: calc(100%-var(--foo));
```

Which is invalid CSS.

This is because the algorithm we used to know when we had to inject a
space around the `-` didn't take the `%` sign into account.

We also didn't handle uppercase units like `123PX` properly. This PR
fixes both issues.

## Test plan

1. Added a regression test for the `%`
2. Added a regression test for uppercase units like `123PX`

Fixes: #18288
2025-06-11 20:18:14 +00:00
depfu[bot]
427649eee4
Update @types/react-dom 19.1.5 → 19.1.6 (patch) (#18284)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react-dom (19.1.5 → 19.1.6) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-06-11 13:57:02 -04:00
Robin Malfait
b88371a309
Prepare v4.1.9 (#18285)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-06-11 15:57:38 +02:00
Rózsa Zoltán
aa817fb6de
fix: don't break CSS keywords when formatting math expressions (#18220)
Fixes #18219

## Summary

In an arbitrary value, if there's a non-numeric character both before
and after a hyphen, there's no need for a space.

## Test plan

`decodeArbitraryValue` will correctly format special CSS values like
`fit-content`. I believe spaces are only necessary if there's a digit
either before or after the hyphen.

```js
decodeArbitraryValue('min(fit-content,calc(100dvh-4rem))')
```

This way, the result of the following arbitrary value will also be
correct:

```html
<div class="min-h-[min(fit-content,calc(100dvh-4rem))]"></div>
```

```css
.min-h-\[min\(fit-content\,calc\(100dvh-4rem\)\)\] {
  min-height: min(fit-content, calc(100dvh - 4rem));
}
```

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-06-10 10:38:31 -04:00
depfu[bot]
da0895655e
Update @types/react 19.1.4 → 19.1.6 (patch) (#18267)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.1.4 → 19.1.6) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-06-10 07:07:41 -04:00
Cyril Duez
bea843c90a
CSS Parser: Handle string with semi-colon in custom properties. (#18251)
Strings are not parsed correctly for custom properties which makes the
following CSS raise an `Unterminated string: ";"` error:

```css
:root { 
  --custom: 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ==';
}
```

According to the spec, we should accept semi-colon as long as they are
not at the top level.
> The allowed syntax for [custom
properties](https://drafts.csswg.org/css-variables/#custom-property) is
extremely permissive. The <declaration-value> production matches any
sequence of one or more tokens, so long as the sequence does not contain
bad-string-token, bad-url-token, unmatched )-token, ]-token, or }-token,
or top-level semicolon-token tokens or delim-token tokens with a value
of "!".

Extract from: https://drafts.csswg.org/css-variables/#syntax

I was only able to reproduce with **tailwindcss v4**, the previous
version seems to support this. This issue is mitigated by the fact that
even if you want to use a data URL in a custom property, you would need
to wrap the value in a `url()` anyway:

```css
:root { 
  --my-icon-url: url('data:image/svg+xml;base64,...==');
}

.icon {
  background-image: var(--my-icon-url);
}
```

Which works perfectly fine with the current/latest version (v4.1.8).

The fix suggested is to share the same code between regular property and
custom property when it comes to detect that the value is a string
starting with a `SINGLE_QUOTE` or `DOUBLE_QUOTE`. I have moved the
existing code in a `findEndStringIdx` which returns the position of the
ending single/double quote.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-06-09 14:03:44 +00:00
depfu[bot]
fd95af4284
Update bun 1.2.13 → 1.2.15 (patch) (#18252)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ bun (1.2.13 → 1.2.15) · [Repo](https://github.com/oven-sh/bun)





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>
2025-06-09 09:23:05 -04:00
Robin Malfait
f0f42f62c0
Use shards when running integration tests (#18246)
This PR splits CI integration tests in 3 shards to reduce the time it
takes to run them to hopefully prevent timeouts on a single step.
2025-06-06 19:43:17 +00:00
depfu[bot]
ada85b1f24
Update turbo 2.5.3 → 2.5.4 (patch) (#18243)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ turbo (2.5.3 → 2.5.4) ·
[Repo](https://github.com/turborepo/turbo)





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>
2025-06-06 12:14:04 -04:00
depfu[bot]
21ece6c6da
Update all of nextjs 15.3.2 → 15.3.3 (patch) (#18234)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint-config-next (15.3.2 → 15.3.3)





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



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



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vercel/next.js/releases/tag/v15.3.3">15.3.3</a></h4>

<blockquote><div class="markdown-alert markdown-alert-note" dir="auto">
<p class="markdown-alert-title" dir="auto"><svg 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>Reinstate <code class="notranslate">vary</code> (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/79939">#79939</a>)</li>
<li>fix(next-swc): Fix interestingness detection for React Compiler (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/79558">#79558</a>)</li>
<li>fix(next-swc): Fix react compiler usefulness detector (<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/79480">#79480</a>)</li>
<li>fix(dev-overlay): Better handle edge-case file paths in launchEditor
(<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/79526">#79526</a>)</li>
<li>Client router should discard stale prefetch entries for static pages
(<a
href="https://bounce.depfu.com/github.com/vercel/next.js/pull/79362">#79362</a>)</li>
</ul>
<h3 dir="auto">Credits</h3>
<p dir="auto">Huge thanks to <a
href="https://bounce.depfu.com/github.com/gaojude">@gaojude</a>, <a
href="https://bounce.depfu.com/github.com/kdy1">@kdy1</a>, <a
href="https://bounce.depfu.com/github.com/bgw">@bgw</a>, and <a
href="https://bounce.depfu.com/github.com/unstubbable">@unstubbable</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="d9ec4a4b57...3ab8db7383">See
the full diff on Github</a>. The new version differs by 8 commits:</p>
<ul>
<li><a
href="3ab8db7383"><code>v15.3.3</code></a></li>
<li><a
href="18c8113ebd"><code>[backport]
Reinstate `vary` (#79939)</code></a></li>
<li><a
href="e18212f546"><code>re-enable
vary header deploy test (#79753)</code></a></li>
<li><a
href="ec202eccf0"><code>Revert
&quot;[next-server] skip setting vary header for basic routes&quot;
(#79426)</code></a></li>
<li><a
href="e2f264fdce"><code>fix(next-swc):
Fix interestingness detection for React Compiler (15.3)
(#79558)</code></a></li>
<li><a
href="562fac78da"><code>fix(next-swc):
Fix react compiler usefulness detector (15.3) (#79480)</code></a></li>
<li><a
href="06097fd7bb"><code>fix(dev-overlay):
Better handle edge-case file paths in launchEditor
(#79526)</code></a></li>
<li><a
href="bda731fa96"><code>Client
router should discard stale prefetch entries for static pages
(#79362)</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>
2025-06-05 17:02:09 -04:00
depfu[bot]
63f6a6c4c8
Update @types/bun 1.2.13 → 1.2.15 (patch) (#18222)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/bun (1.2.13 → 1.2.15) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-06-05 13:30:12 -04:00
depfu[bot]
288ab3e879
Update postcss 8.5.3 → 8.5.4 (patch) (#18225)
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?




#### ✳️ postcss (8.5.3 → 8.5.4) ·
[Repo](https://github.com/postcss/postcss) ·
[Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/postcss/postcss/releases/tag/8.5.4">8.5.4</a></h4>

<blockquote><ul dir="auto">
<li>Fixed Parcel compatibility issue (by <a
href="https://bounce.depfu.com/github.com/git-sumitchaudhary">@git-sumitchaudhary</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="22c309d329...6cb4a6673f">See
the full diff on Github</a>. The new version differs by 23 commits:</p>
<ul>
<li><a
href="6cb4a6673f"><code>Release
8.5.4 version</code></a></li>
<li><a
href="ec5c1e0310"><code>Update
dependencies</code></a></li>
<li><a
href="e85e9385c8"><code>Fix
code format</code></a></li>
<li><a
href="50542335f2"><code>fixed
error at line 401 (#2046)</code></a></li>
<li><a
href="0538b6309e"><code>docs:
Update README.md (#2044)</code></a></li>
<li><a
href="b5f407f397"><code>Add
`postcss-fontsource-url` to plugins (#2043)</code></a></li>
<li><a
href="94b5260be6"><code>Clarify
documentation for `node.source.end.offset` (#2032)</code></a></li>
<li><a
href="a20724af14"><code>Fix
Markdown syntax</code></a></li>
<li><a
href="cf6b969751"><code>fix:
add a mention for postcssense extension (#2040)</code></a></li>
<li><a
href="ff48c29ebb"><code>Increase
size</code></a></li>
<li><a
href="f82c2a6090"><code>Add
`Input#fromLineAndColumn`, `CssSyntaxError.input.offset` and
`CssSyntaxError.input.endOffset` (#2034)</code></a></li>
<li><a
href="7892a29ff8"><code>Fix
missing offset in `Node#positionInside`, `Node#positionBy` and
`Node#rangeBy` (#2033)</code></a></li>
<li><a
href="875f1fb7f8"><code>Fix
`opts` argument of `Node#rangeBy()` and `Node#positionBy()` cannot be
omitted (#2031)</code></a></li>
<li><a
href="25443a5930"><code>Update
dependencies</code></a></li>
<li><a
href="0a76df5f60"><code>add
postcss-auto-var-fallback fallback plugin to plugins.md
(#2026)</code></a></li>
<li><a
href="3fc6388e91"><code>Update
Nano ID</code></a></li>
<li><a
href="368bd00920"><code>Change
in type definition of property `css` of `result` (#2025)</code></a></li>
<li><a
href="9229218b51"><code>Update
dependencies</code></a></li>
<li><a
href="6c2a808c4a"><code>Clean
up code</code></a></li>
<li><a
href="31a5de71d1"><code>Return
type of method `raw` of `Stringifier` can be boolean, as the property
`semicolon` can be boolean (#2024)</code></a></li>
<li><a
href="c5058912f2"><code>There
are no other `props` with non-string type.</code></a></li>
<li><a
href="a9cf39e7fe"><code>The
return type of the method rawValue in Stringifier should be `any`
instead of `string`</code></a></li>
<li><a
href="7ac43db0c3"><code>Update
ESLint config</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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-06-05 09:34:20 -04:00
nakanoh
8bfbac5d20
docs: fix typo in container.ts comment ("a the" -> "the") (#18223)
## Description
Fixed a typo in the container.ts comment where "a the" was incorrectly
written as "the" for better readability.

## Test Plan
- [x] Verified the change is purely documentation-related and doesn't
affect functionality
- [x] Ensured the comment is more readable and grammatically correct
- [x] No tests needed as this is a documentation-only change

## Changes
- Modified comment in `packages/tailwindcss/src/compat/container.ts`
- Changed "When setting a the `screens` in v3" to "When setting the
`screens` in v3"

Co-authored-by: 中野 博文 <hirofumi0082@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-06-05 09:21:56 -04:00
nakanoh
0c1c0c4aa5
docs: fix typo ("a arbitrary" -> "an arbitrary") (#18227)
## Summary
- Fixes a typo: "a arbitrary" → "an arbitrary" in a comment/description.

## Details
- This is a documentation-only change. No code logic is affected.

## Test Plan
- N/A (doc-only)

Co-authored-by: 中野 博文 <hirofumi0082@gmail.com>
2025-06-05 09:30:31 +00:00
Robin Malfait
f425720c24
Do not migrate blur in wire:model.blur (#18216)
This PR fixes an issue where the `blur` in `wire:model.blur="…"` was
incorrectly migrated. We solved it by marking `wire:…` as an unsafe
region (`…` can be anything but whitespace).

Fixes: #18187

## Test plan

Added a test with this use case

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-06-03 18:27:59 +00:00
Robin Malfait
b3fde17eac
Migrate negative arbitrary values to negative bare values (#18212)
This PR adds some improvements to the upgrade tool where it can now also
migrate negative arbitrary values to negative bare values.

We already had support for the positive version of this:
```diff
- mb-[32rem]
+ mb-128
```

But now it can also handle negative values:
```diff
- mb-[-32rem]
+ -mb-128
```

The only tricky part here is that we had to hoist the `-` sign. Before
this PR, we were actually generating `mb--128` and that is invalid so it
was thrown out.

## Test plan

1. Added a test to ensure that the negative values are correctly
transformed.
2025-06-03 08:53:58 +00:00
depfu[bot]
191195af7e
Update @types/react-dom 19.1.3 → 19.1.5 (patch) (#18198)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react-dom (19.1.3 → 19.1.5) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-06-02 09:39:20 -04:00
depfu[bot]
54c86d4be7
Update @vitejs/plugin-react 4.4.1 → 4.5.0 (minor) (#18181)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​vitejs/plugin-react (4.4.1 → 4.5.0) ·
[Repo](https://github.com/vitejs/vite) ·
[Changelog](https://github.com/vitejs/vite/blob/main/packages/plugin-react/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/vitejs/vite/releases/tag/v4.5.0">4.5.0</a></h4>

<blockquote><p dir="auto">Please refer to <a
href="https://bounce.depfu.com/github.com/vitejs/vite/blob/v4.5.0/packages/vite/CHANGELOG.md">CHANGELOG.md</a>
for details.</p></blockquote>
<p><em>Does any of this look wrong? <a
href="feedback">Please
let us know.</a></em></p>
</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>
2025-05-30 10:07:30 -04:00
Robin Malfait
31c0a21452
Improve upgrade migrations (#18184)
This PR fixes 2 issues with the migration tool where certain classes
weren't migrated. This PR fixes those 2 scenarios:

### Scenario 1

When you have an arbitrary opacity modifier that doesn't use `%`, but is
just a number typically between `0` and `1` then this was not converted
to the bare value equivalent before.

E.g.:

```html
<div class="bg-[#f00]/[0.16]"></dv>
```

Will now be converted to:

```html
<div class="bg-[#f00]/16"></dv>
```

### Scenario 2

Fixes a bug when a CSS function was used in a fallback value in the CSS
variable shorthand syntax. In that case we didn't migrate the class to
the new syntax.

This was because we assumed that a `(` was found, that we are dealing
with a CSS function.

E.g.: 
```html
<div class="w-[--spacing(1)]"></div>
                        ^  This indicates a CSS function, we should not be 
                           converting this to `w-(--spacing(1))`
```

But if a function was used as a fallback value, for example:

```html
<div class="bg-[--my-color,theme(colors.red.500)]"></dv>
```

Then we also didn't migrate it, but since the function call is in the
fallback, we can still migrate it.

Will now properly be converted to:

```html
<div class="bg-(--my-color,var(--color-red-500))"></dv>
```


## Test plan

1. Added a test for the first case
2. Added a test for the second case
3. Also added an integration-like test that runs all the migration steps
to make sure that the `theme(…)` in the fallback also gets updated to
`var(…)`. This one caught an issue because the `var(…)` wasn't handling
prefixes correctly.
2025-05-30 13:32:59 +00:00
depfu[bot]
3c629dec86
Update tsup 8.4.0 → 8.5.0 (minor) (#18125)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ tsup (8.4.0 → 8.5.0)





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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-05-29 13:38:00 -04:00
Robin Malfait
193eb84f6c
Release v4.1.8 (#18164)
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-05-28 17:01:27 +02:00
Robin Malfait
4bfacb33a0
Improve error messages when @apply fails (#18059)
This PR improves error messages when `@apply` fails. Right now it gives
you a generic error message that you cannot apply a certain utility.

```css
.foo {
  @apply bg-red-500;
}
```

Would result in:
```
Cannot apply unknown utility class: bg-red-500
```

However, there are some situations where we can give you more context
about what's happening.

### Missing `@import "tailwindcss"` or `@reference`

If you are in a Vue file for example, and you have the following code:
```vue
<template>
  <div class="foo"></div>
</template>

<style>
.foo {
  @apply bg-red-500;
}
</style>
```

Then this will now result in:
```
Cannot apply unknown utility class `bg-white`. Are you using CSS modules or similar and missing `@reference`? https://tailwindcss.com/docs/functions-and-directives#reference-directive
```

We do this by checking if we found a `@tailwind utilities` or
`@reference`. If not, we throw this more specific error.

### Explicitly excluded classes via `@source not inline('…')`

Or via the legacy `blocklist` from a JS config.

If you then have the following file:
```css
@import "tailwindcss";

@source not inline('bg-white');

.foo {
  @apply bg-white;
}
```

Then this will now result in:
```
Cannot apply utility class `bg-white` because it has been explicitly disabled: https://tailwindcss.com/docs/detecting-classes-in-source-files#explicitly-excluding-classes
```

We do this by checking if the class was marked as invalid.

### Applying unprefixed class in prefix mode

If you have the prefix option configured, but you are applying a
non-prefixed class, then we will show the following error:

Given this input:
```css
@import "tailwindcss" prefix(tw);

.foo {
  @apply underline;
}
```


The following error is thrown:
```
Cannot apply unprefixed utility class `underline`. Did you mean `tw:underline`?
```

### Applying known utilities with unknown variants

If you have unknown variants, then we will list them as well if the base
utility does compile correctly.

Given this input:

```css
@import "tailwindcss";

.foo {
  @apply hocus:hover:pocus:bg-red-500;
}
```

The following error is thrown:
```
Cannot apply utility class `hocus:hover:pocus:bg-red-500` because the `hocus` and `pocus` variants do not exist.
```

## Test plan

1. Everything behaves the same, but the error messages give more
details.
2. Updated tests with new error messages
3. Added new unit tests to verify the various scenarios
4. Added a Vue specific integration test with a `<style>…</style>` block
using `@apply`

[ci-all] There are some newlines here and there, let's verify that they
work identically on all platforms.

---------

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
2025-05-27 20:19:14 +02:00
Jordan Pittman
58a6ad0e28
Ignore .pnpm-store in Oxide (#18163)
Fixes #18148
2025-05-27 13:02:10 -04:00
depfu[bot]
a37f6ba6a3
Update turbo 2.5.2 → 2.5.3 (patch) (#18157)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ turbo (2.5.2 → 2.5.3) ·
[Repo](https://github.com/turborepo/turbo)





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>
2025-05-27 11:12:06 +02:00
Robin Malfait
1d4c263c73
Fix Haml pre-processing crash when there is no \n at the end of the file (#18155)
This PR fixes a Haml pre-processing issue where a crash occurs if there
is no trailing `\n` at the end of the file and the code before it was
considered Ruby code.

This happens in situations where Ruby code was used. E.g.:

```
- index = 0
- index += 1
```

In this situation when we see the `-` on the second line, then we will
find the whole indented block and parse it as Ruby code instead. The
block ands at the last `\n`, but since we reach the end of the file,
there is no `\n`. Right now we incorrectly reset the internal cursor to
the last known `\n` position. This means that the `start` of the Ruby
block will be _after_ the `end` of the Ruby block and the Haml parser
will crash.

To solve this, once we reach the end of the file, we don't reset the
cursor to the wrong position.

Fixes:
https://github.com/tailwindlabs/tailwindcss/issues/17379#issuecomment-2910108646

## Test plan

1. Added a regression test that did fail before the fix, and doesn't
anymore
2025-05-26 18:52:44 +02:00
Robin Malfait
5131237d67
Fix migrating mt-[0px] to -mt-[0px] instead of the other way around (#18154)
I was testing to upgrade tool on various random projects just to see how
it behaves. Then I noticed an odd migration...

This PR fixes an issue where the upgrade tool accidentally migrated
classes such as `mt-[0px]` to `-mt-[0px]`. The reason for this is
because we are trying to find a replacement, and the computed signature
for both of them are exactly the same.

- `mt-[0px]` translates to:

   ```css
   .x {
     margin-top: 0px;
   }
   ```

- `-mt-[0px]` translates to:

   ```css
   .x {
     margin-top: calc(0px * -1);
   }
   ```

   Which in turn translates to

   ```css
   .x {
     margin-top: 0px;
   }
   ```

   Notice that this is `0px`, not `-0px`.

Internally we use the roots of functional utilities to find
replacements. For intellisense purposes we typically show negative
versions before positive versions. This then means that we will try
`-mt-*` before `mt-*`. Because of the signature above, the `mt-[0px]`
was translated into `-mt-[0px]`.

We could solve this in a few ways. The first thing we can try is to make
sure that the signature is not the same and that `-mt-[0px]` actually
translates into `-0px` not `0px`.

This would solve our problem of the accidental migration. However, if we
_just_ sort the functional utilities roots such that the positive
versions exist before negative version and rely on the fact that
`-mt-[0px]` has the same signature. Then it also means that by doing
that we can migrate `-mt-[0px]` into `mt-[0px]` which is even better
because it's the same result and shorter.

## Test plan

1. Added a test to verify that `mt-[0px]` does not get migrated to
`-mt-[0px]`.
2. Added a test to verify that `-mt-[0px]` does get migrated to
`mt-[0px]`.
2025-05-26 16:29:22 +02:00
depfu[bot]
5d4e8f00fa
Update eslint 9.25.1 → 9.27.0 (minor) (#18132)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.25.1 → 9.27.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.27.0">9.27.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="d71e37f450"><code
class="notranslate">d71e37f</code></a> feat: Allow flags to be set in
ESLINT_FLAGS env variable (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19717">#19717</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="ba456e000e"><code
class="notranslate">ba456e0</code></a> feat: Externalize MCP server (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19699">#19699</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="07c1a7e839"><code
class="notranslate">07c1a7e</code></a> feat: add <code
class="notranslate">allowRegexCharacters</code> to <code
class="notranslate">no-useless-escape</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19705">#19705</a>)
(sethamus)</li>
<li>
<a
href="7bc6c71ca3"><code
class="notranslate">7bc6c71</code></a> feat: add no-unassigned-vars rule
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19618">#19618</a>)
(Jacob Bandes-Storch)</li>
<li>
<a
href="ee40364297"><code
class="notranslate">ee40364</code></a> feat: convert
no-array-constructor suggestions to autofixes (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19621">#19621</a>)
(sethamus)</li>
<li>
<a
href="32957cde72"><code
class="notranslate">32957cd</code></a> feat: support TS syntax in <code
class="notranslate">max-params</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19557">#19557</a>)
(Nitin Kumar)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="5687ce7055"><code
class="notranslate">5687ce7</code></a> fix: correct mismatched removed
rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19734">#19734</a>)
(루밀LuMir)</li>
<li>
<a
href="dc5ed337fd"><code
class="notranslate">dc5ed33</code></a> fix: correct types and tighten
type definitions in <code class="notranslate">SourceCode</code> class
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19731">#19731</a>)
(루밀LuMir)</li>
<li>
<a
href="de1b5deba0"><code
class="notranslate">de1b5de</code></a> fix: correct <code
class="notranslate">service</code> property name in <code
class="notranslate">Linter.ESLintParseResult</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19713">#19713</a>)
(Francesco Trotta)</li>
<li>
<a
href="60c3e2cf92"><code
class="notranslate">60c3e2c</code></a> fix: sort keys in
eslint-suppressions.json to avoid git churn (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19711">#19711</a>)
(Ron Waldon-Howe)</li>
<li>
<a
href="9da90ca3c1"><code
class="notranslate">9da90ca</code></a> fix: add <code
class="notranslate">allowReserved</code> to <code
class="notranslate">Linter.ParserOptions</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19710">#19710</a>)
(Francesco Trotta)</li>
<li>
<a
href="fbb8be9256"><code
class="notranslate">fbb8be9</code></a> fix: add <code
class="notranslate">info</code> to <code
class="notranslate">ESLint.DeprecatedRuleUse</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19701">#19701</a>)
(Francesco Trotta)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="25de55055d"><code
class="notranslate">25de550</code></a> docs: Update description of
frozen rules to mention TypeScript (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19736">#19736</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="bd5def66d1"><code
class="notranslate">bd5def6</code></a> docs: Clean up configuration
files docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19735">#19735</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="4d0c60d073"><code
class="notranslate">4d0c60d</code></a> docs: Add Neovim to editor
integrations (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19729">#19729</a>)
(Maria José Solano)</li>
<li>
<a
href="71317ebeaf"><code
class="notranslate">71317eb</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="4c289e685e"><code
class="notranslate">4c289e6</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="f0f0d46ab2"><code
class="notranslate">f0f0d46</code></a> docs: clarify that unused
suppressions cause non-zero exit code (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19698">#19698</a>)
(Milos Djermanovic)</li>
<li>
<a
href="8ed32734cc"><code
class="notranslate">8ed3273</code></a> docs: fix internal usages of
<code class="notranslate">ConfigData</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19688">#19688</a>)
(Francesco Trotta)</li>
<li>
<a
href="eb316a83a4"><code
class="notranslate">eb316a8</code></a> docs: add <code
class="notranslate">fmt</code> and <code
class="notranslate">check</code> sections to <code
class="notranslate">Package.json Conventions</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19686">#19686</a>)
(루밀LuMir)</li>
<li>
<a
href="a3a2559248"><code
class="notranslate">a3a2559</code></a> docs: fix wording in Combine
Configs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19685">#19685</a>)
(Milos Djermanovic)</li>
<li>
<a
href="c8d17e11dc"><code
class="notranslate">c8d17e1</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="f8f1560de6"><code
class="notranslate">f8f1560</code></a> chore: upgrade @eslint/js@9.27.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19739">#19739</a>)
(Milos Djermanovic)</li>
<li>
<a
href="ecaef7351f"><code
class="notranslate">ecaef73</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="596fdc6204"><code
class="notranslate">596fdc6</code></a> chore: update dependency
@arethetypeswrong/cli to ^0.18.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19732">#19732</a>)
(renovate[bot])</li>
<li>
<a
href="f791da0401"><code
class="notranslate">f791da0</code></a> chore: remove unbalanced curly
brace from <code class="notranslate">.editorconfig</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19730">#19730</a>)
(Maria José Solano)</li>
<li>
<a
href="e86edee091"><code
class="notranslate">e86edee</code></a> refactor: Consolidate Config
helpers (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19675">#19675</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="cf3635299e"><code
class="notranslate">cf36352</code></a> chore: remove shared types (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19718">#19718</a>)
(Francesco Trotta)</li>
<li>
<a
href="f60f276497"><code
class="notranslate">f60f276</code></a> refactor: Easier RuleContext
creation (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19709">#19709</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="58a171e8f0"><code
class="notranslate">58a171e</code></a> chore: update dependency
@eslint/plugin-kit to ^0.3.1 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19712">#19712</a>)
(renovate[bot])</li>
<li>
<a
href="3a075a29cf"><code
class="notranslate">3a075a2</code></a> chore: update dependency
@eslint/core to ^0.14.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19715">#19715</a>)
(renovate[bot])</li>
<li>
<a
href="44bac9d15c"><code
class="notranslate">44bac9d</code></a> ci: run tests in Node.js 24 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19702">#19702</a>)
(Francesco Trotta)</li>
<li>
<a
href="35304dd2b0"><code
class="notranslate">35304dd</code></a> chore: add missing <code
class="notranslate">funding</code> field to packages (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19684">#19684</a>)
(루밀LuMir)</li>
<li>
<a
href="f305beb82c"><code
class="notranslate">f305beb</code></a> test: mock <code
class="notranslate">process.emitWarning</code> to prevent output
disruption (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19687">#19687</a>)
(Francesco Trotta)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.26.0">9.26.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="e9754e7433"><code
class="notranslate">e9754e7</code></a> feat: add reportGlobalThis to
no-shadow-restricted-names (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19670">#19670</a>)
(sethamus)</li>
<li>
<a
href="0fa2b7a366"><code
class="notranslate">0fa2b7a</code></a> feat: add suggestions for <code
class="notranslate">eqeqeq</code> rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19640">#19640</a>)
(Nitin Kumar)</li>
<li>
<a
href="dcbdcc9c6b"><code
class="notranslate">dcbdcc9</code></a> feat: Add MCP server (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19592">#19592</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="2dfd83ef4e"><code
class="notranslate">2dfd83e</code></a> feat: add <code
class="notranslate">ignoreDirectives</code> option in <code
class="notranslate">no-unused-expressions</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19645">#19645</a>)
(sethamus)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="96e84de55a"><code
class="notranslate">96e84de</code></a> fix: check cache file existence
before deletion (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19648">#19648</a>)
(sethamus)</li>
<li>
<a
href="d683aebc8e"><code
class="notranslate">d683aeb</code></a> fix: don't crash on tests with
circular references in <code class="notranslate">RuleTester</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19664">#19664</a>)
(Milos Djermanovic)</li>
<li>
<a
href="9736d5d158"><code
class="notranslate">9736d5d</code></a> fix: add <code
class="notranslate">namespace</code> to <code
class="notranslate">Plugin.meta</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19661">#19661</a>)
(Milos Djermanovic)</li>
<li>
<a
href="17bae69e02"><code
class="notranslate">17bae69</code></a> fix: update <code
class="notranslate">RuleTester.run()</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19634">#19634</a>)
(Nitin Kumar)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="dd98d63f09"><code
class="notranslate">dd98d63</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="c25e858d2d"><code
class="notranslate">c25e858</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="b2397e9bef"><code
class="notranslate">b2397e9</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="addd0a6a62"><code
class="notranslate">addd0a6</code></a> docs: fix formatting of unordered
lists in Markdown (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19660">#19660</a>)
(Milos Djermanovic)</li>
<li>
<a
href="a21b38db02"><code
class="notranslate">a21b38d</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="c0721a7f34"><code
class="notranslate">c0721a7</code></a> docs: fix double space in command
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19657">#19657</a>)
(CamWass)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="5b247c859f"><code
class="notranslate">5b247c8</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.26.0</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19681">#19681</a>)
(Francesco Trotta)</li>
<li>
<a
href="d6fa4ac031"><code
class="notranslate">d6fa4ac</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="09586905be"><code
class="notranslate">0958690</code></a> chore: disambiguate internal
types <code class="notranslate">LanguageOptions</code> and <code
class="notranslate">Rule</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19669">#19669</a>)
(Francesco Trotta)</li>
<li>
<a
href="f1c858e3c1"><code
class="notranslate">f1c858e</code></a> chore: fix internal type
references to <code class="notranslate">Plugin</code> and <code
class="notranslate">Rule</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19665">#19665</a>)
(Francesco Trotta)</li>
<li>
<a
href="40dd2998ce"><code
class="notranslate">40dd299</code></a> refactor: One-shot ESQuery
selector analysis (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19652">#19652</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="1cfd702422"><code
class="notranslate">1cfd702</code></a> chore: update dependency
@eslint/json to ^0.12.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19656">#19656</a>)
(renovate[bot])</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="3ed4b3652d...b9080cf28d">See
the full diff on Github</a>. The new version differs by 58 commits:</p>
<ul>
<li><a
href="b9080cf28d"><code>9.27.0</code></a></li>
<li><a
href="b7a5c66129"><code>Build:
changelog update for 9.27.0</code></a></li>
<li><a
href="f8f1560de6"><code>chore:
upgrade @eslint/js@9.27.0 (#19739)</code></a></li>
<li><a
href="ecaef7351f"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="25de55055d"><code>docs:
Update description of frozen rules to mention TypeScript
(#19736)</code></a></li>
<li><a
href="bd5def66d1"><code>docs:
Clean up configuration files docs (#19735)</code></a></li>
<li><a
href="d71e37f450"><code>feat:
Allow flags to be set in ESLINT_FLAGS env variable
(#19717)</code></a></li>
<li><a
href="5687ce7055"><code>fix:
correct mismatched removed rules (#19734)</code></a></li>
<li><a
href="596fdc6204"><code>chore:
update dependency @arethetypeswrong/cli to ^0.18.0
(#19732)</code></a></li>
<li><a
href="ba456e000e"><code>feat:
Externalize MCP server (#19699)</code></a></li>
<li><a
href="dc5ed337fd"><code>fix:
correct types and tighten type definitions in `SourceCode` class
(#19731)</code></a></li>
<li><a
href="4d0c60d073"><code>docs:
Add Neovim to editor integrations (#19729)</code></a></li>
<li><a
href="f791da0401"><code>chore:
remove unbalanced curly brace from `.editorconfig`
(#19730)</code></a></li>
<li><a
href="e86edee091"><code>refactor:
Consolidate Config helpers (#19675)</code></a></li>
<li><a
href="07c1a7e839"><code>feat:
add `allowRegexCharacters` to `no-useless-escape`
(#19705)</code></a></li>
<li><a
href="cf3635299e"><code>chore:
remove shared types (#19718)</code></a></li>
<li><a
href="f60f276497"><code>refactor:
Easier RuleContext creation (#19709)</code></a></li>
<li><a
href="71317ebeaf"><code>docs:
Update README</code></a></li>
<li><a
href="de1b5deba0"><code>fix:
correct `service` property name in `Linter.ESLintParseResult` type
(#19713)</code></a></li>
<li><a
href="58a171e8f0"><code>chore:
update dependency @eslint/plugin-kit to ^0.3.1 (#19712)</code></a></li>
<li><a
href="3a075a29cf"><code>chore:
update dependency @eslint/core to ^0.14.0 (#19715)</code></a></li>
<li><a
href="60c3e2cf92"><code>fix:
sort keys in eslint-suppressions.json to avoid git churn
(#19711)</code></a></li>
<li><a
href="4c289e685e"><code>docs:
Update README</code></a></li>
<li><a
href="9da90ca3c1"><code>fix:
add `allowReserved` to `Linter.ParserOptions` type
(#19710)</code></a></li>
<li><a
href="7bc6c71ca3"><code>feat:
add no-unassigned-vars rule (#19618)</code></a></li>
<li><a
href="ee40364297"><code>feat:
convert no-array-constructor suggestions to autofixes
(#19621)</code></a></li>
<li><a
href="fbb8be9256"><code>fix:
add `info` to `ESLint.DeprecatedRuleUse` type (#19701)</code></a></li>
<li><a
href="f0f0d46ab2"><code>docs:
clarify that unused suppressions cause non-zero exit code
(#19698)</code></a></li>
<li><a
href="44bac9d15c"><code>ci:
run tests in Node.js 24 (#19702)</code></a></li>
<li><a
href="32957cde72"><code>feat:
support TS syntax in `max-params` (#19557)</code></a></li>
<li><a
href="35304dd2b0"><code>chore:
add missing `funding` field to packages (#19684)</code></a></li>
<li><a
href="8ed32734cc"><code>docs:
fix internal usages of `ConfigData` type (#19688)</code></a></li>
<li><a
href="f305beb82c"><code>test:
mock `process.emitWarning` to prevent output disruption
(#19687)</code></a></li>
<li><a
href="eb316a83a4"><code>docs:
add `fmt` and `check` sections to `Package.json Conventions`
(#19686)</code></a></li>
<li><a
href="a3a2559248"><code>docs:
fix wording in Combine Configs (#19685)</code></a></li>
<li><a
href="c8d17e11dc"><code>docs:
Update README</code></a></li>
<li><a
href="8bbabc4691"><code>9.26.0</code></a></li>
<li><a
href="16f5ff7991"><code>Build:
changelog update for 9.26.0</code></a></li>
<li><a
href="5b247c859f"><code>chore:
upgrade to `@eslint/js@9.26.0` (#19681)</code></a></li>
<li><a
href="d6fa4ac031"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="e9754e7433"><code>feat:
add reportGlobalThis to no-shadow-restricted-names
(#19670)</code></a></li>
<li><a
href="0fa2b7a366"><code>feat:
add suggestions for `eqeqeq` rule (#19640)</code></a></li>
<li><a
href="dd98d63f09"><code>docs:
Update README</code></a></li>
<li><a
href="96e84de55a"><code>fix:
check cache file existence before deletion (#19648)</code></a></li>
<li><a
href="c25e858d2d"><code>docs:
Update README</code></a></li>
<li><a
href="09586905be"><code>chore:
disambiguate internal types `LanguageOptions` and `Rule`
(#19669)</code></a></li>
<li><a
href="dcbdcc9c6b"><code>feat:
Add MCP server (#19592)</code></a></li>
<li><a
href="b2397e9bef"><code>docs:
Update README</code></a></li>
<li><a
href="d683aebc8e"><code>fix:
don&#39;t crash on tests with circular references in `RuleTester`
(#19664)</code></a></li>
<li><a
href="f1c858e3c1"><code>chore:
fix internal type references to `Plugin` and `Rule`
(#19665)</code></a></li>
<li><a
href="9736d5d158"><code>fix:
add `namespace` to `Plugin.meta` type (#19661)</code></a></li>
<li><a
href="40dd2998ce"><code>refactor:
One-shot ESQuery selector analysis (#19652)</code></a></li>
<li><a
href="addd0a6a62"><code>docs:
fix formatting of unordered lists in Markdown (#19660)</code></a></li>
<li><a
href="a21b38db02"><code>docs:
Update README</code></a></li>
<li><a
href="c0721a7f34"><code>docs:
fix double space in command (#19657)</code></a></li>
<li><a
href="1cfd702422"><code>chore:
update dependency @eslint/json to ^0.12.0 (#19656)</code></a></li>
<li><a
href="2dfd83ef4e"><code>feat:
add `ignoreDirectives` option in `no-unused-expressions`
(#19645)</code></a></li>
<li><a
href="17bae69e02"><code>fix:
update `RuleTester.run()` type (#19634)</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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-05-23 17:57:32 -04:00
depfu[bot]
8fcc63331c
Update @napi-rs/wasm-runtime 0.2.9 → 0.2.10 (minor) (#18127)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​napi-rs/wasm-runtime (0.2.9 → 0.2.10) ·
[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>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-05-23 17:52:54 -04:00
Robin Malfait
884f02c46b
Fix "Cannot read properties of undefined" crash on malformed arbitrary value (#18133)
This PR fixes a crash when an arbitrary value was malformed and crashed
the build.

If you have a utility like `[--btn-border:var(--color-maroon)/90)]`
which is malformed, it will crash the build. It might not be easy to
spot but the easy is the additional `)` after the `90`.

The reason this crashes is because we parse the value
`var(--color-maroon)/90)` and when we see `)` we assume it's the end of
a "function" which also assumes it was preceded by a `(`. This is not
the case and we crash.

This PR fixes that by not assuming the parsed object is available and
uses `?` to be safe and only access `nodes` if it's available.

I'm actually not 100% sure what the best solution is in this scenario
because these candidates could (and will) be returned from Oxide so even
if we throw a more descriptive error, it will still crash the build and
you might not even have control over the candidate.

This candidate will now eventually generate the following CSS:
```css
.\[--btn-border\:var\(--color-maroon\)\/90\)\] {
  --btn-border: var(--color-maroon) / ;
}
```

Which still looks odd, but even Lightning CSS doesn't throw an error in
this case (because it's a CSS variable definition), so I think it's the
best we can do. If you open your devtools you will see the weird values,
so it's still debug-able.

<img width="359" alt="image"
src="https://github.com/user-attachments/assets/2eb48662-64de-4417-a2da-1577bf9075b5"
/>

Fixes: #17064

## Test plan

Manually tested the candidate that crashed it, and after the change
generated the above CSS. Then used it in JSFiddle to proof it's fixed
now. https://jsfiddle.net/z850ykew/

Couldn't use Tailwind Play because the candidate will cause a crash
there as well 😅
2025-05-23 19:59:02 +00:00
Robin Malfait
9cb38993d2
Upgrade deprecated order-none to order-0 (#18126)
This PR adds an initial version for deprecated utilities. Right now it's
hardcoded to just the `order-none` utility.

This means that `order-0` and `order-[0]` will not be migrated to
`order-none` anymore. We did that automatically because we prefer named
utilities over bare values and arbitrary values.

With this PR, `order-none` is ignored.

Similarly, `order-none` will be migrated to `order-0` instead (defined
in a separate migration for deprecated values). Made it a new migration
instead of using the legacy migration because there all utilities still
exist, but are defined differently (e.g.: `shadow`, `shadow-sm`,
`shadow-xs`).

This PR is also an initial version, it doesn't add any form of
`deprecated` flag or feature on a per-utility implementation basis. This
therefor has the side effect that if you have a custom `order-none`
defined, that it will also be ignored during migrations.


## Test plan

1. Added tests to ensure the `order-0` is not migrated to `order-none`
2. Added tests to ensure `order-none` is migrated to `order-0` (if it's
safe to do so, the signature is still computed to ensure the output is
the same).
3. Ran this on the Tailwind Plus codebase and ensured that `order-0` is
not migrated to `order-none` and that `order-none` is migrated to
`order-0`.

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-05-23 15:21:21 +00:00
Álvaro Mondéjar Rubio
ed3cecd39d
Support Leptos class: attributes (#18093)
<!--

👋 Hey, thanks for your interest in contributing to Tailwind!

**Please ask first before starting work on any significant new
features.**

It's never a fun experience to have your pull request declined after
investing a lot of time and effort into a new feature. To avoid this
from happening, we request that contributors create a discussion to
first discuss any significant new features.

For more info, check out the contributing guide:


https://github.com/tailwindcss/tailwindcss/blob/main/.github/CONTRIBUTING.md

-->

Fixes #18092

## Summary

Using the Svelte preprocessor for Rust files we can support Leptos
`class:` attributes syntax.

## Test plan

```
pnpm t
```

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-05-23 10:15:33 +00:00
depfu[bot]
24ed64ebb3
Update @types/react 19.1.2 → 19.1.4 (patch) (#18085)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ @​types/react (19.1.2 → 19.1.4) ·
[Repo](https://github.com/DefinitelyTyped/DefinitelyTyped)





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>
2025-05-19 16:07:59 -04:00
depfu[bot]
7013ca37ed
Update semver 7.7.1 → 7.7.2 (patch) (#18084)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ semver (7.7.1 → 7.7.2) ·
[Repo](https://github.com/npm/node-semver) ·
[Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/npm/node-semver/releases/tag/v7.7.2">7.7.2</a></h4>

<blockquote><h2 dir="auto">
<a
href="https://bounce.depfu.com/github.com/npm/node-semver/compare/v7.7.1...v7.7.2">7.7.2</a>
(2025-05-12)</h2>
<h3 dir="auto">Bug Fixes</h3>
<ul dir="auto">
<li>
<a
href="fcafb61ed5"><code
class="notranslate">fcafb61</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/780">#780</a>
add missing <code class="notranslate">'use strict'</code> directives (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/780">#780</a>)
(<a href="https://bounce.depfu.com/github.com/Fdawgs">@Fdawgs</a>)</li>
<li>
<a
href="c99f336fa3"><code
class="notranslate">c99f336</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/781">#781</a>
prerelease identifier starting with digits (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/781">#781</a>)
(<a
href="https://bounce.depfu.com/github.com/mbtools">@mbtools</a>)</li>
</ul>
<h3 dir="auto">Chores</h3>
<ul dir="auto">
<li>
<a
href="c760403b93"><code
class="notranslate">c760403</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/784">#784</a>
template-oss-apply for workflow permissions (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/784">#784</a>)
(<a
href="https://bounce.depfu.com/github.com/wraithgar">@wraithgar</a>)</li>
<li>
<a
href="2677f2a883"><code
class="notranslate">2677f2a</code></a> <a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/778">#778</a>
bump @npmcli/template-oss from 4.23.6 to 4.24.3 (<a
href="https://bounce.depfu.com/github.com/npm/node-semver/pull/778">#778</a>)
(<a
href="https://bounce.depfu.com/github.com/dependabot">@dependabot</a>[bot],
<a
href="https://bounce.depfu.com/github.com/npm-cli-bot">@npm-cli-bot</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="30c438bb46...281055e771">See
the full diff on Github</a>. The new version differs by 6 commits:</p>
<ul>
<li><a
href="281055e771"><code>chore:
release 7.7.2 (#783)</code></a></li>
<li><a
href="fcafb61ed5"><code>fix:
add missing `&#39;use strict&#39;` directives (#780)</code></a></li>
<li><a
href="c760403b93"><code>chore:
template-oss-apply for workflow permissions (#784)</code></a></li>
<li><a
href="c99f336fa3"><code>fix:
prerelease identifier starting with digits (#781)</code></a></li>
<li><a
href="2677f2a883"><code>chore:
bump @npmcli/template-oss from 4.23.6 to 4.24.3 (#778)</code></a></li>
<li><a
href="0b98655dbc"><code>chore:
bump @npmcli/template-oss from 4.23.4 to 4.23.6 (#760)</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>
2025-05-19 15:19:43 -04:00
Jordan Pittman
9df5ba78bf
Fix PostCSS crash when using toJSON() (#18083)
When I added source maps to PostCSS I mistakenly assumed that `.source`
on a node could be `undefined`. The comment above the property in
PostCSS says that `source` can be `undefined` but this is a commentary
on the value upon **access** not its expected value on **write**:

```ts
declare abstract class Node_ {
  /**
   * …
   *
   * The nodes that are created manually using the public APIs
   * provided by PostCSS will have `source` undefined and
   * will be absent in the source map.
   *
   * …
   */
  source?: Node.Source
}
```

Rather, what these types mean is that *if the property exists* it must
be defined. But otherwise the property can be missing if a node has no
source location metadata. This generally wasn't a problem with the
string-returning APIs but the `toJSON()` API in PostCSS expects that
`source` is defined if present. This caused a crash because our license
comment doesn't have a source location.

I've addressed this by deleting the `source` property from the node if
source location data is not available.

Fixes #18082

ref https://github.com/parcel-bundler/parcel/issues/10161
2025-05-19 11:28:09 -04:00
Robin Malfait
a42251cc29
Improve performance of upgrade tool (#18068)
This PR improves the performance of the upgrade tool due to a regression
introduced by https://github.com/tailwindlabs/tailwindcss/pull/18057

Essentially, we had to make sure that we are not in `<style>…</style>`
tags because we don't want to migrate declarations in there such as
`flex-shrink: 0;`

The issue with this approach is that we checked _before_ the candidate
if a `<style` cold be found and if we found an `</style>` tag after the
candidate.

We would basically do this check for every candidate that matches.

Running this on our Tailwind UI codebase, this resulted in a bit of a
slowdown:

```diff
- Before: ~13s
+  After: ~5m 39s
```

... quite the difference.

This is because we have a snapshot file that contains ~650k lines of
code. Looking for `<style>` and `</style>` tags in a file that large is
expensive, especially if we do it a lot.

I ran some numbers and that file contains ~1.8 million candidates.

Anyway, this PR fixes that by doing a few things:

1. We will compute the `<style>` and `</style>` tag positions only once
per file and cache it. This allows us to re-use this work for every
candidate that needs it.
2. We track the positions, which means that we can simply check if a
candidate's location is within any of 2 start and end tags. If so, we
skip it.

Running the numbers now gets us to:

```diff
- Before: ~5m 39s
+  After: ~9s
```

Much better!

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2025-05-19 10:41:40 -04:00
Robin Malfait
71fb9cdf59
Improve @tailwindcss/upgrade and pnpm workspaces support (#18065)
This PR fixes an issue where an error such as:

<img width="1702" alt="image"
src="https://github.com/user-attachments/assets/4e6f75c7-3182-4497-939e-96cff08c55ae"
/>

Will be thrown during the upgrade process. This can happen when you are
using `pnpm` and your CSS file includes a `@import "tailwindcss";`. In
this scenario, `tailwindcss` will be loaded from a shared `.pnpm` folder
outside of the current working directory.

In this case, we are also not interested in migrating _that_ file, but
we also don't want the upgrade process to just crash.

I didn't see an option to ignore errors like this, so wrapped it in a
try/catch instead.

It also fixes another issue where if you are using a pnpm workspace and
run the upgrade tool from the root, then it throws you an error that you
cannot add dependencies to the workspace root unless `-w` or
`--workspace-root` flags are passed.

For this, we disable the check entirely using the
`--ignore-workspace-root-check` flag. If we always used the
`--workspace-root` flag, then the dependencies would always be added to
the root, regardless of where you are running the script from which is
not what we want.

## Test plan

Before:

<img width="1816" alt="image"
src="https://github.com/user-attachments/assets/78246876-3eb6-4539-a557-d3d366f1b3a3"
/>

After:

<img width="1816" alt="image"
src="https://github.com/user-attachments/assets/a65e4421-d7c5-4d83-b35d-934708543e25"
/>

Before:
 
<img width="1816" alt="image"
src="https://github.com/user-attachments/assets/53772661-2c4a-4212-84d9-a556a0ad320f"
/>

After:

<img width="1816" alt="image"
src="https://github.com/user-attachments/assets/5bfaf20e-34b8-44fd-9b59-e72d36738879"
/>
2025-05-19 12:47:08 +02:00
Robin Malfait
c7d368b3c4
Do not migrate declarations in <style> blocks (#18057)
This PR improves the upgrade tool by making sure that we don't migrate
CSS declarations in `<style>…</style>` blocks.

We do this by making sure that:

1. We detect a declaration, the current heuristic is that the candidate
is:
   - Preceded by whitespace
   - Followed by a colon and whitespace

   ```html
   <style>
   .foo {
       flex-shrink: 0;
      ^           ^^
   }
   </style>
   ```

2. We are in a `<style>…</style>` block

   ```html
   <style>
   ^^^^^^
   .foo {
       flex-shrink: 0;
   }
   </style>
   ^^^^^^^^
   ```

The reason we have these 2 checks is because just relying on the first
heuristic alone, also means that we will not be migrating keys in JS
objects, because they typically follow the same structure:

```js
let classes = {
 flex: 0,
^    ^^
}
```

Another important thing to note is that we can't just ignore anything in
between `<style>…</style>` blocks, because you could still be using
`@apply` that we _do_ want to migrate.

Last but not least, the first heuristics is not perfect either. If you
are writing minified CSS then this will likely fail if there is no
whitespace around the candidate.

But my current assumption is that nobody should be writing minified CSS,
and minified CSS will very likely be generated and gitignored. In either
situation, replacements in minified CSS will not be any worse than it is
today.

I'm open to suggestions for better heuristics.

## Test plan

1. Added an integration test that verifies that we do migrate `@apply`
and don't migrate the `flex-shrink: 0;` declaration.


Fixes: #17975
2025-05-16 12:53:34 +02:00
depfu[bot]
d69604e202
Update all of nextjs 15.3.1 → 15.3.2 (patch) (#18046) 2025-05-15 18:12:52 +02:00
Philipp Spiess
74e084ad27
Prepare v4.1.7 release (#18040)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-05-15 15:31:18 +02:00
Robin Malfait
bf591febf3
Fix missing extracted classes containing . in Clojure (#18038)
This PR fixes an issue in the Clojure pre-processor where candidates
including `.` characters were not extracted correctly.

The solution here is to only replace the `.` with a ` ` when the `.` is
not surrounded by numbers. This means that:


```
:.foo.bar
```
Becomes
```
: foo bar
```

But
```
:.gap-1.5.flex
```

Becomes
```
: gap-1.5 flex
```

This way the `gap-1.5` is correctly extracted.

## Test plan

1. Added a test for this case
2. Tested this in the extractor tool as well. Notice how the `gap-1.5`
is correctly extracted here.

<img width="1247" alt="image"
src="https://github.com/user-attachments/assets/f5dd2600-5c5e-4ad8-88af-4e5be44340f5"
/>


Fixes: 17760
2025-05-15 14:50:03 +02:00
Robin Malfait
1ada8e0f22
Make candidate template migrations faster (#18025)
This PR makes the migrations for templates much faster. To make this
work, I also had to move things around a bit (so you might want to check
this PR commit by commit). I also solved an issue by restructuring the
code.

### Performance

For starters, we barely applied any caching when migrating candidates
from α to β. The problem with this is that in big projects the same
candidates will appear _everywhere_, so caching is going to be useful
here.

One of the reasons why we didn't do any caching is that some migrations
were checking if a migration is actually safe to do. To do this, we were
checking the `location` (the location of the candidate in the template).
Since this location is unique for each template, caching was not
possible.

So the first order of business was to hoist the `isSafeMigration` check
up as the very first thing we do in the migration.

If we do this first, then the only remaining code relies on the
`DesignSystem`, `UserConfig` and `rawCandidate`.

In a project, the `DesignSystem` and `UserConfig` will be the same
during the migration, only the `rawCandidate` will be different which
means that we can move all this logic in a good old `DefaultMap` and
cache the heck out of it.

Running the numbers on our Tailwind Plus repo, this results in:
```
    Total seen candidates: 2 211 844
Total migrated candidates: 7 775 
               Cache hits: 1 575 700
```

That's a lot of work we _don't_ have to do. Looking at the timings, the
template migration step goes from ~45s to ~10s because of this.

Another big benefit of this is that this makes migrations _actually_
safe. Before we were checking if a migration was safe to do in specific
migrations. But other migrations were still printing the candidate which
could still result in an unsafe migration.

For example when migrating the `blur` and the `shadow` classes, the
`isSafeMigration` was used. But if the input was `!flex` then the safety
check wasn't even checked in this specific migration.

### Safe migrations

Also made some changes to the `isSafeMigration` logic itself. We used to
start by checking the location, but thinking about the problem again,
the actual big problem we were running into is classes that are short
like `blur`, and `shadow` because they could be used in other contexts
than a Tailwind CSS class.

Inverting this logic means that more specific Tailwind CSS classes will
very likely _not_ cause any issues at all.

For example:
- If you have variants: `hover:focus:flex`
- If you have arbitrary properties: `[color:red]`
- If you have arbitrary values: `bg-[red]`
- If you have a modifier: `bg-red-500/50`
- If you have a `-` in the name: `bg-red-500`

Even better if we can't parse a candidate at all, we can skip the
migrations all together.

This brings us to the issue in #17974, one of the issues was already
solved by just hoisting the `isSafeMigration`. But to make the issue was
completely solved I also made sure that in Vue attributes like
`:active="…"` are also considered unsafe (note: `:class` is allowed).

Last but not least, in case of the `!duration` that got replaced with
`duration!` was solved by verifying that the candidate actually produces
valid CSS. We can compute the signature for this class.

The reason this wasn't thrown away earlier is because we can correctly
parse `duration` but `duration` on its own doesn't exist,
`duration-<number>` does exist as a functional utility which is why it
parsed in the first place.

Fixes: #17974

## Test plan

1. Ran the tool on our Tailwind UI Templates repo to compare the new
output with the "old" behavior and there were no differences in output.
2. Ran the tool on our Tailwind Plus repo, and the template migration
step went from ~45s to ~10s.
3. Added additional tests to verify the issues in #17974 are fixed.


[ci-all] let's run this on all CI platforms...
2025-05-15 11:17:39 +00:00
Philipp Spiess
6fb98d2f40
Upgrade lightningcss to 1.30.1 (#18037) 2025-05-15 13:16:02 +02:00
Philipp Spiess
f3157cd9a6
Ignore custom variants with :merge(…) selectors (#18020)
Closes #15617

## Summary

This PR ignores `addVariant(…)` legacy JS plugin calls for variants that
are using the [`:merge(…)`
selector](https://v3.tailwindcss.com/docs/plugins#parent-and-sibling-states)
for parent and sibling states. We can ignore these now because in v4,
`group-*` and `peer-*` variants _compound automatically_ and you don't
have to define them anymore.

## Test plan

Added a unit test to ensure that the `optional` variant example from the
v3 docs work as expected.
2025-05-15 13:00:00 +02:00
Philipp Spiess
e57a2f5a3a
Change casing of utilities with named values to kebab-case to match u… (#18017)
Fixes #16156

## Summary

This PR adds a new 3 -> 4 template migration that changes the casing of
in both utility values and modifier values from camelCase to kebab-case
to match the updated CSS variable names.

## Test plan

- Added integration test, see the diff in the PR.
2025-05-14 14:51:51 +02:00
Philipp Spiess
4db711d1e4
Fix -rotate-* with arbitary values (#18014)
Fixes #18013

## Test plan

See updated unit tests
2025-05-14 11:40:37 +02:00
Robin Malfait
498f9ff003
Migrate bare values to named values (#18000)
This PR improves the upgrade tool by also migrating bare values to named
values defined in the `@theme`.

Recently we shipped some updates dat allowed us to migrate arbitrary
values (with square brackets), but we didn't migrate bare values yet.

That means that in this example:
```html
<div class="aspect-[16/9]"></div>
<div class="aspect-16/9"></div>
```

We migrated this to:
```html
<div class="aspect-video"></div>
<div class="aspect-16/9"></div>
```

With this change, we will also try and migrate the bare value to a named
value. So this example:
```html
<div class="aspect-[16/9]"></div>
<div class="aspect-16/9"></div>
```

Now becomes:
```html
<div class="aspect-video"></div>
<div class="aspect-video"></div>
```

## Test plan

1. Added unit tests for the new functionality.
2. Ran this on a local project


Before:
<img width="432" alt="image"
src="https://github.com/user-attachments/assets/ce1adfbd-7be1-4062-bea5-66368f748e44"
/>

After:
<img width="382" alt="image"
src="https://github.com/user-attachments/assets/a385c94c-4e4c-4e1c-ac73-680c56ac4081"
/>
2025-05-13 17:35:11 +02:00
Philipp Spiess
ef2e6c71fe
Upgrade: Migrate outline class (#17996)
This PR adds a migration from `outline` to `outline-solid` for the v3 ->
v4 upgrade tool.

## Test plan

- Added integration test
2025-05-13 14:20:40 +02:00
Philipp Spiess
4fba87bc90
Upgrade lightningcss to 1.30.0 (#17979) 2025-05-12 15:49:18 +02:00
Philipp Spiess
ba944ca3b1
Upgrade bun to 1.2.13 (#17981) 2025-05-12 15:17:19 +02:00
Tommy D. Rossi
5688f0a7ae
Add ignore pattern for node_modules in globby search in upgrade cli (#17969)
When passing `gitignore: true` to globby it will start a search for all
.gitignore files, this initial search includes node_modules making it
hang forever for large monorepos with many files inside node_modules
2025-05-12 11:09:40 +00:00
depfu[bot]
19e2b29c68
Update @types/react-dom 19.1.2 → 19.1.3 (patch) (#17976) 2025-05-12 11:02:57 +02:00
depfu[bot]
0d975f5f06
Update dedent 1.5.3 → 1.6.0 (minor) (#17965)
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?




#### ✳️ dedent (1.5.3 → 1.6.0) · [Repo](https://github.com/dmnd/dedent)
· [Changelog](https://github.com/dmnd/dedent/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/dmnd/dedent/releases/tag/v1.6.0">1.6.0</a></h4>

<blockquote><h2 dir="auto">What's Changed</h2>
<ul dir="auto">
<li>feat: add <code class="notranslate">trimWhitespace</code> option by
<a href="https://bounce.depfu.com/github.com/43081j">@43081j</a> in <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/97">#97</a>
</li>
</ul>
<h2 dir="auto">New Contributors</h2>
<ul dir="auto">
<li>
<a href="https://bounce.depfu.com/github.com/43081j">@43081j</a> made
their first contribution in <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/pull/97">#97</a>
</li>
</ul>
<p dir="auto"><strong>Full Changelog</strong>: <a
href="https://bounce.depfu.com/github.com/dmnd/dedent/compare/v1.5.3...v1.6.0"><tt>v1.5.3...v1.6.0</tt></a></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="90644fe0be...ab2ce25762">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="ab2ce25762"><code>1.6.0
(#98)</code></a></li>
<li><a
href="86902f7c97"><code>feat:
add `trimWhitespace` option (#97)</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>
2025-05-10 13:21:48 +02:00
Jordan Pittman
f0986ce127
Prevent duplicate suggestions when using @theme and @utility together (#17675)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1313

Right now given this CSS:
```css
@theme reference {
  --text-header: 1.5rem;
}

@utility text-header {
  text-transform: uppercase;
}
```

You'll see two entries for `text-header` in IntelliSense completions but
we only want you to see one. This PR solves this by merging their
modifier lists and de-duping by class name.
2025-05-09 20:12:43 +00:00
Robin Malfait
3386049b7b
Fix upgrade error when using @import … source(…) (#17963)
This PR fixes an issue when running the upgrade tool and if any of the
CSS files has an import that looks like this:

```css
@import "tailwindcss" source("…");
```

This was trying to resolve `tailwindcss" source("…` instead of
`tailwindcss`.

This PR fixes that.

## Test plan

1. Ran it locally on a project

Before:

<img width="1158" alt="image"
src="https://github.com/user-attachments/assets/09bf5d69-797c-4330-ade1-edc213f7ce5c"
/>

After:

<img width="1029" alt="image"
src="https://github.com/user-attachments/assets/d1c9e194-30e2-4564-83c5-d9a259a67e90"
/>
2025-05-09 20:04:48 +00:00
Robin Malfait
737994b7aa
Allow _ before numbers during candidate extraction (#17961)
This PR fixes a bug where a class like `header_1` wasn't properly
extracted because we didn't allow an `_` before a number.

This PR fixes that by allowing an `_` before a number.

Fixes: #17960


## Test plan

1. Added a test to verify this works
2. Existing tests work

Used the visualizer tool for this to verify that the `header_1` class is
being extracted:
<img width="1816" alt="image"
src="https://github.com/user-attachments/assets/fdc21602-0e2b-4e4e-92a1-19c4f4f5393f"
/>
2025-05-09 16:29:28 +00:00
Robin Malfait
2d139984da
Prepare v4.1.6 release (#17951)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-05-09 13:39:18 +00:00
Philipp Spiess
47bb007eae
Download platform specific package if optionalDependencies are skipped (#17929)
Closes #15806

This PR adds a new `postinstall` script to `@tailwindcss/oxide` that
will attempt to download the platform-specific optional dependency to
avoid issues when the package manager does not do that automatically
(see #15806). The implementation for this is fairly simple: The npm
package is downloaded from the official npm servers and extracted into
the `node_modules` directory of the `@tailwidncss/oxide` installation.

## Test plan 

Since we still lack a solid repro of #15806, the way I tested this
change was to manually remove all automatically-installed optional
dependencies and then running the postinstall script manually. The
script then downloads the right version package which makes the import
to `@tailwidncss/oxide` work. An appropriate integration test was added
too.

I furthermore also validated that:

- This works across CI platforms [ci-all]
- The postinstall script bails out when running `pnpm install` in the
dev setup. This is necessary since doing the initial install won't have
any binary dependencies yet so it would download invalid versions from
npm (as the version numbers locally refer to the last released version).
We can safely bail out here though since this was never an issue with
local development.
- The postinstall script does not do anything when the
`@tailwindcss/oxide` library is added _unless_ the issue is detected.

[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-05-09 14:55:02 +02:00
Robin Malfait
2f6679abfe
Print sources when DEBUG=* is set (#17952)
This PR improves the debug logging by also adding the provided `@source`
to the log file. It will also print the optimized sources (the ones we
expand and mark as `Auto` or `Pattern`).


In the logs, this will look like this:

```
2025-05-09T11:03:32.906478Z  INFO tailwindcss_oxide::scanner: Provided sources:
2025-05-09T11:03:32.906544Z  INFO tailwindcss_oxide::scanner: Source: PublicSourceEntry { base: "/Users/robin/github.com/RobinMalfait/spreadsheet", pattern: "**/*", negated: false }
2025-05-09T11:03:32.906589Z  INFO tailwindcss_oxide::scanner: Optimized sources:
2025-05-09T11:03:32.906595Z  INFO tailwindcss_oxide::scanner: Source: Auto { base: "/Users/robin/github.com/RobinMalfait/spreadsheet" }
```

Or if you have more sources:

```
2025-05-09T11:06:54.767546Z  INFO tailwindcss_oxide::scanner: Provided sources:
2025-05-09T11:06:54.767660Z  INFO tailwindcss_oxide::scanner: Source: PublicSourceEntry { base: "/Users/robin/github.com/RobinMalfait/spreadsheet", pattern: "**/*", negated: false }
2025-05-09T11:06:54.767987Z  INFO tailwindcss_oxide::scanner: Source: PublicSourceEntry { base: "/Users/robin/github.com/RobinMalfait/spreadsheet/app", pattern: "./routes/*.{jsx,tsx}", negated: false }
2025-05-09T11:06:54.767992Z  INFO tailwindcss_oxide::scanner: Source: PublicSourceEntry { base: "/Users/robin/github.com/RobinMalfait/spreadsheet/app", pattern: "./utils/*.ts", negated: false }
2025-05-09T11:06:54.768450Z  INFO tailwindcss_oxide::scanner: Optimized sources:
2025-05-09T11:06:54.768455Z  INFO tailwindcss_oxide::scanner: Source: Auto { base: "/Users/robin/github.com/RobinMalfait/spreadsheet" }
2025-05-09T11:06:54.768459Z  INFO tailwindcss_oxide::scanner: Source: Pattern { base: "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes", pattern: "*.jsx" }
2025-05-09T11:06:54.768462Z  INFO tailwindcss_oxide::scanner: Source: Pattern { base: "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes", pattern: "*.tsx" }
2025-05-09T11:06:54.768466Z  INFO tailwindcss_oxide::scanner: Source: Pattern { base: "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils", pattern: "*.ts" }
```
2025-05-09 13:39:17 +02:00
Philipp Spiess
ae57d26852
Update contribution docs (#17911)
This PR updates the contribution docs to make it easier for anyone
contributing to Tailwind CSS.
2025-05-09 12:14:32 +02:00
Jordan Pittman
ff9f183368
Fix source map paths in CI (#17938) 2025-05-08 22:56:49 +02:00
Jordan Pittman
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)
2025-05-08 16:29:49 -04:00
Robin Malfait
62706dccb0
Trigger updates to the internal upgrades repo (#17928)
This PR updates will now trigger the new internal `upgrades` repo
instead of the Tailwind Play repo directly.

We will be updating more internal repos when a new version is published.
We will also use that new repo to update our other repos for other
published packages in the future.
2025-05-08 18:46:27 +02:00
Philipp Spiess
17ca56d386
Fix bug with nested @apply rules in utility classes (#17924) (#17925)
Fixes #17924

When an `@apply` pointed to utility that nested usages of `@apply`,
these nested usages were not properly carried through the dependency
chain. This was because we were only tracking dependencies on the
immediate parent rather than all parents.

To fix this, this PR:

- Modifies the dependency resolution to track dependencies through the
entire parent path
- Uses a `walk(…)` for the node replacement logic so that all nested
`@apply` usages are also resolved (as these are now tracked in the
dependency list anyways

## Test Plan

- Added a regression test for #17924 to the unit tests and ensure
existing tests don't break
2025-05-08 18:26:07 +02:00
Philipp Spiess
179e5ddd7c
Add more folders to the list of ignored content dirs (#17892)
Closes #15452

This PR adds more directories to the list of ignored content dirs. These
are now handled the same as `node_modules`:

- Contents of this directory are ignored by default to avoid scanning
dependency trees
- Explicit `@import`s inside these folders are now treated as
_external_, bypassing any `.gitignore` files.

The new extensions are:

- Version control systems: `.hg`, `.svn`
- Bundler caches: `.venv`, `venv`, `.yarn`
- Framework caches: `.next`, `.turbo`, `.parcel-cache`, `__pycache__`,
`.svelte-kit`

## Test plan

Verified with the repro of #15452 by renaming the ignored directory to
`.venv` and installing a local tarball:

<img width="1283" alt="Screenshot 2025-05-06 at 13 14 55"
src="https://github.com/user-attachments/assets/3265acbb-e121-47b3-ac6c-e174770f8234"
/>
2025-05-08 11:27:11 +02:00
Robin Malfait
d8c4df8001
Write to log file when using DEBUG=* (#17906)
This PR improves the debugability of the scanner when using `DEBUG=*` by
writing to a `tailwindcss-{pid}.log` file in the current working
directory.

It will include all the tracing information from the scanner. This PR
also introduces `Discovering {path}` and `Reading {path}` logs.

- `Discovering {path}` — this is logged when we are traversing the file
system looking for files. We use the `ignore` crate, and log this
information in the `filter_entry` callback. If a file was already
ignored by `.gitignore` files, this won't show up, but it also means
that we will not read it.
- `Reading {path}` — this is when we are actually reading the file so we
can start extracting potential Tailwind CSS classes.

These will give you some insights in what paths are being scanned, and
if we get stuck, where we get stuck.

Also, we are appending to the file. In the log below, you can already
see that a `tailwindcss-<number>.log` file exists already even though it
didn't exist before running the command. This should make it easier to
debug if we get stuck on a specific file/folder because the file will be
populated with information.

There are a few reasons for appending to a file:
1. There is a lot of output, so spamming the stdout/stderr is not ideal
2. If you run the same command again, after changing your `@source`
directives, you could diff the outputs. (although, the timestamps will
be different)
3. When using `DEBUG=*`, a lot of other tools also output debug
information, so writing to a file should make this better.

<details>

<summary>Example log</summary>

```log
2025-05-06T23:13:45.912292Z  INFO scan_sources: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.912697Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/out.css"
2025-05-06T23:13:45.912716Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/tailwindcss-61347.log"
2025-05-06T23:13:45.912748Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app"
2025-05-06T23:13:45.912786Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/test"
2025-05-06T23:13:45.912814Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/test/utils.ts"
2025-05-06T23:13:45.912851Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils"
2025-05-06T23:13:45.912873Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/flatten.ts"
2025-05-06T23:13:45.912884Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/matrix.ts"
2025-05-06T23:13:45.912893Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/default-map.ts"
2025-05-06T23:13:45.912904Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/tailwind.css"
2025-05-06T23:13:45.912914Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/root.tsx"
2025-05-06T23:13:45.912936Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain"
2025-05-06T23:13:45.912962Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.ts"
2025-05-06T23:13:45.912972Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/walk-ast.ts"
2025-05-06T23:13:45.912995Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature"
2025-05-06T23:13:45.913019Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.test.ts"
2025-05-06T23:13:45.913029Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.test.ts"
2025-05-06T23:13:45.913039Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.ts"
2025-05-06T23:13:45.913048Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.ts"
2025-05-06T23:13:45.913058Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.ts"
2025-05-06T23:13:45.913067Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.test.ts"
2025-05-06T23:13:45.913077Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/ast.ts"
2025-05-06T23:13:45.913086Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.ts"
2025-05-06T23:13:45.913095Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.ts"
2025-05-06T23:13:45.913105Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.test.ts"
2025-05-06T23:13:45.913121Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation-result.ts"
2025-05-06T23:13:45.913505Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tmp.test.ts"
2025-05-06T23:13:45.913514Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.test.ts"
2025-05-06T23:13:45.913523Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.test.ts"
2025-05-06T23:13:45.913531Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.ts"
2025-05-06T23:13:45.913554Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions"
2025-05-06T23:13:45.913583Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.test.ts"
2025-05-06T23:13:45.913592Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.ts"
2025-05-06T23:13:45.913601Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.test.ts"
2025-05-06T23:13:45.913613Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.ts"
2025-05-06T23:13:45.913622Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.test.ts"
2025-05-06T23:13:45.913631Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.ts"
2025-05-06T23:13:45.913640Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.ts"
2025-05-06T23:13:45.913648Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.test.ts"
2025-05-06T23:13:45.913656Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.ts"
2025-05-06T23:13:45.913665Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.test.ts"
2025-05-06T23:13:45.913673Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.ts"
2025-05-06T23:13:45.913681Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.ts"
2025-05-06T23:13:45.913689Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.test.ts"
2025-05-06T23:13:45.913697Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.ts"
2025-05-06T23:13:45.913705Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.test.ts"
2025-05-06T23:13:45.913713Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.ts"
2025-05-06T23:13:45.913720Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.ts"
2025-05-06T23:13:45.913728Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.test.ts"
2025-05-06T23:13:45.913741Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/index.ts"
2025-05-06T23:13:45.913749Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.test.ts"
2025-05-06T23:13:45.913757Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.test.ts"
2025-05-06T23:13:45.913783Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/__snapshots__"
2025-05-06T23:13:45.913817Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.test.ts"
2025-05-06T23:13:45.913826Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.ts"
2025-05-06T23:13:45.913833Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/function-utils.ts"
2025-05-06T23:13:45.913841Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.ts"
2025-05-06T23:13:45.913849Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.test.ts"
2025-05-06T23:13:45.913857Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.test.ts"
2025-05-06T23:13:45.913879Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes"
2025-05-06T23:13:45.913896Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes/_index.tsx"
2025-05-06T23:13:45.914172Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/README.md"
2025-05-06T23:13:45.914197Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/public"
2025-05-06T23:13:45.914228Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/public/fonts"
2025-05-06T23:13:45.914268Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/package.json"
2025-05-06T23:13:45.914289Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/scripts"
2025-05-06T23:13:45.914310Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/scripts/generate-documentation.ts"
2025-05-06T23:13:45.914332Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/.github"
2025-05-06T23:13:45.914383Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/.github/workflows"
2025-05-06T23:13:45.914410Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/.github/workflows/ci.yml"
2025-05-06T23:13:45.914420Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/tsconfig.json"
2025-05-06T23:13:45.914455Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/vite.config.ts"
2025-05-06T23:13:45.914486Z  INFO scan_sources: tailwindcss_oxide::scanner: Discovering "/Users/robin/github.com/RobinMalfait/spreadsheet/biome.json"
2025-05-06T23:13:45.914512Z  INFO scan_sources: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.914515Z  INFO extract_candidates: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.914518Z  INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.914524Z  INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: Reading 58 file(s)
2025-05-06T23:13:45.914808Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/tailwindcss-61347.log"
2025-05-06T23:13:45.914990Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/routes/_index.tsx"
2025-05-06T23:13:45.915138Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/.github/workflows/ci.yml"
2025-05-06T23:13:45.915140Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.test.ts"
2025-05-06T23:13:45.915145Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.ts"
2025-05-06T23:13:45.915163Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/tsconfig.json"
2025-05-06T23:13:45.915153Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.ts"
2025-05-06T23:13:45.915226Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/matrix.ts"
2025-05-06T23:13:45.915229Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/walk-ast.ts"
2025-05-06T23:13:45.915372Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/test/utils.ts"
2025-05-06T23:13:45.915578Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/ast.ts"
2025-05-06T23:13:45.915599Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.ts"
2025-05-06T23:13:45.915637Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.ts"
2025-05-06T23:13:45.915647Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.ts"
2025-05-06T23:13:45.915657Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/vite.config.ts"
2025-05-06T23:13:45.915680Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.test.ts"
2025-05-06T23:13:45.915681Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/flatten.ts"
2025-05-06T23:13:45.915701Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/parser.test.ts"
2025-05-06T23:13:45.915706Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/README.md"
2025-05-06T23:13:45.915691Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/utils/default-map.ts"
2025-05-06T23:13:45.915701Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.test.ts"
2025-05-06T23:13:45.915730Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/package.json"
2025-05-06T23:13:45.915753Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.ts"
2025-05-06T23:13:45.915731Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/biome.json"
2025-05-06T23:13:45.915787Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/validate.test.ts"
2025-05-06T23:13:45.915822Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/signature/tokenizer.test.ts"
2025-05-06T23:13:45.915826Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.ts"
2025-05-06T23:13:45.915885Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/root.tsx"
2025-05-06T23:13:45.915885Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/types.ts"
2025-05-06T23:13:45.915886Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.ts"
2025-05-06T23:13:45.915990Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/type-checker.test.ts"
2025-05-06T23:13:45.915995Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/text.test.ts"
2025-05-06T23:13:45.915998Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.ts"
2025-05-06T23:13:45.916000Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.ts"
2025-05-06T23:13:45.916024Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.ts"
2025-05-06T23:13:45.916056Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/function-utils.ts"
2025-05-06T23:13:45.916058Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/scripts/generate-documentation.ts"
2025-05-06T23:13:45.916070Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.ts"
2025-05-06T23:13:45.916075Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/logic.test.ts"
2025-05-06T23:13:45.916063Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.test.ts"
2025-05-06T23:13:45.916119Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation-result.ts"
2025-05-06T23:13:45.916120Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/index.ts"
2025-05-06T23:13:45.916148Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/expression.test.ts"
2025-05-06T23:13:45.916152Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/engineering.test.ts"
2025-05-06T23:13:45.916193Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.ts"
2025-05-06T23:13:45.916219Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tmp.test.ts"
2025-05-06T23:13:45.916228Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/tokenizer.test.ts"
2025-05-06T23:13:45.916245Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.test.ts"
2025-05-06T23:13:45.916256Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/spreadsheet.ts"
2025-05-06T23:13:45.916253Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/lookup.test.ts"
2025-05-06T23:13:45.916267Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/sequence.test.ts"
2025-05-06T23:13:45.916287Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.test.ts"
2025-05-06T23:13:45.916286Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/version-control.test.ts"
2025-05-06T23:13:45.916317Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/intrinsics.test.ts"
2025-05-06T23:13:45.916323Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/evaluation.ts"
2025-05-06T23:13:45.916354Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/statistics.ts"
2025-05-06T23:13:45.916562Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/date.ts"
2025-05-06T23:13:45.916609Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/domain/functions/math.ts"
2025-05-06T23:13:45.916676Z  INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.916682Z  INFO extract_candidates:parse_all_blobs: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.916688Z  INFO extract_candidates:parse_all_blobs:extract: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918271Z  INFO extract_candidates:parse_all_blobs:extract: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918282Z  INFO extract_candidates:parse_all_blobs: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918286Z  INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918288Z  INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: Reading 2 file(s)
2025-05-06T23:13:45.918315Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/app/tailwind.css"
2025-05-06T23:13:45.918504Z  INFO tailwindcss_oxide::scanner: Reading "/Users/robin/github.com/RobinMalfait/spreadsheet/out.css"
2025-05-06T23:13:45.918512Z  INFO extract_candidates:read_all_files: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918519Z  INFO extract_candidates:extract_css_variables: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918522Z  INFO extract_candidates:extract_css_variables:extract: tailwindcss_oxide::scanner: enter
2025-05-06T23:13:45.918635Z  INFO extract_candidates:extract_css_variables:extract: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.918640Z  INFO extract_candidates:extract_css_variables: tailwindcss_oxide::scanner: exit
2025-05-06T23:13:45.919059Z  INFO extract_candidates: tailwindcss_oxide::scanner: exit
```

</details>

We also output where we are writing the file to. This looks like this
when using the CLI:
<img width="1462" alt="image"
src="https://github.com/user-attachments/assets/79c2cc95-adea-4bbd-a4f1-101de45726f5"
/>

Last but not least, this also ignores `.log` files by default

## Test plan

Ran the CLI (but you can use any tool real, since this is implemented in
Oxide) with the `DEBUG=*` flag.
The file generated, looks like the example I shared above.
2025-05-07 10:19:54 -04:00
Robin Malfait
449dfcf00d
Make upgrades faster (#17898)
This PR fixes an issue where the upgrade tests were taking too long.
This PR fixes that.

Essentially when updating dependencies we did this:
```sh
npm install tailwindcss@latest
npm install @tailwindcss/postcss@latest
npm install prettier-plugin-tailwindcss@latest
```

But this is not ideal, because we are calling out to `npm` and run each
dependency in isolation.

With this PR, we essentially do it all in one go:
```sh
npm install tailwindcss@latest @tailwindcss/postcss@latest prettier-plugin-tailwindcss@latest
```

## Test plan

Testing this locally, the results look like this:

| Before | After |
|--------|-------|
| <img width="590" alt="image"
src="https://github.com/user-attachments/assets/c899d432-78c3-4945-af73-3ef4fffa08da"
/> | <img width="656" alt="image"
src="https://github.com/user-attachments/assets/a448d711-dd74-44cf-9790-c8a14fc6964f"
/> |


In CI:

| Before (with a failure) | After |
| --- | --- |
| <img width="224" alt="image"
src="https://github.com/user-attachments/assets/f58a6bf6-fdbe-4474-aa1f-444ab51a53c9"
/> | <img width="224" alt="image"
src="https://github.com/user-attachments/assets/54606df5-4f69-444b-8d4c-5ce27c5d6b41"
/> |

[ci-all]
2025-05-06 20:02:13 +02:00
Brandon McConnell
4f8539c063
Fix bug replacing modifier variable shorthand syntax underscores (#17889)
Resolves #17888

**Reproduction URL:** https://play.tailwindcss.com/YvIekuzVRd

Changes:
* Don't use `decodeArbitraryValue` when parsing variable shorthand
syntax in modifiers
* replace `decodeArbitraryValue(modifier.slice(1, -1))` with
`modifier.slice(1, -1)`
  * added test case, passing ✅

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-05-06 09:32:47 -04:00
depfu[bot]
ed45952d15
Update turbo 2.5.0 → 2.5.2 (patch) (#17887) 2025-05-06 12:54:26 +02:00
Robin Malfait
5c5ae04db6
Fix Windows CI build (#17878)
This PR fixes a Windows CI issue due to the recently merged #17846

There might be better ways to solve this, but essentially we want to
make sure we are always dealing with `\n` and not `\r\n`. This PR fixes
that by replacing all `\r\n` with `\n` in the tests.
2025-05-05 17:38:53 +02:00
Jordan Pittman
6a1df6acf6
Ignore @tailwind utilities inside @reference (#17836)
You can use `@reference "tailwindcss"` or `@reference
"../path/to/your/css/file.css"` to reference your theme for use in
`@apply`, `theme(…)`, etc…

Unfortunatley, because the imported file still contains `@tailwind
utilities` it would trigger a re-scan of the filesystem — even though
the use of `@reference` ensures that no CSS can actually be output by
the import itself.

This PR does two things:
- Adds some explicit feature detection tests for what features we pick
up in a stylesheet based on the CSS written and how things are imported
- Explicitly ignores `@tailwind utilities` inside of `@reference` so it
isn't a trigger for file scanning

Because of how Vite itself handles dependencies editing files on disk
will still trigger a rebuild of any file using `@reference`. This is
because Vite rebuilds files when _any_ of its transitive dependencies
change.

For example, given this Vue file:
```vue
<style>
@reference "./styles.css";
</style>
<template> <!-- ... --> </template>
```

And this stylesheet:
```css
@import "tailwindcss";
```

The dependency chain looks like this: `file.vue -> styles.css -> {all
the sources in your project}`

Vite sees that a file (e.g. `index.html`) has changed, thus `styles.css`
needs change, which means `file.vue` needs to be compiled again as well.
Now in reality we depend on the _on disk_ version of styles.css not the
compiled version but Vite itself doesn't know that (or have a way to
indicate this afaik).

Coming up with a solution to that problem will have to be a separate PR
— but there is a workaround:

### 1. Inline the imports from `@import "tailwindcss";`

Replace this in your main stylesheet:
```css
@import "tailwindcss";
```

with this (this is basically what `node_modules/tailwindcss/index.css`
is):
```css
@layer theme, base, components, utilities;

@import 'tailwindcss/theme' layer(theme);
@import 'tailwindcss/preflight' layer(base);
@import 'tailwindcss/utilities' layer(utilities);

/* the rest of your styles imports, styles, etc… */
```

### 2. Split your stylesheet into "main" and "theme" parts

Your "theme" is comprised of the `@import 'tailwindcss/theme'
layer(theme);` import, any custom `@theme` blocks, any `@config`
directives, and any `@plugin` directives. Move all of these into their
own file.

For example, replace this with two files:

```css
@layer theme, base, components, utilities;
@import 'tailwindcss/theme' layer(theme);
@import 'tailwindcss/preflight' layer(base);
@import 'tailwindcss/utilities' layer(utilities);

@theme {
  --color-primary: #c0ffee;
}

@plugin "./my-plugin.js";

/* the rest of your styles imports, styles, etc… */
```

with a theme file:
```css
@import 'tailwindcss/theme' layer(theme);

/* all your `@theme` stuff goes in this file */
@theme {
  --color-primary: #c0ffee;
}

/* additionally any @config or @plugin does too */
@plugin "./my-plugin.js";
```

and your main CSS file:
```css
@layer theme, base, components, utilities;
@import './my-theme.css'; /* I replaced this import */
@import 'tailwindcss/preflight' layer(base);
@import 'tailwindcss/utilities' layer(utilities);

/* the rest of your styles imports, styles, etc… */
```

### 3. Import only your "theme" file in your Vue components / CSS
modules / etc…

```vue
<style>
@reference "./my-theme.css";
</style>
<template> <!-- ... --> </template>
```

Fixes #17693
2025-05-05 11:21:55 -04:00
Robin Malfait
d38554d110
Fix HAML extraction with embedded Ruby (#17846)
This PR fixes and improves the HAML extractor by ensuring that whenever
we detect lines or blocks of Ruby code (`-`, `=` and `~`) characters at
the beginning of the line, we treat them as Ruby code.

Fixes: #17813

## Test Plan

1. Existing tests pass
2. Changed 1 existing test which embedded Ruby syntax
3. Added a dedicated test to ensure the HAML file in the linked issue is
parsed correctly

Running this in the internal extractor tool you can see that the
`w-[12px]`, `w-[16px]`, `h-[12px]`, and `h-[16px]` are properly
extracted.

Note: the `mr-12px` is also extracted, but not highlighted because this
is not a valid Tailwind CSS class.

<img width="1816" alt="image"
src="https://github.com/user-attachments/assets/fc5929ca-bc71-47d2-b21b-7abeec86f54d"
/>
2025-05-05 10:26:17 -04:00
depfu[bot]
473f0241bf
Update h3 1.15.1 → 1.15.3 (patch) (#17873) 2025-05-05 10:30:10 +00:00
depfu[bot]
e00d0926eb
Update @vitejs/plugin-react 4.3.4 → 4.4.1 (minor) (#17862) 2025-05-04 19:28:46 +02:00
depfu[bot]
dd5ec49606
Update eslint 9.24.0 → 9.25.1 (minor) (#17850)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.24.0 → 9.25.1) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.25.1">9.25.1</a></h4>

<blockquote><h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="cdc8e8c950"><code
class="notranslate">cdc8e8c</code></a> fix: revert directive detection
in no-unused-expressions (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19639">#19639</a>)
(sethamus)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="1f2b057ddc"><code
class="notranslate">1f2b057</code></a> chore: upgrade @eslint/js@9.25.1
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19642">#19642</a>)
(Milos Djermanovic)</li>
<li>
<a
href="771317fa93"><code
class="notranslate">771317f</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.25.0">9.25.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="dcd95aafa3"><code
class="notranslate">dcd95aa</code></a> feat: support TypeScript syntax
in no-empty-function rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19551">#19551</a>)
(sethamus)</li>
<li>
<a
href="77d6d5bc49"><code
class="notranslate">77d6d5b</code></a> feat: support TS syntax in <code
class="notranslate">no-unused-expressions</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19564">#19564</a>)
(Sweta Tanwar)</li>
<li>
<a
href="90228e5d57"><code
class="notranslate">90228e5</code></a> feat: support <code
class="notranslate">JSRuleDefinition</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19604">#19604</a>)
(루밀LuMir)</li>
<li>
<a
href="59ba6b7378"><code
class="notranslate">59ba6b7</code></a> feat: add allowObjects option to
no-restricted-properties (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19607">#19607</a>)
(sethamus)</li>
<li>
<a
href="db650a036b"><code
class="notranslate">db650a0</code></a> feat: support TypeScript syntax
in <code class="notranslate">no-invalid-this</code> rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19532">#19532</a>)
(Tanuj Kanti)</li>
<li>
<a
href="9535cffe7b"><code
class="notranslate">9535cff</code></a> feat: support TS syntax in <code
class="notranslate">no-loop-func</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19559">#19559</a>)
(Nitin Kumar)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="910bd13c4c"><code
class="notranslate">910bd13</code></a> fix: <code
class="notranslate">nodeTypeKey</code> not being used in <code
class="notranslate">NodeEventGenerator</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19631">#19631</a>)
(StyleShit)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="ca7a735dde"><code
class="notranslate">ca7a735</code></a> docs: update <code
class="notranslate">no-undef-init</code> when not to use section (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19624">#19624</a>)
(Tanuj Kanti)</li>
<li>
<a
href="1b870c9da4"><code
class="notranslate">1b870c9</code></a> docs: use <code
class="notranslate">eslint-config-xo</code> in the getting started guide
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19629">#19629</a>)
(Nitin Kumar)</li>
<li>
<a
href="5d4af16ab1"><code
class="notranslate">5d4af16</code></a> docs: add types for multiple rule
options (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19616">#19616</a>)
(Tanuj Kanti)</li>
<li>
<a
href="e8f8d57bd6"><code
class="notranslate">e8f8d57</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="a40348f1f6"><code
class="notranslate">a40348f</code></a> docs: no-use-before-define tweaks
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19622">#19622</a>)
(Kirk Waiblinger)</li>
<li>
<a
href="0ba3ae3e5a"><code
class="notranslate">0ba3ae3</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="865dbfed6c"><code
class="notranslate">865dbfe</code></a> docs: ensure "learn more"
deprecation links point to useful resource (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19590">#19590</a>)
(Kirk Waiblinger)</li>
<li>
<a
href="f80b746d85"><code
class="notranslate">f80b746</code></a> docs: add known limitations for
no-self-compare (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19612">#19612</a>)
(Nitin Kumar)</li>
<li>
<a
href="865aed6293"><code
class="notranslate">865aed6</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="88dc1965a4"><code
class="notranslate">88dc196</code></a> chore: upgrade @eslint/js@9.25.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19636">#19636</a>)
(Milos Djermanovic)</li>
<li>
<a
href="345288d7b2"><code
class="notranslate">345288d</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="affe6be018"><code
class="notranslate">affe6be</code></a> chore: upgrade trunk (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19628">#19628</a>)
(sethamus)</li>
<li>
<a
href="dd20cf274e"><code
class="notranslate">dd20cf2</code></a> test: fix <code
class="notranslate">no-loop-func</code> test with duplicate variable
reports (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19610">#19610</a>)
(Milos Djermanovic)</li>
<li>
<a
href="bd05397ef6"><code
class="notranslate">bd05397</code></a> chore: upgrade <code
class="notranslate">@eslint/*</code> dependencies (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19606">#19606</a>)
(Milos Djermanovic)</li>
<li>
<a
href="22ea18b8ba"><code
class="notranslate">22ea18b</code></a> chore: replace invalid <code
class="notranslate">int</code> type with <code
class="notranslate">number</code> inside JSDocs. (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19597">#19597</a>)
(Arya Emami)</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="d49f5b7333...3ed4b3652d">See
the full diff on Github</a>. The new version differs by 29 commits:</p>
<ul>
<li><a
href="3ed4b3652d"><code>9.25.1</code></a></li>
<li><a
href="7a19ccd052"><code>Build:
changelog update for 9.25.1</code></a></li>
<li><a
href="1f2b057ddc"><code>chore:
upgrade @eslint/js@9.25.1 (#19642)</code></a></li>
<li><a
href="771317fa93"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="cdc8e8c950"><code>fix:
revert directive detection in no-unused-expressions
(#19639)</code></a></li>
<li><a
href="e62e267615"><code>9.25.0</code></a></li>
<li><a
href="bc2c3e6acc"><code>Build:
changelog update for 9.25.0</code></a></li>
<li><a
href="88dc1965a4"><code>chore:
upgrade @eslint/js@9.25.0 (#19636)</code></a></li>
<li><a
href="345288d7b2"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="910bd13c4c"><code>fix:
`nodeTypeKey` not being used in `NodeEventGenerator`
(#19631)</code></a></li>
<li><a
href="ca7a735dde"><code>docs:
update `no-undef-init` when not to use section (#19624)</code></a></li>
<li><a
href="affe6be018"><code>chore:
upgrade trunk (#19628)</code></a></li>
<li><a
href="1b870c9da4"><code>docs:
use `eslint-config-xo` in the getting started guide
(#19629)</code></a></li>
<li><a
href="5d4af16ab1"><code>docs:
add types for multiple rule options (#19616)</code></a></li>
<li><a
href="e8f8d57bd6"><code>docs:
Update README</code></a></li>
<li><a
href="a40348f1f6"><code>docs:
no-use-before-define tweaks (#19622)</code></a></li>
<li><a
href="0ba3ae3e5a"><code>docs:
Update README</code></a></li>
<li><a
href="865dbfed6c"><code>docs:
ensure &quot;learn more&quot; deprecation links point to useful resource
(#19590)</code></a></li>
<li><a
href="dcd95aafa3"><code>feat:
support TypeScript syntax in no-empty-function rule
(#19551)</code></a></li>
<li><a
href="77d6d5bc49"><code>feat:
support TS syntax in `no-unused-expressions` (#19564)</code></a></li>
<li><a
href="90228e5d57"><code>feat:
support `JSRuleDefinition` type (#19604)</code></a></li>
<li><a
href="f80b746d85"><code>docs:
add known limitations for no-self-compare (#19612)</code></a></li>
<li><a
href="59ba6b7378"><code>feat:
add allowObjects option to no-restricted-properties
(#19607)</code></a></li>
<li><a
href="db650a036b"><code>feat:
support TypeScript syntax in `no-invalid-this` rule
(#19532)</code></a></li>
<li><a
href="dd20cf274e"><code>test:
fix `no-loop-func` test with duplicate variable reports
(#19610)</code></a></li>
<li><a
href="9535cffe7b"><code>feat:
support TS syntax in `no-loop-func` (#19559)</code></a></li>
<li><a
href="bd05397ef6"><code>chore:
upgrade `@eslint/*` dependencies (#19606)</code></a></li>
<li><a
href="22ea18b8ba"><code>chore:
replace invalid `int` type with `number` inside JSDocs.
(#19597)</code></a></li>
<li><a
href="865aed6293"><code>docs:
Update README</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>
2025-05-03 01:04:36 +02:00
Robin Malfait
c095071f22
Skip .css files when migrating templates (#17854)
This PR fixes an issue where the upgrade tool also migrates `.css` files
as-if they are content files. This is not the intended behavior.

## Test plan

Ran this on my personal website. 

Before:
<img width="1316" alt="image"
src="https://github.com/user-attachments/assets/2b7337c6-7b88-4811-911f-139ab2e31b3b"
/>

After: 
<img width="1046" alt="image"
src="https://github.com/user-attachments/assets/55f09355-37cb-419b-9924-973cf2681c1d"
/>
2025-05-03 00:48:45 +02:00
Robin Malfait
4e4275638f
Design system driven upgrade migrations (#17831)
This PR introduces a vastly improved upgrade migrations system, to
migrate your codebase and modernize your utilities to make use of the
latest variants and utilities.

It all started when I saw this PR the other day:
https://github.com/tailwindlabs/tailwindcss/pull/17790

I was about to comment "Don't forget to add a migration". But I've been
thinking about a system where we can automate this process away. This PR
introduces this system.

This PR introduces upgrade migrations based on the internal Design
System, and it mainly updates arbitrary variants, arbitrary properties
and arbitrary values.

## The problem

Whenever we ship new utilities, or you make changes to your CSS file by
introducing new `@theme` values, or adding new `@utility` rules. It
could be that the rest of your codebase isn't aware of that, but you
could be using these values.

For example, it could be that you have a lot of arbitrary properties in
your codebase, they look something like this:

```html
<div class="[color-scheme:dark] [text-wrap:balance]"></div>
```

Whenever we introduce new features in Tailwind CSS, you probably don't
keep an eye on the release notes and update all of these arbitrary
properties to the newly introduced utilities.

But with this PR, we can run the upgrade tool:

```console
npx -y @tailwindcss/upgrade@latest
```

...and it will upgrade your project to use the new utilities:

```html
<div class="scheme-dark text-balance"></div>
```

It also works for arbitrary values, for example imagine you have classes
like this:

```html
<!-- Arbitrary property -->
<div class="[max-height:1lh]"></div>

<!-- Arbitrary value -->
<div class="max-h-[1lh]"></div>
```

Running the upgrade tool again:

```console
npx -y @tailwindcss/upgrade@latest
```

... gives you the following output:

```html
<!-- Arbitrary property -->
<div class="max-h-lh"></div>

<!-- Arbitrary value -->
<div class="max-h-lh"></div>
```

This is because of the original PR I mentioned, which introduced the
`max-h-lh` utilities.

A nice benefit is that this output only has 1 unique class instead of 2,
which also potentially reduces the size of your CSS file.

It could also be that you are using arbitrary values where you (or a
team member) didn't even know an alternative solution existed.

E.g.:

```html
<div class="w-[48rem]"></div>
```

After running the upgrade tool you will get this:

```html
<div class="w-3xl"></div>
```

We can go further though. Since the release of Tailwind CSS v4, we
introduced the concept of "bare values". Essentially allowing you to
type a number on utilities where it makes sense, and we produce a value
based on that number.

So an input like this:

```html
<div class="border-[123px]"></div>
```

Will be optimized to just:

```html
<div class="border-123"></div>
```

This can be very useful for complex utilities, for example, how many
times have you written something like this:

```html
<div class="grid-cols-[repeat(16,minmax(0,1fr))]"></div>
```

Because up until Tailwind CSS v4, we only generated 12 columns by
default. But since v4, we can generate any number of columns
automatically.

Running the migration tool will give you this:

```html
<div class="grid-cols-16"></div>
```

### User CSS

But, what if I told you that we can keep going...

In [Catalyst](https://tailwindcss.com/plus/ui-kit) we often use classes
that look like this for accessibility reasons:

```html
<div class="text-[CanvasText] bg-[Highlight]"></div>
```

What if you want to move the `CanvasText` and `Highlight` colors to your
CSS:

```css
@import "tailwincdss";

@theme {
  --color-canvas: CanvasText;
  --color-highlight: Highlight;
}
```

If you now run the upgrade tool again, this will be the result:

```html
<div class="text-canvas bg-highlight"></div>
```

We never shipped a `text-canvas` or `bg-highlight` utility, but the
upgrade tool uses your own CSS configuration to migrate your codebase.

This will keep your codebase clean, consistent and modern and you are in
control.

Let's look at one more example, what if you have this in a lot of
places:

```html
<div class="[scrollbar-gutter:stable]"></div>
```

And you don't want to wait for the Tailwind CSS team to ship a
`scrollbar-stable` (or similar) feature. You can add your own utility:

```css
@import "tailwincdss";

@utility scrollbar-stable {
  scrollbar-gutter: stable;
}
```

```html
<div class="scrollbar-stable"></div>
```

## The solution — how it works

There are 2 big things happening here:

1. Instead of us (the Tailwind CSS team) hardcoding certain migrations,
we will make use of the internal `DesignSystem` which is the source of
truth for all this information. This is also what Tailwind CSS itself
uses to generate the CSS file.

   The internal `DesignSystem` is essentially a list of all:

   1. The internal utilities
   2. The internal variants
   3. The default theme we ship
   4. The user CSS
      1. With custom `@theme` values
      2. With custom `@custom-variant` implementations
      3. With custom `@utility` implementations
2. The upgrade tool now has a concept of `signatures`

The signatures part is the most interesting one, and it allows us to be
100% sure that we can migrate your codebase without breaking anything.

A signature is some unique identifier that represents a utility. But 2
utilities that do the exact same thing will have the same signature.

To make this work, we have to make sure that we normalize values. One
such value is the selector. I think a little visualization will help
here:

| UTILITY          | GENERATED SIGNATURE     |
| ---------------- | ----------------------- |
| `[display:flex]` | `.x { display: flex; }` |
| `flex`           | `.x { display: flex; }` |

They have the exact same signature and therefore the upgrade tool can
safely migrate them to the same utility.

For this we will prefer the following order:

1. Static utilities — essentially no brackets. E.g.: `flex`,
`grid-cols-2`
2. Arbitrary values — e.g.: `max-h-[1lh]`, `border-[2px]`
3. Arbitrary properties — e.g.: `[color-scheme:dark]`, `[display:flex]`

We also have to canonicalize utilities to there minimal form.
Essentially making sure we increase the chance of finding a match.

```
[display:_flex_] → [display:flex] → flex
[display:_flex]  → [display:flex] → flex
[display:flex_]  → [display:flex] → flex
[display:flex]   → [display:flex] → flex
```

If we don't do this, then the signatures will be slightly different, due
to the whitespace:

| UTILITY            | GENERATED SIGNATURE       |
| ------------------ | ------------------------- |
| `[display:_flex_]` | `.x { display:  flex ; }` |
| `[display:_flex]`  | `.x { display:  flex; }`  |
| `[display:flex_]`  | `.x { display: flex ; }`  |
| `[display:flex]`   | `.x { display: flex; }`   |

### Other small improvements

A few other improvements are for optimizing existing utilities:

1. Remove unnecessary data types. E.g.:

   - `bg-[color:red]` -> `bg-[red]`
- `shadow-[shadow:inset_0_1px_--theme(--color-white/15%)]` ->
`shadow-[inset_0_1px_--theme(--color-white/15%)]`

This also makes use of these signatures and if dropping the data type
results in the same signature then we can safely drop it.

Additionally, if a more specific utility exists, we will prefer that
one. This reduced ambiguity and the need for data types.

   - `bg-[position:123px]` → `bg-position-[123px]`
   - `bg-[123px]` → `bg-position-[123px]`
   - `bg-[size:123px]` → `bg-size-[123px]`


2. Optimizing modifiers. E.g.:
   - `bg-red-500/[25%]` → `bg-red-500/25`
   - `bg-red-500/[100%]` → `bg-red-500`
   - `bg-red-500/100` → `bg-red-500`

3. Hoist `not` in arbitrary variants

- `[@media_not_(prefers-color-scheme:dark)]:flex` →
`not-[@media_(prefers-color-scheme:dark)]:flex` → `not-dark:flex` (in
case you are using the default `dark` mode implementation

4. Optimize raw values that could be converted to bare values. This uses
the `--spacing` variable to ensure it is safe.

   - `w-[64rem]` → `w-256`

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-05-02 23:18:06 +02:00
Philipp Spiess
45cd32eed7
Prepare v4.1.5 release (#17830)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-04-30 16:57:44 +02:00
Philipp Spiess
a636933cd4
Add discrete properties to the default list of transition properties (#17812)
This PR changes the `transition` utility to include five new properties:

- `display`
- `visibility`
- `content-visibility`
- `overlay`
- `pointer-eventes`

On its own, this change does nothing since these properties will apply
their change _immediately_. However, in combination with
`transition-discrete` this will ensure that you can now transition these
types without requiering `transition-all` or arbitrary transition
properties.

## Test plan

- Ensured this works in the Vite playground with native `<dialog>`
components


https://github.com/user-attachments/assets/89bf4a75-b681-4574-8bb4-845fffdec43b

Notice how:

- the backdrop stays open until the transition is over (that's because
of `overlay` in the property list)
- the dialog is displayed until the transition is over

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-04-30 14:30:58 +02:00
depfu[bot]
ab4eb18bab
Update @types/react-dom 19.1.1 → 19.1.2 (patch) (#17829) 2025-04-30 11:50:44 +02:00
Robin Malfait
dbc8023a08
Do not sort and format stylesheets that didn't change (#17824)
This PR improves the upgrade tooling at tiny bit to make sure that as
long as we didn't change any of the stylesheets, that we also don't sort
internal nodes and/or format the stylesheet at all.

This is important in case the Prettier rules are different or if a
totally different formatter is used.

Essentially, if we didn't have to change the stylesheets because of a
migration, we don't want to change it due to a formatter either.
2025-04-29 17:07:35 +00:00
Philipp Spiess
d3846a4570
Update test to retry assertion on empty file and don't include forward-slash in the assertion (#17821)
To fix the CI issues we have, it turns out there are two issues:

1. The file-read was not retried, making it possible for all platforms
(but mostly Windows because it's the slowest) to sometimes not have the
file created yet before making the assertion.
2. A forward-slash in the assertion message path would always be a
backslash when run on Windows, thus the Windows test would never pass.

## Test plan

 - [ci-all] and have the test pass two times.
2025-04-29 18:16:58 +02:00
depfu[bot]
9fec4ef60b
Update bun 1.2.8 → 1.2.11 (patch) (#17816) 2025-04-29 17:58:01 +02:00
Robin Malfait
d2daf59524
Skip color-mix(…) when opacity is 100% (#17815)
This PR improves colors with alpha values where the alpha value results
in 100%.

Before this change, a class like `bg-red-500/100` would be generated as:

```css
.bg-red-500\/100 {
  background-color: #ef4444;
}

@supports (color: color-mix(in lab, red, red)) {
  .bg-red-500\/100 {
    background-color: color-mix(in oklab, var(--color-red-500) 100%, transparent);
  }
}
```

But we don't need the `color-mix`, or the fallback styles at all in case
the alpha value is 100%.

After this change the `bg-red-500/100` class will generate as:

```css
.bg-red-500\/100 {
  background-color: var(--color-red-500);
}
```

Which is essentially the same as `bg-red-500`, but we can migrate that
away in the future. At least the generated CSS is smaller.

## Test plan

1. Added a test to ensure the generated value doesn't include color-mix
at all.
2025-04-28 13:30:01 -04:00
Robin Malfait
3a1b27e3f8
Pretty print variants starting with @ (#17814)
While working on another PR I noticed that some variants were re-printed
in an odd way.

Specifically, this PR fixes an issue where variants using the `@`-root
were incorrectly printed.

- `@lg` was printed as `@-lg`
- `@[400px]` was printed as `@-[400px]`

This is now special cased where the `-` is not inserted for `@`-root
variants.

## Test plan

1. Added a test to ensure the `@`-root variants are printed correctly.
2025-04-28 13:25:04 -04:00
Philipp Spiess
af1d4aa683 Temporarily disable broken Windows test 2025-04-28 10:53:46 +02:00
Jordan Pittman
ba103799f7
Add h-lh/min-h-lh/max-h-lh utilities to match an elements line height (#17790)
This PR adds the following utilities that can be used to match an
elements line height:

- `h-lh`
- `min-h-lh`
- `max-h-lh`

These are all equivalent to providing `1lh` as an arbitrary value. e.g.
`h-[1lh]`
2025-04-26 15:14:45 -04:00
Philipp Spiess
62ca1ec42d Fix Windows tests 2025-04-25 13:02:18 +02:00
Philipp Spiess
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.
2025-04-25 12:02:10 +02:00
depfu[bot]
231cdddb94 Update all of nextjs to version 15.3.1 2025-04-25 09:59:56 +00:00
depfu[bot]
d780a55e65
Update @playwright/test 1.51.1 → 1.52.0 (minor) (#17780) 2025-04-25 11:52:55 +02:00
Robin Malfait
2bf2b4db98
update changelog 2025-04-24 11:14:27 +02:00
Robin Malfait
46758f7c29
Bump all Tailwind CSS related dependencies during upgrade (#17763)
This PR bumps all Tailwind CSS related dependencies when running the
upgrade tool _if_ the dependency exists in your package.json file.

E.g.: if you have `tailwindcss` and `@tailwindcss/vite` in your
package.json, then both will be updated to the latest version.

This PR is not trying to be smart and skip updating if you are already
on the latest version. It will just try and update the dependencies and
your package manager will do nothing in case it was already the latest
version.

## Test plan

Ran this on one of my personal projects and this was the output:
<img width="1023" alt="image"
src="https://github.com/user-attachments/assets/a189fe7a-a58a-44aa-9246-b720e7a2a892"
/>


Notice that I don't have `@tailwindcss/vite` logs because I am using
`@tailwindcss/postcss`.
2025-04-24 11:13:21 +02:00
Philipp Spiess
a7f4a4d3b8
Upgrade wasm related dependencies (#17753)
Closes #17752
Closes #17748
Closes #17747
Closes #17746
Closes #17745
2025-04-23 15:15:06 +02:00
Robin Malfait
8e826b18f3
Ensure @tailwindcss/upgrade runs on Tailwind CSS v4 projects and is idempotent (#17717)
This PR ensures that the `@tailwindcss/upgrade` tool works on existing
Tailwind CSS v4 projects. This PR also ensures that the upgrade tool is
idempotent, meaning that it can be run multiple times and it should
result in the same output.

One awesome feature this unlocks is that you can run the upgrade tool on
your codebase at any time and upgrade classes if you still have some
legacy syntaxes, such as `bg-[var(--my-color)]`, in your muscle memory.

One small note: If something changed in the first run, re-running will
not work immediately because your git repository will not be clean and
the upgrade tool requires your git repo to be clean. But once you
verified and committed your changes, the upgrade tool will be
idempotent.

Idempotency is guaranteed by ensuring that some migrations are skipped
by checking what version of Tailwind CSS you are on _before_ the version
is upgraded.

For the Tailwind CSS version: We will resolve `tailwindcss` itself to
know the _actual_ version that is installed (the one resolved from
`node_modules`). Not the one available in your package.json. Your
`package.json` could be out of sync if you reverted changes but didn't
run `npm install` yet.

Back to Idempotency:

For example, we have migrations where we change the variant order of
stacked variants. If we would run these migrations every time you run
the upgrade tool then we would be flip-flopping the order every run.

See: https://tailwindcss.com/docs/upgrade-guide#variant-stacking-order

Another example is where we rename some utilities. For example, we
rename:

| Before      | After       |
| ----------- | ----------- |
| `shadow`    | `shadow-sm` |
| `shadow-sm` | `shadow-xs` |

Notice how we have `shadow-sm` in both the `before` and `after` column.

If we would run the upgrade tool again, then we would eventually migrate
your original `shadow` to `shadow-sm` (first run) and then to
`shadow-xs` (second run). Which would result in the wrong shadow.

See: https://tailwindcss.com/docs/upgrade-guide#renamed-utilities

---

The order of upgrade steps changed a bit as well to make the internals
are easier to work with and reason about.

1. Find CSS files
2. Link JS config files (if you are in a Tailwind CSS v3 project)
3. Migrate the JS config files (if you are in a Tailwind CSS v3 project)
4. Upgrade Tailwind CSS to v4 (or the latest version at that point)
5. Migrate the stylesheets (we used to migrate the source files first)
6. Migrate the source files

This is done so that step 5 and 6 will always operate on a Tailwind CSS
v4 project and we don't need to check the version number again. This is
also necessary because your CSS file will now very likely contain
`@import "tailwindcss";` which doesn't exist in Tailwind CSS v3.

This also means that we can rely on the same internals that Tailwind CSS
actually uses for locating the source files. We will use
`@tailwindcss/oxide`'s scanner to find the source files (and it also
keeps your custom `@source` directives into account).

This PR also introduces a few actual migrations related to recent
features and changes we shipped.

1. We migrate deprecated classes to their new names:

   | Before                | After                 |
   | --------------------- | --------------------- |
   | `bg-left-top`         | `bg-top-left`         |
   | `bg-left-bottom`      | `bg-bottom-left`      |
   | `bg-right-top`        | `bg-top-right`        |
   | `bg-right-bottom`     | `bg-bottom-right`     |
   | `object-left-top`     | `object-top-left`     |
   | `object-left-bottom`  | `object-bottom-left`  |
   | `object-right-top`    | `object-top-right`    |
   | `object-right-bottom` | `object-bottom-right` |

   Introduced in:

   - https://github.com/tailwindlabs/tailwindcss/pull/17378
   - https://github.com/tailwindlabs/tailwindcss/pull/17437

2. We migrate simple arbitrary variants to their dedicated variant:

   | Before                  | After               |
   | ----------------------- | ------------------- |
   | `[&:user-valid]:flex`   | `user-valid:flex`   |
   | `[&:user-invalid]:flex` | `user-invalid:flex` |

   Introduced in:

   - https://github.com/tailwindlabs/tailwindcss/pull/12370

3. We migrate `@media` variants to their dedicated variant:

| Before | After |
| ----------------------------------------------------- |
------------------------- |
| `[@media_print]:flex` | `print:flex` |
| `[@media(prefers-reduced-motion:no-preference)]:flex` |
`motion-safe:flex` |
| `[@media(prefers-reduced-motion:reduce)]:flex` | `motion-reduce:flex`
|
| `[@media(prefers-contrast:more)]:flex` | `contrast-more:flex` |
| `[@media(prefers-contrast:less)]:flex` | `contrast-less:flex` |
| `[@media(orientation:portrait)]:flex` | `portrait:flex` |
| `[@media(orientation:landscape)]:flex` | `landscape:flex` |
| `[@media(forced-colors:active)]:flex` | `forced-colors:flex` |
| `[@media(inverted-colors:inverted)]:flex` | `inverted-colors:flex` |
| `[@media(pointer:none)]:flex` | `pointer-none:flex` |
| `[@media(pointer:coarse)]:flex` | `pointer-coarse:flex` |
| `[@media(pointer:fine)]:flex` | `pointer-fine:flex` |
| `[@media(any-pointer:none)]:flex` | `any-pointer-none:flex` |
| `[@media(any-pointer:coarse)]:flex` | `any-pointer-coarse:flex` |
| `[@media(any-pointer:fine)]:flex` | `any-pointer-fine:flex` |
| `[@media(scripting:none)]:flex` | `noscript:flex` |

The new variants related to `inverted-colors`, `pointer`, `any-pointer`
and `scripting` were introduced in:

   - https://github.com/tailwindlabs/tailwindcss/pull/11693
   - https://github.com/tailwindlabs/tailwindcss/pull/16946
   - https://github.com/tailwindlabs/tailwindcss/pull/11929
   - https://github.com/tailwindlabs/tailwindcss/pull/17431

   This also applies to the `not` case, e.g.:

| Before | After |
| --------------------------------------------------------- |
----------------------------- |
| `[@media_not_print]:flex` | `not-print:flex` |
| `[@media_not(prefers-reduced-motion:no-preference)]:flex` |
`not-motion-safe:flex` |
| `[@media_not(prefers-reduced-motion:reduce)]:flex` |
`not-motion-reduce:flex` |
| `[@media_not(prefers-contrast:more)]:flex` | `not-contrast-more:flex`
|
| `[@media_not(prefers-contrast:less)]:flex` | `not-contrast-less:flex`
|
| `[@media_not(orientation:portrait)]:flex` | `not-portrait:flex` |
| `[@media_not(orientation:landscape)]:flex` | `not-landscape:flex` |
| `[@media_not(forced-colors:active)]:flex` | `not-forced-colors:flex` |
| `[@media_not(inverted-colors:inverted)]:flex` |
`not-inverted-colors:flex` |
| `[@media_not(pointer:none)]:flex` | `not-pointer-none:flex` |
| `[@media_not(pointer:coarse)]:flex` | `not-pointer-coarse:flex` |
| `[@media_not(pointer:fine)]:flex` | `not-pointer-fine:flex` |
| `[@media_not(any-pointer:none)]:flex` | `not-any-pointer-none:flex` |
| `[@media_not(any-pointer:coarse)]:flex` |
`not-any-pointer-coarse:flex` |
| `[@media_not(any-pointer:fine)]:flex` | `not-any-pointer-fine:flex` |
| `[@media_not(scripting:none)]:flex` | `not-noscript:flex` |

For each candidate, we run a set of upgrade migrations. If at the end of
the migrations the original candidate is still the same as the new
candidate, then we will parse & print the candidate one more time to
pretty print into consistent classes. Luckily parsing is cached so there
is no real downside overhead.

Consistency (especially with arbitrary variants and values) will reduce
your CSS file because there will be fewer "versions" of your class.

Concretely, the pretty printing will apply changes such as:

| Before                 | After             |
| ---------------------- | ----------------- |
| `bg-[var(--my-color)]` | `bg-(--my-color)` |
| `bg-[rgb(0,_0,_0)]`    | `bg-[rgb(0,0,0)]` |

Another big important reason for this change is that these classes on
their own
would have been migrated _if_ another migration was relevant for this
candidate.
This means that there are were some inconsistencies. E.g.:

| Before | After | Reason |
| ----------------------- | ---------------------- |
------------------------------------ |
| `!bg-[var(--my-color)]` | `bg-(--my-color)!` | Because the `!` is in
the wrong spot |
| `bg-[var(--my-color)]` | `bg-[var(--my-color)]` | Because no
migrations rand |

As you can see, the way the `--my-color` variable is used, is different.
This
changes will make sure it will now always be consistent:
| Before | After |
| ----------------------- | ---------------------- |
| `!bg-[var(--my-color)]` | `bg-(--my-color)!` |
| `bg-[var(--my-color)]` | `bg-(--my-color)` |

Yay!

Of course, if you don't want these more cosmetic changes, you can always
ignore the upgrade and revert these changes and only commit the changes
you want.

# Test plan

- All existing tests still pass.
- But I had to delete 1 test (we tested that Tailwind CSS v3 was
required).
- And had to mock the `version.isMajor` call to ensure we run the
individual migration tests correctly.
- Added new tests to test:
  1. Migrating Tailwind CSS v4 projects works
  1. Idempotency of the upgrade tool

[ci-all]
2025-04-22 11:10:46 -04:00
Robin Malfait
25ec6a3b7d
Ignore .db files by default (#17711)
This PR ignores `.db` files by default. We already ignored `.sqlite` and
`.sqlite3` files but we didn't ignore `.db` files which is a common
extension for database files as well.

Due to the binary nature of `.db` files, scanning these could result in
hard to debug errors such as:


![image](https://github.com/user-attachments/assets/52f779ac-cd5f-4f37-9615-2163bf852999)
2025-04-22 14:53:11 +00:00
Jordan Pittman
8bf06ab770
Handle legacy key behavior in theme-driven suggestions for @utility (#17733)
Fixes
https://github.com/tailwindlabs/tailwindcss-intellisense/issues/1328

The alpha and beta releases used `_` in theme keys to represent a `.`.
This meant we used `--leading-1_5` instead of `--leading-1\.5` to add
utilities like `leading-1.5`. We prefer the use of the escaped dot now
but still want to make sure suggestions for the legacy key format still
works as expected when surrounded by numbers.

This is the same as #16433 but for `@utility` since we apparently missed
this when emitting suggestions for it
2025-04-22 10:02:28 -04:00
Jordan Pittman
650558df5e Update changelog 2025-04-22 09:31:25 -04:00
Jordan Pittman
ee0d7525d8
Hide default shadow suggestions when missing theme keys (#17743)
Right now if you have a completely empty theme we'll still suggest
`shadow`, `inset-shadow`, and `text-shadow` as utilities even tho they
won't exist. This fixes this by checking for the theme key when
computing the suggestions.
2025-04-22 09:30:19 -04:00
depfu[bot]
fc4afc2538
Update @types/react 19.0.12 → 19.1.2 (minor) (#17738) 2025-04-22 10:54:44 +02:00
Adam Wathan
8feb6a758a
Ignore .geojson files by default (#17700)
Resolves https://github.com/tailwindlabs/tailwindcss/issues/17699

GeoJSON files are giant JSON files for geographic data structures and
will never contain Tailwind classes, but because they are often huge
they will slow down the build a lot if scanned.

---------

Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2025-04-18 10:46:33 +02:00
depfu[bot]
adcf1dede2
Update turbo 2.4.4 → 2.5.0 (minor) (#17678) 2025-04-15 12:03:19 +02:00
Philipp Spiess
25539e3533 Fix release script 2025-04-14 19:06:18 +02:00
Philipp Spiess
aa836d3442
Prepare v4.1.4 release (#17669)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-04-14 17:32:30 +02:00
depfu[bot]
cf2591c44b
Update eslint 9.22.0 → 9.24.0 (minor) (#17656)
Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-14 11:13:15 +02:00
Robin Malfait
bbd916aaa0
Ignore binary extensions, except in folder names (#17595)
We generate a glob to ignore binary extensions that looks something like
this:
```
*.{mp4,pages,exe,…}
```

However, if you have a folder that happens to end in `.pages` for
example, then this folder will be ignored in its entirety.

To solve this, we added a new flag to the `Gitignore` struct so we can
register a bunch of ignore rules that _only_ apply to paths and not
folders.

Fixes: #17569

## Test plan

- Added a unit test

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-12 13:30:17 +02:00
Philipp Spiess
d801d8dc54
Fix publish step on CI (#17654)
To publish the newly adde WASM builds, we rely on the
`node-linker=hoisted` `.npmrc` flag that isn't read when `pnpm
--recursive` is used. To work around it, this PR excludes the wasm
package from the `--recursive` part and manually published it
afterwards.


## Test Plan

Ensured this does not error now when trying a `--dry run`.

<img width="1273" alt="Screenshot 2025-04-11 at 17 43 38"
src="https://github.com/user-attachments/assets/68a28552-0125-4da1-92ff-74e58368abe4"
/>
2025-04-11 18:14:48 +02:00
Philipp Spiess
83ce4c0a30
Add experimental @tailwindcss/oxide-wasm32-wasi (#17558)
Closes #17448
Closes #13133

This PR adds an a new Oxide target for `wasm32-wasip1-threads`:
`@tailwindcss/oxide-wasm32-wasi`. The goal of this is to enable more
environments to run Oxide, including (but not limited to) StackBlitz.

We're making use of `napi-rs`'s upcoming v3 features to simplify the
setup here, meaning `napi-rs` will configure the WASM target and create
an npm package that works across Node and browser environments.

## MacOS AArch64 issues

While setting up an integration test for the new WASM target, I ran into
an issue where FS reads where not terminating on macOS. After some
research I found this to be a limitation of the Node.js container
interface right now, see: https://github.com/nodejs/node/issues/47193

### Windows issues

We also found that the Node.js wasi container does not properly support
Windows: https://github.com/nodejs/uvwasi/issues/11

For now we, it's probably best for MacOS AArch64 users and Windows users
to use the native modules instead.

## Test plan

The `@tailwindcss/oxide-wasm32-wasi` npm package can be built locally
via `pnpm build` and then run with the Oxide API. A usage example can be
taken from the newly added integration test.

Furthermore this was tested to work as a polyfill on StackBlitz:
https://stackblitz.com/edit/vitejs-vite-uks3gt5p

[ci-all]

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-11 17:19:55 +02:00
Philipp Spiess
6e1f53348d
Workaround Chrome rendering bug for skew-* utilities (#17627)
Closes #17621

Chrome as a pretty ugly rendering glitch when using a `skew-*` utility
in Tailwind 4: https://play.tailwindcss.com/HuiZtbrHOc

The was not an issue in v3 since transforms were set up in a different
way. Without the `var(…)` syntax, the difference boils down to this:

```css
.skew-v3 {
  transform: rotate(0) skewX(-20deg);
}

.skew-v4 {
  transform: rotateX(0) rotateY(0) rotateZ(0) skewX(-20deg);
}
```

It appears that using any of the single-dimension rotate functions will
cause the Chrome rendering to glitch.

After doing some digging, we found [that initially these `@property`s
were defined as type `<transform-function>` and later changed to
`*`](https://github.com/tailwindlabs/tailwindcss/issues/15144). With a
type of `*`, it makes sense that the initial value of these variables
can default to `initial` without any compromises, allowing us to default
to something like this now:

```css
.skew-new {
  transform: skewX(-20deg);
}
```

Tested this change in the latest version of Chrome (135) and it does
make the rendering glitch in the initial issue disappear. By using the
`var(--tw-rotate-x,)` syntax we also ensure this works on older versions
of Safari (tested on Safari 15.5 and 16.4).

Note, however, that there are still glitches happening when you combine
rotate and skew in the latest version of Chrome or when you transition
the `skew(…)` variable. This also happens in plain CSS with no variables
though, so there isn't something we can do about this:
https://play.tailwindcss.com/g3FXPEJHpn

## Test plan

- Tested on latest Chrome, Firefox, and Safari as well as Safari 15.5
and 16.4.

<img width="564" alt="Screenshot 2025-04-09 at 18 01 51"
src="https://github.com/user-attachments/assets/2e0b1c96-7c4d-41a8-b3d0-0f6134a3e635"
/>
2025-04-11 16:38:46 +02:00
Scott Bedard
3bea760ff2
Add test coverage for property-specific colors (#17436)
There are several property-specific color variables available to
maintain compatibility with v3. [My team and I would like to use
them](https://github.com/tailwindlabs/tailwindcss/discussions/17400),
but would feel more comfortable if they were formally supported by v4.
This PR adds test coverage for those features.

@RobinMalfait has confirmed that these features are not intended to be
removed, [see conversation here
&rarr;](1355213322)

@crswll Has opened a PR here for the related documentation
- https://github.com/tailwindlabs/tailwindcss.com/pull/2178
2025-04-11 13:40:10 +02:00
Justin Wong
6d8dd82c40
Fix shadow-inherit, inset-shadow-inherit, drop-shadow-inherit, and text-shadow-inherit (#17647)
Fixes #17643.

This PR completely removes the `color-mix()` function for
`shadow-inherit`. This does mean intensity and alpha channel support has
been removed when using `shadow-inherit`[^1].

With intensity modifiers in #17398, all colors are wrapped in
`color-mix()`. However, it seems `inherit` does not work as a value in
`color-mix()` in Firefox or Chrome (don't have a means to test Safari).

[^1]: While writing this, I noticed other color utilities allow alpha
channel modifier syntax for `inherit` - do we want to look at removing
those too?

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-11 13:36:18 +02:00
Justin Wong
c0af1e2129
Fix fontSize array upgrade (#17630)
Fixes #17622.

Adds a specific handling case in `themeableValues()` in
`packages/tailwindcss/src/compat/apply-config-to-theme.ts`. It seems
like this has unique handling in v3 for an array value, whereby the
second value is treated as a `line-height`.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-11 13:23:54 +02:00
Justin Wong
3ab7f12563
Fix container names with hyphens (#17628)
Fixes #17614.

Candidate parsing for variants only account for the root `@` if there no
hyphens. It seems like the current logic assumes if it *does* have a
hyphen, then it would be one of `@min` or `@max`. However, with:

```css
@theme {
  --container-foo-bar: 1440px;
}
```
Then `@foo-bar` should be valid. However, we only check for `@foo-bar`
and `@foo` as roots, but never `@`. This PR adds a check for `@` at the
very end after iterating through root permutations.

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2025-04-11 12:28:25 +02:00
depfu[bot]
cdecb55702
Update @types/react-dom 19.0.4 → 19.1.1 (minor) (#17619) 2025-04-09 10:26:51 +02:00
Robin Malfait
f66d287436
Fix brace expansion with range going down (#17591) 2025-04-07 15:48:19 +02:00
depfu[bot]
76e18e679c
Update all of react 19.0.0 → 19.1.0 (minor) (#17564)
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ react (19.0.0 → 19.1.0) ·
[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.1.0">19.1.0</a></h4>

<blockquote><h3 dir="auto">Owner Stack</h3>
<p dir="auto">An Owner Stack is a string representing the components
that are directly responsible for rendering a particular component. You
can log Owner Stacks when debugging or use Owner Stacks to enhance error
overlays or other development tools. Owner Stacks are only available in
development builds. Component Stacks in production are unchanged.</p>
<ul dir="auto">
<li>An Owner Stack is a development-only stack trace that helps identify
which components are responsible for rendering a particular component.
An Owner Stack is distinct from a Component Stacks, which shows the
hierarchy of components leading to an error.</li>
<li>The <a
href="https://react.dev/reference/react/captureOwnerStack">captureOwnerStack
API</a> is only available in development mode and returns a Owner Stack,
if available. The API can be used to enhance error overlays or log
component relationships when debugging. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/29923">#29923</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32353">#32353</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/30306">#30306</a>,<br>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32538">#32538</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32529">#32529</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32538">#32538</a>
</li>
</ul>
<h3 dir="auto">React</h3>
<ul dir="auto">
<li>Enhanced support for Suspense boundaries to be used anywhere,
including the client, server, and during hydration. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32069">#32069</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32163">#32163</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32224">#32224</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32252">#32252</a>
</li>
<li>Reduced unnecessary client rendering through improved hydration
scheduling <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31751">#31751</a>
</li>
<li>Increased priority of client rendered Suspense boundaries <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31776">#31776</a>
</li>
<li>Fixed frozen fallback states by rendering unfinished Suspense
boundaries on the client. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31620">#31620</a>
</li>
<li>Reduced garbage collection pressure by improving Suspense boundary
retries. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31667">#31667</a>
</li>
<li>Fixed erroneous “Waiting for Paint” log when the passive effect
phase was not delayed <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31526">#31526</a>
</li>
<li>Fixed a regression causing key warnings for flattened positional
children in development mode. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32117">#32117</a>
</li>
<li>Updated <code class="notranslate">useId</code> to use valid CSS
selectors, changing format from <code class="notranslate">:r123:</code>
to <code class="notranslate">«r123»</code>. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32001">#32001</a>
</li>
<li>Added a dev-only warning for null/undefined created in useEffect,
useInsertionEffect, and useLayoutEffect. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32355">#32355</a>
</li>
<li>Fixed a bug where dev-only methods were exported in production
builds. React.act is no longer available in production builds. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32200">#32200</a>
</li>
<li>Improved consistency across prod and dev to improve compatibility
with Google Closure Complier and bindings <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31808">#31808</a>
</li>
<li>Improve passive effect scheduling for consistent task yielding. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31785">#31785</a>
</li>
<li>Fixed asserts in React Native when
passChildrenWhenCloningPersistedNodes is enabled for OffscreenComponent
rendering. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32528">#32528</a>
</li>
<li>Fixed component name resolution for Portal <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32640">#32640</a>
</li>
<li>Added support for beforetoggle and toggle events on the dialog
element. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32479">#32479</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32479">#32479</a>
</li>
</ul>
<h3 dir="auto">React DOM</h3>
<ul dir="auto">
<li>Fixed double warning when the <code class="notranslate">href</code>
attribute is an empty string <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31783">#31783</a>
</li>
<li>Fixed an edge case where <code
class="notranslate">getHoistableRoot()</code> didn’t work properly when
the container was a Document <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32321">#32321</a>
</li>
<li>Removed support for using HTML comments (e.g. <code
class="notranslate">&lt;!-- --&gt;</code>) as a DOM container. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32250">#32250</a>
</li>
<li>Added support for <code class="notranslate">&lt;script&gt;</code>
and <code class="notranslate">&lt;template&gt;</code> tags to be nested
within <code class="notranslate">&lt;select&gt;</code> tags. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31837">#31837</a>
</li>
<li>Fixed responsive images to be preloaded as HTML instead of headers
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32445">#32445</a>
</li>
</ul>
<h3 dir="auto">use-sync-external-store</h3>
<ul dir="auto">
<li>Added <code class="notranslate">exports</code> field to <code
class="notranslate">package.json</code> for <code
class="notranslate">use-sync-external-store</code> to support various
entrypoints. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/25231">#25231</a>
</li>
</ul>
<h3 dir="auto">React Server Components</h3>
<ul dir="auto">
<li>Added <code class="notranslate">unstable_prerender</code>, a new
experimental API for prerendering React Server Components on the server
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31724">#31724</a>
</li>
<li>Fixed an issue where streams would hang when receiving new chunks
after a global error <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31840">#31840</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31851">#31851</a>
</li>
<li>Fixed an issue where pending chunks were counted twice. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31833">#31833</a>
</li>
<li>Added support for streaming in edge environments <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31852">#31852</a>
</li>
<li>Added support for sending custom error names from a server so that
they are available in the client for console replaying. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32116">#32116</a>
</li>
<li>Updated the server component wire format to remove IDs for hints and
console.log because they have no return value <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31671">#31671</a>
</li>
<li>Exposed <code class="notranslate">registerServerReference</code> in
client builds to handle server references in different environments. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32534">#32534</a>
</li>
<li>Added react-server-dom-parcel package which integrates Server
Components with the <a href="https://parceljs.org/">Parcel bundler</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31725">#31725</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32132">#32132</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31799">#31799</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32294">#32294</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31741">#31741</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="7aa5dda3b3...4a9df08157">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</details>




#### ✳️ react-dom (19.0.0 → 19.1.0) ·
[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.1.0">19.1.0</a></h4>

<blockquote><h3 dir="auto">Owner Stack</h3>
<p dir="auto">An Owner Stack is a string representing the components
that are directly responsible for rendering a particular component. You
can log Owner Stacks when debugging or use Owner Stacks to enhance error
overlays or other development tools. Owner Stacks are only available in
development builds. Component Stacks in production are unchanged.</p>
<ul dir="auto">
<li>An Owner Stack is a development-only stack trace that helps identify
which components are responsible for rendering a particular component.
An Owner Stack is distinct from a Component Stacks, which shows the
hierarchy of components leading to an error.</li>
<li>The <a
href="https://react.dev/reference/react/captureOwnerStack">captureOwnerStack
API</a> is only available in development mode and returns a Owner Stack,
if available. The API can be used to enhance error overlays or log
component relationships when debugging. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/29923">#29923</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32353">#32353</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/30306">#30306</a>,<br>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32538">#32538</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32529">#32529</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32538">#32538</a>
</li>
</ul>
<h3 dir="auto">React</h3>
<ul dir="auto">
<li>Enhanced support for Suspense boundaries to be used anywhere,
including the client, server, and during hydration. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32069">#32069</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32163">#32163</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32224">#32224</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32252">#32252</a>
</li>
<li>Reduced unnecessary client rendering through improved hydration
scheduling <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31751">#31751</a>
</li>
<li>Increased priority of client rendered Suspense boundaries <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31776">#31776</a>
</li>
<li>Fixed frozen fallback states by rendering unfinished Suspense
boundaries on the client. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31620">#31620</a>
</li>
<li>Reduced garbage collection pressure by improving Suspense boundary
retries. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31667">#31667</a>
</li>
<li>Fixed erroneous “Waiting for Paint” log when the passive effect
phase was not delayed <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31526">#31526</a>
</li>
<li>Fixed a regression causing key warnings for flattened positional
children in development mode. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32117">#32117</a>
</li>
<li>Updated <code class="notranslate">useId</code> to use valid CSS
selectors, changing format from <code class="notranslate">:r123:</code>
to <code class="notranslate">«r123»</code>. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32001">#32001</a>
</li>
<li>Added a dev-only warning for null/undefined created in useEffect,
useInsertionEffect, and useLayoutEffect. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32355">#32355</a>
</li>
<li>Fixed a bug where dev-only methods were exported in production
builds. React.act is no longer available in production builds. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32200">#32200</a>
</li>
<li>Improved consistency across prod and dev to improve compatibility
with Google Closure Complier and bindings <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31808">#31808</a>
</li>
<li>Improve passive effect scheduling for consistent task yielding. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31785">#31785</a>
</li>
<li>Fixed asserts in React Native when
passChildrenWhenCloningPersistedNodes is enabled for OffscreenComponent
rendering. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32528">#32528</a>
</li>
<li>Fixed component name resolution for Portal <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32640">#32640</a>
</li>
<li>Added support for beforetoggle and toggle events on the dialog
element. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32479">#32479</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32479">#32479</a>
</li>
</ul>
<h3 dir="auto">React DOM</h3>
<ul dir="auto">
<li>Fixed double warning when the <code class="notranslate">href</code>
attribute is an empty string <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31783">#31783</a>
</li>
<li>Fixed an edge case where <code
class="notranslate">getHoistableRoot()</code> didn’t work properly when
the container was a Document <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32321">#32321</a>
</li>
<li>Removed support for using HTML comments (e.g. <code
class="notranslate">&lt;!-- --&gt;</code>) as a DOM container. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32250">#32250</a>
</li>
<li>Added support for <code class="notranslate">&lt;script&gt;</code>
and <code class="notranslate">&lt;template&gt;</code> tags to be nested
within <code class="notranslate">&lt;select&gt;</code> tags. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31837">#31837</a>
</li>
<li>Fixed responsive images to be preloaded as HTML instead of headers
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32445">#32445</a>
</li>
</ul>
<h3 dir="auto">use-sync-external-store</h3>
<ul dir="auto">
<li>Added <code class="notranslate">exports</code> field to <code
class="notranslate">package.json</code> for <code
class="notranslate">use-sync-external-store</code> to support various
entrypoints. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/25231">#25231</a>
</li>
</ul>
<h3 dir="auto">React Server Components</h3>
<ul dir="auto">
<li>Added <code class="notranslate">unstable_prerender</code>, a new
experimental API for prerendering React Server Components on the server
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31724">#31724</a>
</li>
<li>Fixed an issue where streams would hang when receiving new chunks
after a global error <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31840">#31840</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31851">#31851</a>
</li>
<li>Fixed an issue where pending chunks were counted twice. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31833">#31833</a>
</li>
<li>Added support for streaming in edge environments <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31852">#31852</a>
</li>
<li>Added support for sending custom error names from a server so that
they are available in the client for console replaying. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32116">#32116</a>
</li>
<li>Updated the server component wire format to remove IDs for hints and
console.log because they have no return value <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31671">#31671</a>
</li>
<li>Exposed <code class="notranslate">registerServerReference</code> in
client builds to handle server references in different environments. <a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32534">#32534</a>
</li>
<li>Added react-server-dom-parcel package which integrates Server
Components with the <a href="https://parceljs.org/">Parcel bundler</a>
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31725">#31725</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32132">#32132</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31799">#31799</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/32294">#32294</a>,
<a
href="https://bounce.depfu.com/github.com/facebook/react/pull/31741">#31741</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="7aa5dda3b3...4a9df08157">See
the full diff on Github</a>. The new version differs by more commits
than we can show here.</p>
</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>
2025-04-07 13:05:56 +00:00
Philipp Spiess
3e9cf87adf
Make polyfill work when the theme variable resolves to another var (#17562)
Discovered while triaging #17556

This PR improves the `color-mix(...)` polyfill to ensure it works when a
theme key links to another theme key via a `var(...)` call.

Imagine this setup:

```css
 @theme {
  --color-red: var(--color-red-500);
  --color-red-500: oklch(63.7% 0.237 25.331);
}
@source inline("text-red/50");
````

Since `--color-red` will link to `--color-red-500` _which is also a
known theme variable_, we can inline the value of `--color-red-500` into
the fallback now:

```css
.text-red\\/50 {
  color: var(--color-red);
}
@supports (color: color-mix(in lab, red, red)) {
  .text-red\\/50 {
    color: color-mix(in oklab, var(--color-red) 50%, transparent);
  }
}
```

This will allow for slightly less confusing behavior.

The code added also handles recursive definitions where a color is
linking to another color that is again linking to the first one (by
adding a `Set` to keep track of already seen variable names).

## Test plan

- Added unit test
2025-04-07 11:42:02 +02:00
Philipp Spiess
811e97d61a
Fix polyfill in combination with unused CSS variable removal (#17555)
This PR fixes an issue we noticed while investigating #17553, where the
unused CSS variable removal didn't work properly when the theme variable
it tried to remove was modified by a polyfill rule.

The way the bookkeeping for the unused CSS variable worked was that it
tired to find the declaration inside it's parent after the traversal.
However, the `color-mix(…)` polyfill has since then made changes to the
declaration so it can't find it's position correctly anymore and will
thus instead delete the last declaration of the node (this caused
unrelated CSS variables to be eliminated while the ones with
`color-mix(…)` were unexpectedly kept).

To fix this, we decided to apply the polyfills after any eventual
deletions. This also ensures that no `@supports` query for the variables
are created and simplifies the code a bit since all polyfills are now
colocated.

## Test plan

- Added a unit test for the example we discovered in #17553
- Luckily the conditions of this seemed rare enough so that it doesn't
cause any other of our tests to update.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2025-04-07 09:18:47 +00:00
385 changed files with 67865 additions and 23431 deletions

View file

@ -1,12 +1,48 @@
# Contributing # Contributing
## Requirements
Before getting started, ensure your system has access to the following tools:
- [Node.js](https://nodejs.org/)
- [Rustup](https://rustup.rs/)
- [pnpm](https://pnpm.io/)
## Getting started
```sh
# Install dependencies
pnpm install
# Install Rust toolchain and WASM targets
rustup default stable
rustup target add wasm32-wasip1-threads
# Build the project
pnpm build
```
## Development workflow
During development, you can run tests in watch mode:
```sh
pnpm tdd
```
The `playgrounds` directory contains example projects you can use to test your changes. To start the Vite playground, use:
```sh
pnpm build && pnpm vite
```
## Bug fixes ## Bug fixes
If you've found a bug in Tailwind that you'd like to fix, [submit a pull request](https://github.com/tailwindlabs/tailwindcss/pulls) with your changes. Include a helpful description of the problem and how your changes address it, and provide tests so we can verify the fix works as expected. If you've found a bug in Tailwind that you'd like to fix, [submit a pull request](https://github.com/tailwindlabs/tailwindcss/pulls) with your changes. Include a helpful description of the problem and how your changes address it, and provide tests so we can verify the fix works as expected.
## New features ## New features
If there's a new feature you'd like to see added to Tailwind, [share your idea with us](https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas) in our discussion forum to get it on our radar as something to consider for a future release. If there's a new feature you'd like to see added to Tailwind, [share your idea with us](https://github.com/tailwindlabs/tailwindcss/discussions/new?category=ideas) in our discussion forum to get it on our radar as something to consider for a future release before starting work on it.
**Please note that we don't often accept pull requests for new features.** Adding a new feature to Tailwind requires us to think through the entire problem ourselves to make sure we agree with the proposed API, which means the feature needs to be high on our own priority list for us to be able to give it the attention it needs. **Please note that we don't often accept pull requests for new features.** Adding a new feature to Tailwind requires us to think through the entire problem ourselves to make sure we agree with the proposed API, which means the feature needs to be high on our own priority list for us to be able to give it the attention it needs.
@ -14,7 +50,7 @@ If you open a pull request for a new feature, we're likely to close it not becau
## Coding standards ## Coding standards
Our code formatting rules are defined in the `"prettier"` section of [package.json](https://github.com/tailwindcss/tailwindcss/blob/main/package.json). You can check your code against these standards by running: Our code formatting rules are defined in the `"prettier"` section of [package.json](https://github.com/tailwindlabs/tailwindcss/blob/main/package.json). You can check your code against these standards by running:
```sh ```sh
pnpm run lint pnpm run lint
@ -28,10 +64,40 @@ pnpm run format
## Running tests ## Running tests
You can run the test suite using the following commands: You can run the TypeScript and Rust test suites using the following command:
```sh ```sh
pnpm build && pnpm test pnpm test
``` ```
Please ensure that the tests are passing when submitting a pull request. If you're adding new features to Tailwind, please include tests. To run the integration tests, use:
```sh
pnpm build && pnpm test:integrations
```
Additionally, some features require testing in browsers (i.e. to ensure CSS variable resolution works as expected). These can be run via:
```sh
pnpm build && pnpm test:ui
```
Please ensure that all tests are passing when submitting a pull request. If you're adding new features to Tailwind CSS, always include tests.
After a successful build, you can also use the npm package tarballs created inside the `dist/` folder to install your build in other local projects.
## Pull request process
When submitting a pull request:
- Ensure the pull request title and description explain the changes you made and why you made them.
- Include a test plan section that outlines how you tested your contributions. We do not accept contributions without tests.
- Ensure all tests pass. You can add the tag `[ci-all]` in your pull request description to run the test suites across all platforms.
When a pull request is created, Tailwind CSS maintainers will be notified automatically.
## Communication
- **GitHub discussions**: For feature ideas and general questions
- **GitHub issues**: For bug reports
- **GitHub pull requests**: For code contributions

1
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
custom: ['https://tailwindcss.com/sponsor']

View file

@ -4,8 +4,26 @@
**Please ask first before starting work on any significant new features.** **Please ask first before starting work on any significant new features.**
It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create an issue to first discuss any significant new features. This includes things like adding new utilities, creating new at-rules, or adding new component examples to the documentation. It's never a fun experience to have your pull request declined after investing a lot of time and effort into a new feature. To avoid this from happening, we request that contributors create a discussion to first discuss any significant new features.
https://github.com/tailwindcss/tailwindcss/blob/master/.github/CONTRIBUTING.md For more info, check out the contributing guide:
https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md
-->
## Summary
<!--
Provide a summary of the issue and the changes you're making. How does your change solve the problem?
-->
## Test plan
<!--
Explain how you tested your changes. Include the exact commands that you used to verify the change works and include screenshots/screen recordings of the update behavior in the browser if applicable.
--> -->

View file

@ -8,12 +8,18 @@ on:
permissions: permissions:
contents: read contents: read
env:
NODE_VERSION: 24
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
tests: tests:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
node-version: [20]
runner: runner:
- name: Windows - name: Windows
os: windows-latest os: windows-latest
@ -21,12 +27,14 @@ jobs:
- name: Linux - name: Linux
os: namespace-profile-default os: namespace-profile-default
# Playwright 1.62+ dropped WebKit support for macOS 14, and hangs
# instead of failing when launching WebKit on a macos-14 runner.
- name: macOS - name: macOS
os: macos-14 os: macos-15
# Exclude windows and macos from being built on feature branches # Exclude windows and macos from being built on feature branches
run-all: run-all:
- ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.body, '[ci-all]') }} - ${{ github.ref == 'refs/heads/main' || contains(github.event.pull_request.body, '[ci-all]') || github.event.pull_request.user.login == 'depfu[bot]' }}
exclude: exclude:
- run-all: false - run-all: false
runner: runner:
@ -41,21 +49,21 @@ jobs:
name: ${{ matrix.runner.name }} name: ${{ matrix.runner.name }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: pnpm/action-setup@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} persist-credentials: false
cache: 'pnpm' - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: ${{ env.NODE_VERSION }}
# Cargo already skips downloading dependencies if they already exist # Cargo already skips downloading dependencies if they already exist
- name: Cache cargo - name: Cache cargo
uses: actions/cache@v4 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with: with:
path: | path: |
~/.cargo/bin/
~/.cargo/registry/index/ ~/.cargo/registry/index/
~/.cargo/registry/cache/ ~/.cargo/registry/cache/
~/.cargo/git/db/ ~/.cargo/git/db/
@ -64,17 +72,25 @@ jobs:
# Cache the `oxide` Rust build # Cache the `oxide` Rust build
- name: Cache oxide build - name: Cache oxide build
uses: actions/cache@v4 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with: with:
path: | path: |
./target/
./crates/node/*.node ./crates/node/*.node
./crates/node/index.js ./crates/node/*.wasm
./crates/node/index.d.ts ./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }} key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install --frozen-lockfile
- name: Build - name: Build
run: pnpm run build run: pnpm run build
@ -96,9 +112,13 @@ jobs:
- name: Run Playwright tests - name: Run Playwright tests
run: npm run test:ui run: npm run test:ui
notify:
if: ${{ always() && github.ref == 'refs/heads/main' && needs.tests.result == 'failure' }}
needs: tests
runs-on: ubuntu-latest
steps:
- name: Notify Discord - name: Notify Discord
if: failure() && github.ref == 'refs/heads/main' uses: discord-actions/message@5c7149c81a83146e5d01f142be1bf87a61831c4d # v2
uses: discord-actions/message@v2
with: with:
webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }} webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }}
message: 'The [most recent build](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `main` branch has failed.' message: 'The [most recent ${{ github.workflow }} workflow](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `main` branch has failed.'

View file

@ -8,13 +8,18 @@ on:
permissions: permissions:
contents: read contents: read
env:
NODE_VERSION: 24
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
tests: tests:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
node-version: [20]
runner: runner:
- name: Windows - name: Windows
os: windows-latest os: windows-latest
@ -30,7 +35,7 @@ jobs:
- vite - vite
- cli - cli
- postcss - postcss
- workers - oxide
- webpack - webpack
# Exclude windows and macos from being built on feature branches # Exclude windows and macos from being built on feature branches
@ -50,21 +55,25 @@ jobs:
name: ${{ matrix.runner.name }} / ${{ matrix.integration }} name: ${{ matrix.runner.name }} / ${{ matrix.integration }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: pnpm/action-setup@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with: with:
node-version: ${{ matrix.node-version }} persist-credentials: false
cache: 'pnpm' - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: ${{ env.NODE_VERSION }}
# Cargo already skips downloading dependencies if they already exist # Cargo already skips downloading dependencies if they already exist
- name: Cache cargo - name: Cache cargo
uses: actions/cache@v4 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with: with:
path: | path: |
~/.cargo/bin/
~/.cargo/registry/index/ ~/.cargo/registry/index/
~/.cargo/registry/cache/ ~/.cargo/registry/cache/
~/.cargo/git/db/ ~/.cargo/git/db/
@ -73,15 +82,23 @@ jobs:
# Cache the `oxide` Rust build # Cache the `oxide` Rust build
- name: Cache oxide build - name: Cache oxide build
uses: actions/cache@v4 uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with: with:
path: | path: |
./target/
./crates/node/*.node ./crates/node/*.node
./crates/node/index.js ./crates/node/*.wasm
./crates/node/index.d.ts ./crates/node/index.d.ts
./crates/node/index.js
./crates/node/browser.js
./crates/node/tailwindcss-oxide.wasi-browser.js
./crates/node/tailwindcss-oxide.wasi.cjs
./crates/node/wasi-worker-browser.mjs
./crates/node/wasi-worker.mjs
key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }} key: ${{ runner.os }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies - name: Install dependencies
run: pnpm install run: pnpm install
@ -96,9 +113,13 @@ jobs:
env: env:
GITHUB_WORKSPACE: ${{ github.workspace }} GITHUB_WORKSPACE: ${{ github.workspace }}
notify:
if: ${{ always() && github.ref == 'refs/heads/main' && needs.tests.result == 'failure' }}
needs: tests
runs-on: ubuntu-latest
steps:
- name: Notify Discord - name: Notify Discord
if: failure() && github.ref == 'refs/heads/main' uses: discord-actions/message@5c7149c81a83146e5d01f142be1bf87a61831c4d # v2
uses: discord-actions/message@v2
with: with:
webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }} webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }}
message: 'The [most recent build](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `main` branch has failed.' message: 'The [most recent ${{ github.workflow }} workflow](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) on the `main` branch has failed.'

View file

@ -2,18 +2,29 @@ name: Prepare Release
on: on:
workflow_dispatch: workflow_dispatch:
inputs:
dry_run:
description: Skip creating the draft GitHub release
required: false
default: true
type: boolean
push: push:
tags: tags:
- 'v*' - 'v*'
env: env:
APP_NAME: tailwindcss-oxide APP_NAME: tailwindcss-oxide
NODE_VERSION: 20 NODE_VERSION: 24
PNPM_VERSION: '11.9.0'
OXIDE_LOCATION: ./crates/node OXIDE_LOCATION: ./crates/node
permissions: permissions:
contents: read contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
strategy: strategy:
@ -43,96 +54,97 @@ jobs:
- os: ubuntu-latest - os: ubuntu-latest
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
strip: strip strip: strip
container: build-flags: --use-napi-cross
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
- os: ubuntu-latest - os: ubuntu-latest
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
strip: llvm-strip strip: aarch64-linux-gnu-strip
container: build-flags: --use-napi-cross
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
- os: ubuntu-latest - os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf target: armv7-unknown-linux-gnueabihf
strip: llvm-strip strip: arm-linux-gnueabihf-strip
container: build-flags: --use-napi-cross
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig
- os: ubuntu-latest - os: ubuntu-latest
target: aarch64-unknown-linux-musl target: aarch64-unknown-linux-musl
strip: aarch64-linux-musl-strip strip-zig: true
download: true build-flags: -x
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
- os: ubuntu-latest - os: ubuntu-latest
target: x86_64-unknown-linux-musl target: x86_64-unknown-linux-musl
strip: strip strip: strip
download: true build-flags: -x
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
name: Build ${{ matrix.target }} (OXIDE) name: Build ${{ matrix.target }} (oxide)
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: pnpm/action-setup@v4 with:
persist-credentials: false
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
version: ${{ env.PNPM_VERSION }}
- name: Use Node.js ${{ env.NODE_VERSION }} - name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm' package-manager-cache: false
# Cargo already skips downloading dependencies if they already exist - name: Install gcc-arm-linux-gnueabihf
- name: Cache cargo if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }}
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Cache the `oxide` Rust build
- name: Cache oxide build
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/index.d.ts
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install Node.JS
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Rust (Stable)
if: ${{ matrix.download }}
run: | run: |
rustup default stable sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
- name: Install binutils-aarch64-linux-gnu
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
run: |
sudo apt-get update
sudo apt-get install binutils-aarch64-linux-gnu -y
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
if: ${{ contains(matrix.target, 'musl') }}
with:
version: 0.14.1
use-cache: false
- name: Install cargo-zigbuild
uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
if: ${{ contains(matrix.target, 'musl') }}
env:
GITHUB_TOKEN: ${{ github.token }}
with:
tool: cargo-zigbuild
- name: Setup rust target - name: Setup rust target
run: rustup target add ${{ matrix.target }} run: rustup target add ${{ matrix.target }}
- name: Install dependencies - name: Install dependencies
run: pnpm install --ignore-scripts --filter=!./playgrounds/* run: pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/*
- name: Build release - name: Build release
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform --target=${{ matrix.target }} ${{ matrix.build-flags }}
env: env:
RUST_TARGET: ${{ matrix.target }} RUST_TARGET: ${{ matrix.target }}
JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }} JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }}
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034 - name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
if: ${{ matrix.strip }} if: ${{ matrix.strip || matrix.strip-zig }}
run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node env:
STRIP_COMMAND: ${{ matrix.strip }}
STRIP_ZIG: ${{ matrix.strip-zig }}
run: |
if [ "$STRIP_ZIG" = "true" ]; then
for file in ${{ env.OXIDE_LOCATION }}/*.node; do
zig objcopy --strip-all "$file" "$file.stripped"
mv "$file.stripped" "$file"
done
exit 0
fi
eval "$STRIP_COMMAND ${{ env.OXIDE_LOCATION }}/*.node"
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with: with:
name: bindings-${{ matrix.target }} name: bindings-${{ matrix.target }}
path: ${{ env.OXIDE_LOCATION }}/*.node path: ${{ env.OXIDE_LOCATION }}/*.node
@ -142,9 +154,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Build FreeBSD - name: Build FreeBSD
uses: cross-platform-actions/action@v0.25.0 uses: cross-platform-actions/action@cdc9ee69ef84a5f2e59c9058335d9c57bcb4ac86 # v0.25.0
env: env:
DEBUG: napi:* DEBUG: napi:*
RUSTUP_HOME: /usr/local/rustup RUSTUP_HOME: /usr/local/rustup
@ -160,23 +174,23 @@ jobs:
shell: bash shell: bash
run: | run: |
sudo pkg install -y -f curl node libnghttp2 npm sudo pkg install -y -f curl node libnghttp2 npm
sudo npm install -g pnpm@9.6.0 --unsafe-perm=true sudo npm install -g pnpm@${{ env.PNPM_VERSION }} --unsafe-perm=true
curl -sSf https://static.rust-lang.org/rustup/archive/1.27.1/x86_64-unknown-freebsd/rustup-init --output rustup-init curl -sSf https://static.rust-lang.org/rustup/archive/1.27.1/x86_64-unknown-freebsd/rustup-init --output rustup-init
chmod +x rustup-init chmod +x rustup-init
./rustup-init -y --profile minimal ./rustup-init -y --profile minimal
source "$HOME/.cargo/env" source "$HOME/.cargo/env"
pnpm install --ignore-scripts --filter=!./playgrounds/* || true pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/* || true
echo "~~~~ rustc --version ~~~~" echo "~~~~ rustc --version ~~~~"
rustc --version rustc --version
echo "~~~~ node -v ~~~~" echo "~~~~ node -v ~~~~"
node -v node -v
echo "~~~~ pnpm --version ~~~~" echo "~~~~ pnpm --version ~~~~"
pnpm --version pnpm --version
pnpm run --filter ${{ env.OXIDE_LOCATION }} build pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform
strip -x ${{ env.OXIDE_LOCATION }}/*.node strip -x ${{ env.OXIDE_LOCATION }}/*.node
ls -la ${{ env.OXIDE_LOCATION }} ls -la ${{ env.OXIDE_LOCATION }}
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with: with:
name: bindings-x86_64-unknown-freebsd name: bindings-x86_64-unknown-freebsd
path: ${{ env.OXIDE_LOCATION }}/*.node path: ${{ env.OXIDE_LOCATION }}/*.node
@ -187,18 +201,17 @@ jobs:
name: Build and release Tailwind CSS name: Build and release Tailwind CSS
permissions: permissions:
contents: write # for softprops/action-gh-release to create GitHub release contents: write # Required for creating releases
# https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions
id-token: write
needs: needs:
- build - build
- build-freebsd - build-freebsd
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
fetch-depth: 20 fetch-depth: 20
persist-credentials: false
- run: git fetch --tags -f - run: git fetch --tags -f
@ -207,43 +220,25 @@ jobs:
run: | run: |
echo "TAG_NAME=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV echo "TAG_NAME=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
version: ${{ env.PNPM_VERSION }}
- name: Use Node.js ${{ env.NODE_VERSION }} - name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
package-manager-cache: false
# Cargo already skips downloading dependencies if they already exist - name: Setup WASM target
- name: Cache cargo run: rustup target add wasm32-wasip1-threads
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Cache the `oxide` Rust build
- name: Cache oxide build
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/index.d.ts
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install dependencies - name: Install dependencies
run: pnpm --filter=!./playgrounds/* install run: pnpm --filter=!./playgrounds/* install --frozen-lockfile
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with: with:
path: ${{ env.OXIDE_LOCATION }} path: ${{ env.OXIDE_LOCATION }}
@ -281,25 +276,32 @@ jobs:
echo "$RELEASE_NOTES" >> $GITHUB_ENV echo "$RELEASE_NOTES" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV echo "EOF" >> $GITHUB_ENV
- name: Upload Standalone Artifacts - name: Upload standalone artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with: with:
name: tailwindcss-standalone name: tailwindcss-standalone
path: packages/@tailwindcss-standalone/dist/ path: packages/@tailwindcss-standalone/dist/
- name: Prepare GitHub Release - name: Upload npm package tarballs
uses: softprops/action-gh-release@v2 uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with: with:
draft: true name: npm-package-tarballs
tag_name: ${{ env.TAG_NAME }} path: dist/*.tgz
body: |
${{ env.RELEASE_NOTES }} - name: Prepare GitHub Release
files: | if: ${{ !inputs.dry_run }}
packages/@tailwindcss-standalone/dist/sha256sums.txt env:
packages/@tailwindcss-standalone/dist/tailwindcss-linux-arm64 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
packages/@tailwindcss-standalone/dist/tailwindcss-linux-arm64-musl run: |
packages/@tailwindcss-standalone/dist/tailwindcss-linux-x64 gh release create "${TAG_NAME}" \
packages/@tailwindcss-standalone/dist/tailwindcss-linux-x64-musl --draft \
packages/@tailwindcss-standalone/dist/tailwindcss-macos-arm64 --title "${TAG_NAME}" \
packages/@tailwindcss-standalone/dist/tailwindcss-macos-x64 --notes "${RELEASE_NOTES}" \
packages/@tailwindcss-standalone/dist/tailwindcss-windows-x64.exe packages/@tailwindcss-standalone/dist/sha256sums.txt \
packages/@tailwindcss-standalone/dist/tailwindcss-linux-arm64 \
packages/@tailwindcss-standalone/dist/tailwindcss-linux-arm64-musl \
packages/@tailwindcss-standalone/dist/tailwindcss-linux-x64 \
packages/@tailwindcss-standalone/dist/tailwindcss-linux-x64-musl \
packages/@tailwindcss-standalone/dist/tailwindcss-macos-arm64 \
packages/@tailwindcss-standalone/dist/tailwindcss-macos-x64 \
packages/@tailwindcss-standalone/dist/tailwindcss-windows-x64.exe

View file

@ -1,290 +0,0 @@
name: Release Insiders
on:
push:
branches: [main]
permissions:
contents: read
env:
APP_NAME: tailwindcss-oxide
NODE_VERSION: 20
OXIDE_LOCATION: ./crates/node
RELEASE_CHANNEL: insiders
jobs:
build:
strategy:
matrix:
include:
# Windows
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: windows-latest
target: aarch64-pc-windows-msvc
# macOS
- os: macos-latest
target: x86_64-apple-darwin
strip: strip -x # Must use -x on macOS. This produces larger results on linux.
- os: macos-latest
target: aarch64-apple-darwin
page-size: 14
strip: strip -x # Must use -x on macOS. This produces larger results on linux.
# Android
- os: ubuntu-latest
target: aarch64-linux-android
strip: ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip
- os: ubuntu-latest
target: armv7-linux-androideabi
strip: ${ANDROID_NDK_LATEST_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip
# Linux
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
strip: strip
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
strip: llvm-strip
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
- os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf
strip: llvm-strip
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
strip: aarch64-linux-musl-strip
download: true
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
strip: strip
download: true
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
name: Build ${{ matrix.target }} (OXIDE)
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
# Cargo already skips downloading dependencies if they already exist
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Cache the `oxide` Rust build
- name: Cache oxide build
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/index.d.ts
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install Node.JS
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Rust (Stable)
if: ${{ matrix.download }}
run: |
rustup default stable
- name: Setup rust target
run: rustup target add ${{ matrix.target }}
- name: Install dependencies
run: pnpm install --ignore-scripts --filter=!./playgrounds/*
- name: Build release
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build
env:
RUST_TARGET: ${{ matrix.target }}
JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }}
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
if: ${{ matrix.strip }}
run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: bindings-${{ matrix.target }}
path: ${{ env.OXIDE_LOCATION }}/*.node
build-freebsd:
name: Build x86_64-unknown-freebsd (OXIDE)
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- name: Build FreeBSD
uses: cross-platform-actions/action@v0.25.0
env:
DEBUG: napi:*
RUSTUP_HOME: /usr/local/rustup
CARGO_HOME: /usr/local/cargo
RUSTUP_IO_THREADS: 1
RUST_TARGET: x86_64-unknown-freebsd
with:
operating_system: freebsd
version: '14.0'
memory: 13G
cpu_count: 3
environment_variables: 'DEBUG RUSTUP_IO_THREADS'
shell: bash
run: |
sudo pkg install -y -f curl node libnghttp2 npm
sudo npm install -g pnpm@9.6.0 --unsafe-perm=true
curl -sSf https://static.rust-lang.org/rustup/archive/1.27.1/x86_64-unknown-freebsd/rustup-init --output rustup-init
chmod +x rustup-init
./rustup-init -y --profile minimal
source "$HOME/.cargo/env"
echo "~~~~ rustc --version ~~~~"
rustc --version
echo "~~~~ node -v ~~~~"
node -v
echo "~~~~ pnpm --version ~~~~"
pnpm --version
pnpm install --ignore-scripts --filter=!./playgrounds/* || true
pnpm run --filter ${{ env.OXIDE_LOCATION }} build
strip -x ${{ env.OXIDE_LOCATION }}/*.node
ls -la ${{ env.OXIDE_LOCATION }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: bindings-x86_64-unknown-freebsd
path: ${{ env.OXIDE_LOCATION }}/*.node
release:
runs-on: macos-14
timeout-minutes: 15
name: Build and release Tailwind CSS insiders
permissions:
contents: write # for softprops/action-gh-release to create GitHub release
# https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions
id-token: write
needs:
- build
- build-freebsd
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 20
- name: Resolve version
id: vars
run: |
echo "SHA_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- uses: pnpm/action-setup@v4
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'
# Cargo already skips downloading dependencies if they already exist
- name: Cache cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Cache the `oxide` Rust build
- name: Cache oxide build
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/index.d.ts
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install dependencies
run: pnpm --filter=!./playgrounds/* install
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ${{ env.OXIDE_LOCATION }}
- name: Move artifacts
run: |
cd ${{ env.OXIDE_LOCATION }}
cp bindings-x86_64-pc-windows-msvc/* ./npm/win32-x64-msvc/
cp bindings-aarch64-pc-windows-msvc/* ./npm/win32-arm64-msvc/
cp bindings-x86_64-apple-darwin/* ./npm/darwin-x64/
cp bindings-aarch64-apple-darwin/* ./npm/darwin-arm64/
cp bindings-aarch64-linux-android/* ./npm/android-arm64/
cp bindings-armv7-linux-androideabi/* ./npm/android-arm-eabi/
cp bindings-aarch64-unknown-linux-gnu/* ./npm/linux-arm64-gnu/
cp bindings-aarch64-unknown-linux-musl/* ./npm/linux-arm64-musl/
cp bindings-armv7-unknown-linux-gnueabihf/* ./npm/linux-arm-gnueabihf/
cp bindings-x86_64-unknown-linux-gnu/* ./npm/linux-x64-gnu/
cp bindings-x86_64-unknown-linux-musl/* ./npm/linux-x64-musl/
cp bindings-x86_64-unknown-freebsd/* ./npm/freebsd-x64/
- name: 'Version based on commit: 0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }}'
run: pnpm run version-packages 0.0.0-${{ env.RELEASE_CHANNEL }}.${{ env.SHA_SHORT }}
- name: Build Tailwind CSS
run: pnpm run build
- name: Run pre-publish optimizations scripts
run: node ./scripts/pre-publish-optimizations.mjs
- name: Lock pre-release versions
run: node ./scripts/lock-pre-release-versions.mjs
- name: Publish
run: pnpm --recursive publish --tag ${{ env.RELEASE_CHANNEL }} --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Trigger Tailwind Play update
uses: actions/github-script@v7
with:
github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: 'tailwindlabs',
repo: 'play.tailwindcss.com',
ref: 'master',
workflow_id: 'upgrade-tailwindcss.yml'
})

View file

@ -1,18 +1,34 @@
name: Release name: Release
on: on:
push:
branches: [main]
release: release:
types: [published] types: [published]
workflow_dispatch: workflow_dispatch:
inputs:
channel:
description: Release channel to publish
required: true
default: insiders
type: choice
options:
- insiders
- release
permissions: permissions:
contents: read contents: read
env: env:
APP_NAME: tailwindcss-oxide APP_NAME: tailwindcss-oxide
NODE_VERSION: 20 NODE_VERSION: 24
PNPM_VERSION: '11.9.0'
OXIDE_LOCATION: ./crates/node OXIDE_LOCATION: ./crates/node
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
strategy: strategy:
@ -42,96 +58,97 @@ jobs:
- os: ubuntu-latest - os: ubuntu-latest
target: x86_64-unknown-linux-gnu target: x86_64-unknown-linux-gnu
strip: strip strip: strip
container: build-flags: --use-napi-cross
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian
- os: ubuntu-latest - os: ubuntu-latest
target: aarch64-unknown-linux-gnu target: aarch64-unknown-linux-gnu
strip: llvm-strip strip: aarch64-linux-gnu-strip
container: build-flags: --use-napi-cross
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64
- os: ubuntu-latest - os: ubuntu-latest
target: armv7-unknown-linux-gnueabihf target: armv7-unknown-linux-gnueabihf
strip: llvm-strip strip: arm-linux-gnueabihf-strip
container: build-flags: --use-napi-cross
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-zig
- os: ubuntu-latest - os: ubuntu-latest
target: aarch64-unknown-linux-musl target: aarch64-unknown-linux-musl
strip: aarch64-linux-musl-strip strip-zig: true
download: true build-flags: -x
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
- os: ubuntu-latest - os: ubuntu-latest
target: x86_64-unknown-linux-musl target: x86_64-unknown-linux-musl
strip: strip strip: strip
download: true build-flags: -x
container:
image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine
name: Build ${{ matrix.target }} (OXIDE) name: Build ${{ matrix.target }} (oxide)
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: pnpm/action-setup@v4 with:
persist-credentials: false
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
version: ${{ env.PNPM_VERSION }}
- name: Use Node.js ${{ env.NODE_VERSION }} - name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm' package-manager-cache: false
# Cargo already skips downloading dependencies if they already exist - name: Install gcc-arm-linux-gnueabihf
- name: Cache cargo if: ${{ matrix.target == 'armv7-unknown-linux-gnueabihf' }}
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Cache the `oxide` Rust build
- name: Cache oxide build
uses: actions/cache@v4
with:
path: |
./oxide/target/
./crates/node/*.node
./crates/node/index.js
./crates/node/index.d.ts
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }}
- name: Install Node.JS
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Rust (Stable)
if: ${{ matrix.download }}
run: | run: |
rustup default stable sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
- name: Install binutils-aarch64-linux-gnu
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
run: |
sudo apt-get update
sudo apt-get install binutils-aarch64-linux-gnu -y
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
if: ${{ contains(matrix.target, 'musl') }}
with:
version: 0.14.1
use-cache: false
- name: Install cargo-zigbuild
uses: taiki-e/install-action@65851e10cd6c377f11a60e600abc07cb08643468 # v2
if: ${{ contains(matrix.target, 'musl') }}
env:
GITHUB_TOKEN: ${{ github.token }}
with:
tool: cargo-zigbuild
- name: Setup rust target - name: Setup rust target
run: rustup target add ${{ matrix.target }} run: rustup target add ${{ matrix.target }}
- name: Install dependencies - name: Install dependencies
run: pnpm install --ignore-scripts --filter=!./playgrounds/* run: pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/*
- name: Build release - name: Build release
run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build run: pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform --target=${{ matrix.target }} ${{ matrix.build-flags }}
env: env:
RUST_TARGET: ${{ matrix.target }} RUST_TARGET: ${{ matrix.target }}
JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }} JEMALLOC_SYS_WITH_LG_PAGE: ${{ matrix.page-size }}
- name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034 - name: Strip debug symbols # https://github.com/rust-lang/rust/issues/46034
if: ${{ matrix.strip }} if: ${{ matrix.strip || matrix.strip-zig }}
run: ${{ matrix.strip }} ${{ env.OXIDE_LOCATION }}/*.node env:
STRIP_COMMAND: ${{ matrix.strip }}
STRIP_ZIG: ${{ matrix.strip-zig }}
run: |
if [ "$STRIP_ZIG" = "true" ]; then
for file in ${{ env.OXIDE_LOCATION }}/*.node; do
zig objcopy --strip-all "$file" "$file.stripped"
mv "$file.stripped" "$file"
done
exit 0
fi
eval "$STRIP_COMMAND ${{ env.OXIDE_LOCATION }}/*.node"
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with: with:
name: bindings-${{ matrix.target }} name: bindings-${{ matrix.target }}
path: ${{ env.OXIDE_LOCATION }}/*.node path: ${{ env.OXIDE_LOCATION }}/*.node
@ -141,9 +158,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Build FreeBSD - name: Build FreeBSD
uses: cross-platform-actions/action@v0.25.0 uses: cross-platform-actions/action@cdc9ee69ef84a5f2e59c9058335d9c57bcb4ac86 # v0.25.0
env: env:
DEBUG: napi:* DEBUG: napi:*
RUSTUP_HOME: /usr/local/rustup RUSTUP_HOME: /usr/local/rustup
@ -159,7 +178,7 @@ jobs:
shell: bash shell: bash
run: | run: |
sudo pkg install -y -f curl node libnghttp2 npm sudo pkg install -y -f curl node libnghttp2 npm
sudo npm install -g pnpm@9.6.0 --unsafe-perm=true sudo npm install -g pnpm@${{ env.PNPM_VERSION }} --unsafe-perm=true
curl -sSf https://static.rust-lang.org/rustup/archive/1.27.1/x86_64-unknown-freebsd/rustup-init --output rustup-init curl -sSf https://static.rust-lang.org/rustup/archive/1.27.1/x86_64-unknown-freebsd/rustup-init --output rustup-init
chmod +x rustup-init chmod +x rustup-init
./rustup-init -y --profile minimal ./rustup-init -y --profile minimal
@ -170,12 +189,12 @@ jobs:
node -v node -v
echo "~~~~ pnpm --version ~~~~" echo "~~~~ pnpm --version ~~~~"
pnpm --version pnpm --version
pnpm install --ignore-scripts --filter=!./playgrounds/* || true pnpm install --ignore-scripts --frozen-lockfile --filter=!./playgrounds/* || true
pnpm run --filter ${{ env.OXIDE_LOCATION }} build pnpm run --filter ${{ env.OXIDE_LOCATION }} build:platform
strip -x ${{ env.OXIDE_LOCATION }}/*.node strip -x ${{ env.OXIDE_LOCATION }}/*.node
ls -la ${{ env.OXIDE_LOCATION }} ls -la ${{ env.OXIDE_LOCATION }}
- name: Upload artifacts - name: Upload artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with: with:
name: bindings-x86_64-unknown-freebsd name: bindings-x86_64-unknown-freebsd
path: ${{ env.OXIDE_LOCATION }}/*.node path: ${{ env.OXIDE_LOCATION }}/*.node
@ -183,10 +202,10 @@ jobs:
release: release:
runs-on: macos-14 runs-on: macos-14
timeout-minutes: 15 timeout-minutes: 15
name: Build and release Tailwind CSS name: Build and publish Tailwind CSS
permissions: permissions:
contents: write # for softprops/action-gh-release to create GitHub release contents: read
# https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions # https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions
id-token: write id-token: write
@ -195,47 +214,51 @@ jobs:
- build-freebsd - build-freebsd
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
fetch-depth: 20 fetch-depth: 20
persist-credentials: false
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
with:
version: ${{ env.PNPM_VERSION }}
- name: Use Node.js ${{ env.NODE_VERSION }} - name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v4 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with: with:
node-version: ${{ env.NODE_VERSION }} node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
package-manager-cache: false
# Cargo already skips downloading dependencies if they already exist # npm trusted publishing validates the caller workflow filename, so all npm publishes live here.
- name: Cache cargo # This workflow rebuilds the publish artifacts instead of depending on prepare-release.yml.
uses: actions/cache@v4 - name: Resolve release metadata
with: env:
path: | INPUT_CHANNEL: ${{ github.event.inputs.channel || '' }}
~/.cargo/bin/ run: |
~/.cargo/registry/index/ if [[ "${{ github.event_name }}" == "release" || "$INPUT_CHANNEL" == "release" ]]; then
~/.cargo/registry/cache/ release_channel=$(node ./scripts/release-channel.js)
~/.cargo/git/db/
target/
key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# Cache the `oxide` Rust build echo "RELEASE_KIND=release" >> $GITHUB_ENV
- name: Cache oxide build echo "RELEASE_CHANNEL=$release_channel" >> $GITHUB_ENV
uses: actions/cache@v4 echo "FEATURES_ENV=stable" >> $GITHUB_ENV
with: else
path: | sha_short=$(git rev-parse --short HEAD)
./oxide/target/
./crates/node/*.node echo "RELEASE_KIND=insiders" >> $GITHUB_ENV
./crates/node/index.js echo "RELEASE_CHANNEL=insiders" >> $GITHUB_ENV
./crates/node/index.d.ts echo "SHA_SHORT=$sha_short" >> $GITHUB_ENV
key: ${{ runner.os }}-${{ matrix.target }}-oxide-${{ hashFiles('./crates/**/*') }} echo "INSIDERS_VERSION=0.0.0-insiders.$sha_short" >> $GITHUB_ENV
fi
- name: Setup WASM target
run: rustup target add wasm32-wasip1-threads
- name: Install dependencies - name: Install dependencies
run: pnpm --filter=!./playgrounds/* install run: pnpm --filter=!./playgrounds/* install --frozen-lockfile
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v4 uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with: with:
path: ${{ env.OXIDE_LOCATION }} path: ${{ env.OXIDE_LOCATION }}
@ -255,10 +278,19 @@ jobs:
cp bindings-x86_64-unknown-linux-musl/* ./npm/linux-x64-musl/ cp bindings-x86_64-unknown-linux-musl/* ./npm/linux-x64-musl/
cp bindings-x86_64-unknown-freebsd/* ./npm/freebsd-x64/ cp bindings-x86_64-unknown-freebsd/* ./npm/freebsd-x64/
- name: 'Version based on commit: ${{ env.INSIDERS_VERSION }}'
if: env.RELEASE_KIND == 'insiders'
run: pnpm run version-packages ${INSIDERS_VERSION}
- name: Build Tailwind CSS - name: Build Tailwind CSS
if: env.RELEASE_KIND == 'insiders'
run: pnpm run build
- name: Build Tailwind CSS
if: env.RELEASE_KIND == 'release'
run: pnpm run build run: pnpm run build
env: env:
FEATURES_ENV: stable FEATURES_ENV: ${{ env.FEATURES_ENV }}
- name: Run pre-publish optimizations scripts - name: Run pre-publish optimizations scripts
run: node ./scripts/pre-publish-optimizations.mjs run: node ./scripts/pre-publish-optimizations.mjs
@ -266,25 +298,61 @@ jobs:
- name: Lock pre-release versions - name: Lock pre-release versions
run: node ./scripts/lock-pre-release-versions.mjs run: node ./scripts/lock-pre-release-versions.mjs
- name: Calculate environment variables - name: Upload npm package tarballs
run: | uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
echo "RELEASE_CHANNEL=$(node ./scripts/release-channel.js)" >> $GITHUB_ENV with:
echo "TAILWINDCSS_VERSION=$(node -e 'console.log(require(`./packages/tailwindcss/package.json`).version);')" >> $GITHUB_ENV name: npm-package-tarballs
path: dist/*.tgz
- name: Publish - name: Publish
run: pnpm --recursive publish --tag ${{ env.RELEASE_CHANNEL }} --no-git-checks run: |
env: pnpm --recursive --filter="!@tailwindcss/oxide-wasm32-wasi" publish --tag ${RELEASE_CHANNEL} --no-git-checks
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # The wasm package needs a special npm config that isn't read when pnpm --recursive is used
pushd crates/node/npm/wasm32-wasi; pnpm publish --tag ${RELEASE_CHANNEL} --no-git-checks --config.node-linker=hoisted; popd;
- name: Trigger Tailwind Play update - name: Trigger Tailwind Play update
if: env.RELEASE_CHANNEL == 'latest' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }} github-token: ${{ secrets.TAILWIND_PLAY_TOKEN }}
script: | script: |
await github.rest.actions.createWorkflowDispatch({ await github.rest.actions.createWorkflowDispatch({
owner: 'tailwindlabs', owner: 'tailwindlabs',
repo: 'play.tailwindcss.com', repo: 'upgrades',
ref: 'master', ref: 'main',
workflow_id: 'upgrade-tailwindcss.yml' workflow_id: 'upgrade-tailwindcss.yml'
}) })
notify:
if: ${{ always() && (needs.build.result == 'failure' || needs.build-freebsd.result == 'failure' || needs.release.result == 'failure') }}
needs:
- build
- build-freebsd
- release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Resolve release label
id: release
env:
INPUT_CHANNEL: ${{ github.event.inputs.channel || '' }}
RELEASE_TAG: ${{ github.event.release.tag_name || '' }}
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
tag_name="${RELEASE_TAG:-${GITHUB_REF_NAME}}"
echo "label=release ${tag_name}" >> $GITHUB_OUTPUT
elif [[ "$INPUT_CHANNEL" == "release" ]]; then
version=$(node -p "require('./packages/tailwindcss/package.json').version")
echo "label=release v${version}" >> $GITHUB_OUTPUT
else
sha_short=$(git rev-parse --short HEAD)
echo "label=insiders release 0.0.0-insiders.${sha_short}" >> $GITHUB_OUTPUT
fi
- name: Notify Discord
uses: discord-actions/message@5c7149c81a83146e5d01f142be1bf87a61831c4d # v2
with:
webhookUrl: ${{ secrets.DISCORD_WEBHOOK_URL }}
message: 'The [most recent ${{ github.workflow }} workflow](<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}>) for `${{ steps.release.outputs.label }}` has failed.'

1
.npmrc
View file

@ -1 +0,0 @@
auto-install-peers = true

View file

@ -4,5 +4,6 @@ pnpm-lock.yaml
target/ target/
crates/node/index.d.ts crates/node/index.d.ts
crates/node/index.js crates/node/index.js
crates/ignore/
.next .next
.fingerprint .fingerprint

View file

@ -7,7 +7,490 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
- Nothing yet! ### Added
- Add `@tailwindcss/turbopack` package to run Tailwind CSS with Next.js ([20367](https://github.com/tailwindlabs/tailwindcss/pull/20367))
### Fixed
- Ensure watch mode detects changes to symlinked `@source` files whose real paths aren't otherwise scanned ([#20356](https://github.com/tailwindlabs/tailwindcss/pull/20356))
- Ensure custom variants using `@scope` wrap the generated utilities instead of nesting inside them ([#20369](https://github.com/tailwindlabs/tailwindcss/pull/20369))
- Fix flattening of `@scope` at-rules ([#20369](https://github.com/tailwindlabs/tailwindcss/pull/20369))
- Fix standalone declarations in `@scope`, wrap them in `:where(:scope)` ([#20369](https://github.com/tailwindlabs/tailwindcss/pull/20369))
- Always emit a space for empty fallback values in CSS variables (e.g. `var(--tw-blur,)` → `var(--tw-blur, )`) ([#20373](https://github.com/tailwindlabs/tailwindcss/pull/20373))
- Canonicalization: convert arbitrary breakpoint and container query variants to named equivalents (e.g. `max-[64rem]` → `max-lg`) ([#20380](https://github.com/tailwindlabs/tailwindcss/pull/20380))
- Prevent `@tailwindcss/vite` from crashing on every edit under Vite's experimental `bundledDev` mode ([#20379](https://github.com/tailwindlabs/tailwindcss/pull/20379))
- Ensure `@tailwindcss/oxide` falls back to WASM on platforms without native bindings ([#20383](https://github.com/tailwindlabs/tailwindcss/pull/20383))
- Detect classes in Ruby percent literals using angle brackets or custom delimiters (e.g. `%w<flex>`, `%w|flex|`), including in Slim and Haml templates ([#20387](https://github.com/tailwindlabs/tailwindcss/pull/20387))
- Preserve whitespace in `--default(…)` values in custom functional utilities (e.g. `--default(box alphabetic)` no longer becomes `boxalphabetic`) ([#20392](https://github.com/tailwindlabs/tailwindcss/pull/20392))
- Don't scan gitignored directories (e.g. `node_modules` and `.git`) when the project uses a safelist-style `.gitignore` (e.g. `/*` followed by `!/…` negations) ([#20397](https://github.com/tailwindlabs/tailwindcss/pull/20397))
- Ensure root `theme('…')` namespace lookups in JavaScript plugins and config files return the full namespace object instead of the value of its `DEFAULT` key ([#20399](https://github.com/tailwindlabs/tailwindcss/pull/20399))
- Skip ignored directories entirely when computing watch globs (`scanner.globs`), instead of walking their full contents on every rebuild ([#20408](https://github.com/tailwindlabs/tailwindcss/pull/20408))
- Oxide: drop invalid UTF-8 candidates ([#20389](https://github.com/tailwindlabs/tailwindcss/pull/20389))
- `@tailwindcss/vite` no longer forces a full page reload for external files (e.g.: `.php` files) ([#20414](https://github.com/tailwindlabs/tailwindcss/issues/20414))
- Canonicalization: don't merge utilities that reference different theme variables set to CSS-wide keywords like `unset` ([#20417](https://github.com/tailwindlabs/tailwindcss/pull/20417))
- Don't generate utilities when a modifier is used that would otherwise be silently ignored (e.g. `rounded-sm/[5]`, `shadow-sm/foo`, `stroke-2/50`) ([#20419](https://github.com/tailwindlabs/tailwindcss/pull/20419))
- Only normalize top-level `and`, `or`, and `not` keywords in `supports-[…]` variants (e.g. `selector(a: not (.foo))` → `selector(a:not(.foo))`) ([#20420](https://github.com/tailwindlabs/tailwindcss/pull/20420))
- Don't warn about Angular's `::ng-deep` and `:host-context()` when optimizing CSS ([#20434](https://github.com/tailwindlabs/tailwindcss/pull/20434))
- Don't generate CSS for candidates containing an empty additional modifier (e.g. `bg-red-500/50/` and `group-hover/foo//bar:flex`) ([#20466](https://github.com/tailwindlabs/tailwindcss/pull/20466))
- Sort `min-*`, `max-*`, and container query variants with decimal values numerically (e.g. `min-[40.25rem]` before `min-[40.5rem]`) ([#20512](https://github.com/tailwindlabs/tailwindcss/pull/20512))
- Ensure CSS comments ending with `\*/` are closed correctly instead of swallowing the CSS that follows (e.g. `/* C:\temp\*/`) ([#20508](https://github.com/tailwindlabs/tailwindcss/pull/20508))
- Improve style invalidation performance of `group-*` and `peer-*` variants ([#20513](https://github.com/tailwindlabs/tailwindcss/pull/20513))
## [4.3.3] - 2026-07-16
### Fixed
- Support `--watch --poll[=ms]` in `@tailwindcss/cli` when filesystem events are unreliable or unavailable ([#20297](https://github.com/tailwindlabs/tailwindcss/pull/20297))
- Canonicalization: match arbitrary hex colors against theme colors case-insensitively (e.g. `bg-[#fff]` and `bg-[#FFF]` → `bg-white`) ([#20298](https://github.com/tailwindlabs/tailwindcss/pull/20298))
- Prevent Preflight from overriding Firefox's native `iframe:focus-visible` outline styles ([#20292](https://github.com/tailwindlabs/tailwindcss/pull/20292))
- Ensure `theme('colors.foo')` in JS plugins resolves correctly when both `--color-foo` and `--color-foo-bar` exist ([#20299](https://github.com/tailwindlabs/tailwindcss/pull/20299))
- Ensure fractional opacity modifiers work with named shadow sizes like `shadow-sm/12.5`, `text-shadow-sm/12.5`, `drop-shadow-sm/12.5`, and `inset-shadow-sm/12.5` ([#20302](https://github.com/tailwindlabs/tailwindcss/pull/20302))
- Parse selectors like `[data-foo]div` as two selectors instead of one ([#20303](https://github.com/tailwindlabs/tailwindcss/pull/20303))
- Ensure `@tailwindcss/postcss` rebuilds when a preprocessor like Sass changes the input CSS without changing the input file on disk ([#20310](https://github.com/tailwindlabs/tailwindcss/pull/20310))
- Ensure CSS nesting is handled even when Lightning CSS isn't run, such as in `@tailwindcss/browser` and Tailwind Play ([#20124](https://github.com/tailwindlabs/tailwindcss/pull/20124))
- Prevent achromatic theme colors from shifting hue when mixed in polar color spaces like `oklch` ([#20314](https://github.com/tailwindlabs/tailwindcss/pull/20314))
- Ensure `--spacing(0)` is optimized to `0px` instead of `0` so it remains a `<length>` when used in `calc(…)` ([#20319](https://github.com/tailwindlabs/tailwindcss/pull/20319))
- Load `@parcel/watcher` only when needed in `@tailwindcss/cli --watch` mode, so one-off builds and `--watch --poll` work when `@parcel/watcher` can't be loaded ([#20325](https://github.com/tailwindlabs/tailwindcss/pull/20325))
- Use explicit platform fonts instead of `system-ui` and `ui-sans-serif` so CJK text respects the page's `lang` attribute on Windows ([#20318](https://github.com/tailwindlabs/tailwindcss/pull/20318))
- Prevent `@tailwindcss/upgrade` from rewriting ignored files when run from a subdirectory ([#20329](https://github.com/tailwindlabs/tailwindcss/pull/20329))
- Ensure earlier `@source` rules pointing to nested files are scanned when later `@source` rules point to files in parent folders ([#20335](https://github.com/tailwindlabs/tailwindcss/pull/20335))
- Prevent `@tailwindcss/vite` from triggering full page reloads when scanned files are processed by Vite but haven't been loaded as modules yet ([#20336](https://github.com/tailwindlabs/tailwindcss/pull/20336))
## [4.3.2] - 2026-06-26
### Fixed
- Support bare spacing values for `auto-rows-*` and `auto-cols-*` utilities (e.g. `auto-rows-12` and `auto-cols-16`) ([#20229](https://github.com/tailwindlabs/tailwindcss/pull/20229))
- Prevent `@tailwindcss/cli` in `--watch` mode from crashing on Windows when `@source` points to a directory that doesn't exist ([#20242](https://github.com/tailwindlabs/tailwindcss/pull/20242))
- Prevent `@tailwindcss/vite` from crashing in Deno v2.8.x when `context.parentURL` is not a valid URL ([#20245](https://github.com/tailwindlabs/tailwindcss/pull/20245))
- Ensure `@tailwindcss/cli` in `--watch` mode rebuilds when the input CSS file changes in an ignored directory ([#20246](https://github.com/tailwindlabs/tailwindcss/pull/20246))
- Allow `@variant` rules used in `addBase(…)` to use custom variants defined later ([#20247](https://github.com/tailwindlabs/tailwindcss/pull/20247))
- Prevent `@tailwindcss/vite` from crashing during HMR when scanned files or directories are deleted ([#20259](https://github.com/tailwindlabs/tailwindcss/pull/20259))
- Generate `font-size` instead of `color` declarations for `text-[--spacing(…)]` ([#20260](https://github.com/tailwindlabs/tailwindcss/pull/20260))
- Prevent `@source` patterns from scanning unrelated sibling files and folders ([#20263](https://github.com/tailwindlabs/tailwindcss/pull/20263))
- Extract class candidates adjacent to Template Toolkit delimiters like `%]…[%` in `.tt`, `.tt2`, and `.tx` files ([#20269](https://github.com/tailwindlabs/tailwindcss/pull/20269))
- Extract class candidates from conditional Maud syntax like `p.text-black[condition]` ([#20269](https://github.com/tailwindlabs/tailwindcss/pull/20269))
- Prevent `@position-try` rules from triggering unknown at-rule warnings when optimizing CSS ([#20277](https://github.com/tailwindlabs/tailwindcss/pull/20277))
- Support class suggestions for named opacity modifiers from `--opacity` theme values ([#20287](https://github.com/tailwindlabs/tailwindcss/pull/20287))
- Prevent type errors in `@tailwindcss/postcss` when used with newer PostCSS patch releases ([#20289](https://github.com/tailwindlabs/tailwindcss/pull/20289))
## [4.3.1] - 2026-06-12
### Added
- Add `--silent` option to suppress output in `@tailwindcss/cli` ([#20100](https://github.com/tailwindlabs/tailwindcss/pull/20100))
### Fixed
- Remove deprecation warnings by using `Module#registerHooks` instead of `Module#register` on Node 26+ ([#20028](https://github.com/tailwindlabs/tailwindcss/pull/20028))
- Canonicalization: don't crash when plugin utilities throw for unsupported values ([#20052](https://github.com/tailwindlabs/tailwindcss/pull/20052))
- Allow `@apply` to be used with CSS mixins ([#19427](https://github.com/tailwindlabs/tailwindcss/pull/19427))
- Ensure `not-*` correctly negates `@container` queries, including `style(…)` queries ([#20059](https://github.com/tailwindlabs/tailwindcss/pull/20059))
- Ensure `drop-shadow-*` color utilities work with custom shadow values containing `calc(…)` ([#20080](https://github.com/tailwindlabs/tailwindcss/pull/20080))
- Fix 'Sourcemap is likely to be incorrect' warnings when using `@tailwindcss/vite` ([#20103](https://github.com/tailwindlabs/tailwindcss/pull/20103))
- Ensure `@tailwindcss/webpack` can be installed in Rspack projects without requiring `webpack` as a peer dependency ([#20027](https://github.com/tailwindlabs/tailwindcss/pull/20027))
- Canonicalization: don't suggest invalid `calc(…)` expressions (e.g. `px-[calc(1rem+0px)]` → `px-[calc(1rem+0)]`) ([#20127](https://github.com/tailwindlabs/tailwindcss/pull/20127))
- Canonicalization: avoid suggesting large spacing-scale values for arbitrary lengths (e.g. `left-[99999px]` → `left-[99999px]`, not `left-24999.75`) ([#20130](https://github.com/tailwindlabs/tailwindcss/pull/20130))
- Ensure `@tailwindcss/cli` in `--watch` mode recovers when a tracked dependency is deleted and restored ([#20137](https://github.com/tailwindlabs/tailwindcss/pull/20137))
- Ensure standalone `@tailwindcss/cli` binaries are ignored when scanning for class candidates ([#20139](https://github.com/tailwindlabs/tailwindcss/pull/20139))
- Ensure class candidates are extracted from Twig `addClass(…)` and `removeClass(…)` calls ([#20198](https://github.com/tailwindlabs/tailwindcss/pull/20198))
- Don't crash in the Ruby or Vue preprocessors when scanning files containing invalid UTF-8 bytes ([#19588](https://github.com/tailwindlabs/tailwindcss/pull/19588))
- Allow `@variant` to be used inside `addBase` ([#19480](https://github.com/tailwindlabs/tailwindcss/pull/19480))
- Ensure `@source` globs with symlinks are preserved ([#20203](https://github.com/tailwindlabs/tailwindcss/pull/20203))
- Ensure later `@source` rules can re-include files excluded by earlier `@source not` rules ([#20203](https://github.com/tailwindlabs/tailwindcss/pull/20203))
- Upgrade: don't migrate empty class rules to invalid `@utility` rules ([#20205](https://github.com/tailwindlabs/tailwindcss/pull/20205))
- Ensure transitions between `inset-shadow-none` and other inset shadows work correctly ([#20208](https://github.com/tailwindlabs/tailwindcss/pull/20208))
- Ensure explicitly referenced `@source` directories are scanned even when ignored by git ([#20214](https://github.com/tailwindlabs/tailwindcss/pull/20214))
- Ensure `@source` globs ending in `**/*` preserve dynamic path segments to avoid scanning too many files ([#20217](https://github.com/tailwindlabs/tailwindcss/pull/20217))
- Canonicalization: don't fold `calc(…)` divisions when the result would require high precision (e.g. `w-[calc(100%/3.5)]` → `w-[calc(100%/3.5)]`, not `w-[28.571428571428573%]`) ([#20221](https://github.com/tailwindlabs/tailwindcss/pull/20221))
- Serve ESM type declarations to ESM importers of `@tailwindcss/postcss` ([#20228](https://github.com/tailwindlabs/tailwindcss/pull/20228))
### Changed
- Generate `0` instead of `calc(var(--spacing) * 0)` for spacing utilities like `m-0` and `left-0` ([#20196](https://github.com/tailwindlabs/tailwindcss/pull/20196))
- Generate `var(--spacing)` instead of `calc(var(--spacing) * 1)` for spacing utilities like `m-1` and `left-1` ([#20196](https://github.com/tailwindlabs/tailwindcss/pull/20196))
## [4.3.0] - 2026-05-08
### Added
- Add `@container-size` utility ([#18901](https://github.com/tailwindlabs/tailwindcss/pull/18901))
- Add `scrollbar-{auto,thin,none}` utilities for `scrollbar-width`, and `scrollbar-thumb-*` / `scrollbar-track-*` color utilities for `scrollbar-color` ([#19981](https://github.com/tailwindlabs/tailwindcss/pull/19981), [#20019](https://github.com/tailwindlabs/tailwindcss/pull/20019))
- Add `scrollbar-gutter-*` utilities ([#20018](https://github.com/tailwindlabs/tailwindcss/pull/20018))
- Add `zoom-*` utilities ([#20020](https://github.com/tailwindlabs/tailwindcss/pull/20020))
- Add `tab-*` utilities ([#20022](https://github.com/tailwindlabs/tailwindcss/pull/20022))
- Allow using `@variant` with stacked variants (e.g. `@variant hover:focus { … }`) ([#19996](https://github.com/tailwindlabs/tailwindcss/pull/19996))
- Allow using `@variant` with compound variants (e.g. `@variant hover, focus { … }`) ([#19996](https://github.com/tailwindlabs/tailwindcss/pull/19996))
- Support `--default(…)` in `--value(…)` and `--modifier(…)` for functional `@utility` definitions ([#19989](https://github.com/tailwindlabs/tailwindcss/pull/19989))
### Fixed
- Ensure `@plugin` resolves package JavaScript entries instead of browser CSS entries when using `@tailwindcss/vite` ([#19949](https://github.com/tailwindlabs/tailwindcss/pull/19949))
- Fix relative `@import` and `@plugin` paths resolving from the wrong directory when using `@tailwindcss/vite` ([#19965](https://github.com/tailwindlabs/tailwindcss/pull/19965))
- Ensure CSS files containing `@variant` are processed by `@tailwindcss/vite` ([#19966](https://github.com/tailwindlabs/tailwindcss/pull/19966))
- Resolve imports relative to `base` when `result.opts.from` is not provided when using `@tailwindcss/postcss` ([#19980](https://github.com/tailwindlabs/tailwindcss/pull/19980))
- Canonicalization: preserve significant `_` whitespace in arbitrary values ([#19986](https://github.com/tailwindlabs/tailwindcss/pull/19986))
- Canonicalization: add parentheses when removing whitespace from arbitrary values would hurt readability (e.g. `w-[calc(100%---spacing(60))]` → `w-[calc(100%-(--spacing(60)))]`) ([#19986](https://github.com/tailwindlabs/tailwindcss/pull/19986))
- Canonicalization: preserve the original unit in arbitrary values instead of normalizing to base units (e.g. `-mt-[20in]` → `mt-[-20in]`, not `mt-[-1920px]`) ([#19988](https://github.com/tailwindlabs/tailwindcss/pull/19988))
- Canonicalization: migrate arbitrary `:has()` variants from `[&:has(…)]` to `has-[…]` ([#19991](https://github.com/tailwindlabs/tailwindcss/pull/19991))
- Upgrade: don’t migrate inline `style` attributes (e.g. `style="flex-grow: 1"` → `style="flex-grow: 1"`, not `style="grow: 1"`) ([#19918](https://github.com/tailwindlabs/tailwindcss/pull/19918))
- Allow multiple `@utility` definitions with the same name but different value types ([#19777](https://github.com/tailwindlabs/tailwindcss/pull/19777))
- Export missing `PluginWithConfig` type from `tailwindcss/plugin` to fix errors when inferring plugin config types ([#19707](https://github.com/tailwindlabs/tailwindcss/pull/19707))
- Ensure `start` and `end` legacy utilities without values do not generate CSS ([#20003](https://github.com/tailwindlabs/tailwindcss/pull/20003))
- Ensure `--value(…)` is required in functional `@utility` definitions ([#20005](https://github.com/tailwindlabs/tailwindcss/pull/20005))
- Canonicalization: preserve required whitespace around operators in negated arbitrary values (e.g. `-left-[(var(--a)+var(--b))]`) ([#20011](https://github.com/tailwindlabs/tailwindcss/pull/20011))
## [4.2.4] - 2026-04-21
### Fixed
- Ensure imports in `@import` and `@plugin` still resolve correctly when using Vite aliases in `@tailwindcss/vite` ([#19947](https://github.com/tailwindlabs/tailwindcss/pull/19947))
## [4.2.3] - 2026-04-20
### Fixed
- Canonicalization: improve canonicalization for `tracking-*` utilities by preferring non-negative utilities (e.g. `-tracking-tighter` → `tracking-wider`) ([#19827](https://github.com/tailwindlabs/tailwindcss/pull/19827))
- Fix crash due to invalid characters in candidate (exceeding valid unicode code point range) ([#19829](https://github.com/tailwindlabs/tailwindcss/pull/19829))
- Ensure query params in imports are considered unique resources when using `@tailwindcss/webpack` ([#19723](https://github.com/tailwindlabs/tailwindcss/pull/19723))
- Canonicalization: collapse arbitrary values into shorthand utilities (e.g. `px-[1.2rem] py-[1.2rem]` → `p-[1.2rem]`) ([#19837](https://github.com/tailwindlabs/tailwindcss/pull/19837))
- Canonicalization: collapse `border-{t,b}-*` into `border-y-*`, `border-{l,r}-*` into `border-x-*`, and `border-{t,r,b,l}-*` into `border-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842))
- Canonicalization: collapse `scroll-m{t,b}-*` into `scroll-my-*`, `scroll-m{l,r}-*` into `scroll-mx-*`, and `scroll-m{t,r,b,l}-*` into `scroll-m-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842))
- Canonicalization: collapse `scroll-p{t,b}-*` into `scroll-py-*`, `scroll-p{l,r}-*` into `scroll-px-*`, and `scroll-p{t,r,b,l}-*` into `scroll-p-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842))
- Canonicalization: collapse `overflow-{x,y}-*` into `overflow-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842))
- Canonicalization: collapse `overscroll-{x,y}-*` into `overscroll-*` ([#19842](https://github.com/tailwindlabs/tailwindcss/pull/19842))
- Read from `--placeholder-color` instead of `--background-color` for `placeholder-*` utilities ([#19843](https://github.com/tailwindlabs/tailwindcss/pull/19843))
- Upgrade: ensure files are not emptied out when killing the upgrade process while it's running ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846))
- Upgrade: use `config.content` when migrating from Tailwind CSS v3 to Tailwind CSS v4 ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846))
- Upgrade: never migrate files that are ignored by git ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846))
- Add `.env` and `.env.*` to default ignored content files ([#19846](https://github.com/tailwindlabs/tailwindcss/pull/19846))
- Canonicalization: migrate `overflow-ellipsis` into `text-ellipsis` ([#19849](https://github.com/tailwindlabs/tailwindcss/pull/19849))
- Canonicalization: migrate `start-full` → `inset-s-full`, `start-auto` → `inset-s-auto`, `start-px` → `inset-s-px`, and `start-<number>` → `inset-s-<number>` as well as negative versions ([#19849](https://github.com/tailwindlabs/tailwindcss/pull/19849))
- Canonicalization: migrate `end-full` → `inset-e-full`, `end-auto` → `inset-e-auto`, `end-px` → `inset-e-px`, and `end-<number>` → `inset-e-<number>` as well as negative versions ([#19849](https://github.com/tailwindlabs/tailwindcss/pull/19849))
- Canonicalization: move the `-` sign inside the arbitrary value `-left-[9rem]` → `left-[-9rem]` ([#19858](https://github.com/tailwindlabs/tailwindcss/pull/19858))
- Canonicalization: move the `-` sign outside the arbitrary value `ml-[calc(-1*var(--width))]` → `-ml-(--width)` ([#19858](https://github.com/tailwindlabs/tailwindcss/pull/19858))
- Improve performance when scanning JSONL / NDJSON files ([#19862](https://github.com/tailwindlabs/tailwindcss/pull/19862))
- Support `NODE_PATH` environment variable in standalone CLI ([#19617](https://github.com/tailwindlabs/tailwindcss/pull/19617))
## [4.2.2] - 2026-03-18
### Fixed
- Don't crash when candidates contain prototype properties like `row-constructor` ([#19725](https://github.com/tailwindlabs/tailwindcss/pull/19725))
- Canonicalize `calc(var(--spacing)*…)` expressions into `--spacing(…)` ([#19769](https://github.com/tailwindlabs/tailwindcss/pull/19769))
- Fix crash in canonicalization step when handling utilities containing `@property` at-rules (e.g. `shadow-sm border`) ([#19727](https://github.com/tailwindlabs/tailwindcss/pull/19727))
- Skip full reload for server only modules scanned by client CSS when using `@tailwindcss/vite` ([#19745](https://github.com/tailwindlabs/tailwindcss/pull/19745))
- Add support for Vite 8 in `@tailwindcss/vite` ([#19790](https://github.com/tailwindlabs/tailwindcss/pull/19790))
- Improve canonicalization for bare values exceeding default spacing scale suggestions (e.g. `w-1234 h-1234` → `size-1234`) ([#19809](https://github.com/tailwindlabs/tailwindcss/pull/19809))
- Fix canonicalization resulting in empty list (e.g. `w-5 h-5 size-5` → `''` instead of `size-5`) ([#19812](https://github.com/tailwindlabs/tailwindcss/pull/19812))
- Resolve tsconfig paths to allow for `@import '@/path/to/file';` when using `@tailwindcss/vite` ([#19803](https://github.com/tailwindlabs/tailwindcss/pull/19803))
## [4.2.1] - 2026-02-23
### Fixed
- Allow trailing dash in functional utility names for backwards compatibility ([#19696](https://github.com/tailwindlabs/tailwindcss/pull/19696))
- Properly detect classes containing `.` characters within curly braces in MDX files ([#19711](https://github.com/tailwindlabs/tailwindcss/pull/19711))
## [4.2.0] - 2026-02-18
### Added
- Add mauve, olive, mist, and taupe color palettes to the default theme ([#19627](https://github.com/tailwindlabs/tailwindcss/pull/19627))
- Add `@tailwindcss/webpack` package to run Tailwind CSS as a webpack plugin ([#19610](https://github.com/tailwindlabs/tailwindcss/pull/19610))
- Add `pbs-*` and `pbe-*` utilities for `padding-block-start` and `padding-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601))
- Add `mbs-*` and `mbe-*` utilities for `margin-block-start` and `margin-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601))
- Add `scroll-pbs-*` and `scroll-pbe-*` utilities for `scroll-padding-block-start` and `scroll-padding-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601))
- Add `scroll-mbs-*` and `scroll-mbe-*` utilities for `scroll-margin-block-start` and `scroll-margin-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601))
- Add `border-bs-*` and `border-be-*` utilities for `border-block-start` and `border-block-end` ([#19601](https://github.com/tailwindlabs/tailwindcss/pull/19601))
- Add `inline-*`, `min-inline-*`, `max-inline-*` utilities for `inline-size`, `min-inline-size`, and `max-inline-size` ([#19612](https://github.com/tailwindlabs/tailwindcss/pull/19612))
- Add `block-*`, `min-block-*`, `max-block-*` utilities for `block-size`, `min-block-size`, and `max-block-size` ([#19612](https://github.com/tailwindlabs/tailwindcss/pull/19612))
- Add `inset-s-*`, `inset-e-*`, `inset-bs-*`, `inset-be-*` utilities for `inset-inline-start`, `inset-inline-end`, `inset-block-start`, and `inset-block-end` ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613))
- Add `font-features-*` utility for `font-feature-settings` ([#19623](https://github.com/tailwindlabs/tailwindcss/pull/19623))
### Fixed
- Prevent double `@supports` wrapper for `color-mix` values ([#19450](https://github.com/tailwindlabs/tailwindcss/pull/19450))
- Allow whitespace around `@source inline()` argument ([#19461](https://github.com/tailwindlabs/tailwindcss/pull/19461))
- Emit comment when source maps are saved to files when using `@tailwindcss/cli` ([#19447](https://github.com/tailwindlabs/tailwindcss/pull/19447))
- Detect utilities containing capital letters followed by numbers ([#19465](https://github.com/tailwindlabs/tailwindcss/pull/19465))
- Fix class extraction for Rails' strict locals ([#19525](https://github.com/tailwindlabs/tailwindcss/pull/19525))
- Align `@utility` name validation with Oxide scanner rules ([#19524](https://github.com/tailwindlabs/tailwindcss/pull/19524))
- Fix infinite loop when using `@variant` inside `@custom-variant` ([#19633](https://github.com/tailwindlabs/tailwindcss/pull/19633))
- Allow multiples of `.25` in `aspect-*` fractions (e.g. `aspect-8.5/11`) ([#19688](https://github.com/tailwindlabs/tailwindcss/pull/19688))
- Ensure changes to external files listed via `@source` trigger a full page reload when using `@tailwindcss/vite` ([#19670](https://github.com/tailwindlabs/tailwindcss/pull/19670))
- Improve performance of Oxide scanner in bigger projects by reducing file system walks ([#19632](https://github.com/tailwindlabs/tailwindcss/pull/19632))
- Ensure import aliases in Astro v5 work without crashing when using `@tailwindcss/vite` ([#19677](https://github.com/tailwindlabs/tailwindcss/issues/19677))
- Allow escape characters in `@utility` names to improve support with formatters such as Biome ([#19626](https://github.com/tailwindlabs/tailwindcss/pull/19626))
- Fix incorrect canonicalization results when canonicalizing multiple times ([#19675](https://github.com/tailwindlabs/tailwindcss/pull/19675))
- Add `.jj` to default ignored content directories ([#19687](https://github.com/tailwindlabs/tailwindcss/pull/19687))
### Deprecated
- Deprecate `start-*` and `end-*` utilities in favor of `inset-s-*` and `inset-e-*` utilities ([#19613](https://github.com/tailwindlabs/tailwindcss/pull/19613))
## [4.1.18] - 2025-12-11
### Fixed
- Ensure validation of `source(…)` happens relative to the file it is in ([#19274](https://github.com/tailwindlabs/tailwindcss/pull/19274))
- Include filename and line numbers in CSS parse errors ([#19282](https://github.com/tailwindlabs/tailwindcss/pull/19282))
- Skip comments in Ruby files when checking for class names ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243))
- Skip over arbitrary property utilities with a top-level `!` in the value ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243))
- Support environment API in `@tailwindcss/vite` ([#18970](https://github.com/tailwindlabs/tailwindcss/pull/18970))
- Preserve case of theme keys from JS configs and plugins ([#19337](https://github.com/tailwindlabs/tailwindcss/pull/19337))
- Write source maps correctly on the CLI when using `--watch` ([#19373](https://github.com/tailwindlabs/tailwindcss/pull/19373))
- Handle special defaults (like `ringColor.DEFAULT`) in JS configs ([#19348](https://github.com/tailwindlabs/tailwindcss/pull/19348))
- Improve backwards compatibility for `content` theme key from JS configs ([#19381](https://github.com/tailwindlabs/tailwindcss/pull/19381))
- Upgrade: Handle `future` and `experimental` config keys ([#19344](https://github.com/tailwindlabs/tailwindcss/pull/19344))
- Try to canonicalize any arbitrary utility to a bare value ([#19379](https://github.com/tailwindlabs/tailwindcss/pull/19379))
- Validate candidates similarly to Oxide ([#19397](https://github.com/tailwindlabs/tailwindcss/pull/19397))
- Canonicalization: combine `text-*` and `leading-*` classes ([#19396](https://github.com/tailwindlabs/tailwindcss/pull/19396))
- Correctly handle duplicate CLI arguments ([#19416](https://github.com/tailwindlabs/tailwindcss/pull/19416))
- Don’t emit color-mix fallback rules inside `@keyframes` ([#19419](https://github.com/tailwindlabs/tailwindcss/pull/19419))
- CLI: Don't hang when output is `/dev/stdout` ([#19421](https://github.com/tailwindlabs/tailwindcss/pull/19421))
## [3.4.19] - 2025-12-10
### Fixed
- Don’t break `sibling-*()` functions when used inside `calc(…)` ([#19335](https://github.com/tailwindlabs/tailwindcss/pull/19335))
## [4.1.17] - 2025-11-06
### Fixed
- Substitute `@variant` inside legacy JS APIs ([#19263](https://github.com/tailwindlabs/tailwindcss/pull/19263))
- Prevent occasional crash on Windows when loaded into a worker thread ([#19242](https://github.com/tailwindlabs/tailwindcss/pull/19242))
## [4.1.16] - 2025-10-23
### Fixed
- Discard candidates with an empty data type ([#19172](https://github.com/tailwindlabs/tailwindcss/pull/19172))
- Fix canonicalization of arbitrary variants with attribute selectors ([#19176](https://github.com/tailwindlabs/tailwindcss/pull/19176))
- Fix invalid colors due to nested `&` ([#19184](https://github.com/tailwindlabs/tailwindcss/pull/19184))
- Improve canonicalization for `& > :pseudo` and `& :pseudo` arbitrary variants ([#19178](https://github.com/tailwindlabs/tailwindcss/pull/19178))
## [4.1.15] - 2025-10-20
### Fixed
- Fix Safari devtools rendering issue due to `color-mix` fallback ([#19069](https://github.com/tailwindlabs/tailwindcss/pull/19069))
- Suppress Lightning CSS warnings about `:deep`, `:slotted`, and `:global` ([#19094](https://github.com/tailwindlabs/tailwindcss/pull/19094))
- Fix resolving theme keys when starting with the name of another theme key in JS configs and plugins ([#19097](https://github.com/tailwindlabs/tailwindcss/pull/19097))
- Allow named groups in combination with `not-*`, `has-*`, and `in-*` ([#19100](https://github.com/tailwindlabs/tailwindcss/pull/19100))
- Prevent important utilities from affecting other utilities ([#19110](https://github.com/tailwindlabs/tailwindcss/pull/19110))
- Don’t index into strings with the `theme(…)` function ([#19111](https://github.com/tailwindlabs/tailwindcss/pull/19111))
- Fix parsing issue when `\t` is used in at-rules ([#19130](https://github.com/tailwindlabs/tailwindcss/pull/19130))
- Upgrade: Canonicalize utilities containing `0` values ([#19095](https://github.com/tailwindlabs/tailwindcss/pull/19095))
- Upgrade: Migrate deprecated `break-words` to `wrap-break-word` ([#19157](https://github.com/tailwindlabs/tailwindcss/pull/19157))
### Changed
- Remove the `postinstall` script from oxide ([#19149])(https://github.com/tailwindlabs/tailwindcss/pull/19149)
## [4.1.14] - 2025-10-01
### Fixed
- Handle `'` syntax in ClojureScript when extracting classes ([#18888](https://github.com/tailwindlabs/tailwindcss/pull/18888))
- Handle `@variant` inside `@custom-variant` ([#18885](https://github.com/tailwindlabs/tailwindcss/pull/18885))
- Merge suggestions when using `@utility` ([#18900](https://github.com/tailwindlabs/tailwindcss/pull/18900))
- Ensure that file system watchers created when using the CLI are always cleaned up ([#18905](https://github.com/tailwindlabs/tailwindcss/pull/18905))
- Do not generate `grid-column` utilities when configuring `grid-column-start` or `grid-column-end` ([#18907](https://github.com/tailwindlabs/tailwindcss/pull/18907))
- Do not generate `grid-row` utilities when configuring `grid-row-start` or `grid-row-end` ([#18907](https://github.com/tailwindlabs/tailwindcss/pull/18907))
- Prevent duplicate CSS when overwriting a static utility with a theme key ([#18056](https://github.com/tailwindlabs/tailwindcss/pull/18056))
- Show Lightning CSS warnings (if any) when optimizing/minifying ([#18918](https://github.com/tailwindlabs/tailwindcss/pull/18918))
- Use `default` export condition for `@tailwindcss/vite` ([#18948](https://github.com/tailwindlabs/tailwindcss/pull/18948))
- Re-throw errors from PostCSS nodes ([#18373](https://github.com/tailwindlabs/tailwindcss/pull/18373))
- Detect classes in markdown inline directives ([#18967](https://github.com/tailwindlabs/tailwindcss/pull/18967))
- Ensure files with only `@theme` produce no output when built ([#18979](https://github.com/tailwindlabs/tailwindcss/pull/18979))
- Support Maud templates when extracting classes ([#18988](https://github.com/tailwindlabs/tailwindcss/pull/18988))
- Upgrade: Do not migrate `variant = 'outline'` during upgrades ([#18922](https://github.com/tailwindlabs/tailwindcss/pull/18922))
- Upgrade: Show version mismatch (if any) when running upgrade tool ([#19028](https://github.com/tailwindlabs/tailwindcss/pull/19028))
- Upgrade: Ensure first class inside `className` is migrated ([#19031](https://github.com/tailwindlabs/tailwindcss/pull/19031))
- Upgrade: Migrate classes inside `*ClassName` and `*Class` attributes ([#19031](https://github.com/tailwindlabs/tailwindcss/pull/19031))
## [3.4.18] - 2025-10-01
### Fixed
- Improve support for raw `supports-[…]` queries in arbitrary values ([#13605](https://github.com/tailwindlabs/tailwindcss/pull/13605))
- Fix `require.cache` error when loaded through a TypeScript file in Node 22.18+ ([#18665](https://github.com/tailwindlabs/tailwindcss/pull/18665))
- Support `import.meta.resolve(…)` in configs for new enough Node.js versions ([#18938](https://github.com/tailwindlabs/tailwindcss/pull/18938))
- Allow using newer versions of `postcss-load-config` for better ESM and TypeScript PostCSS config support with the CLI ([#18938](https://github.com/tailwindlabs/tailwindcss/pull/18938))
- Remove irrelevant utility rules when matching important classes ([#19030](https://github.com/tailwindlabs/tailwindcss/pull/19030))
## [4.1.13] - 2025-09-03
### Changed
- Drop warning from browser build ([#18731](https://github.com/tailwindlabs/tailwindcss/issues/18731))
- Drop exact duplicate declarations when emitting CSS ([#18809](https://github.com/tailwindlabs/tailwindcss/issues/18809))
### Fixed
- Don't transition `visibility` when using `transition` ([#18795](https://github.com/tailwindlabs/tailwindcss/pull/18795))
- Discard matched variants with unknown named values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
- Discard matched variants with non-string values ([#18799](https://github.com/tailwindlabs/tailwindcss/pull/18799))
- Show suggestions for known `matchVariant` values ([#18798](https://github.com/tailwindlabs/tailwindcss/pull/18798))
- Replace deprecated `clip` with `clip-path` in `sr-only` ([#18769](https://github.com/tailwindlabs/tailwindcss/pull/18769))
- Hide internal fields from completions in `matchUtilities` ([#18820](https://github.com/tailwindlabs/tailwindcss/pull/18820))
- Ignore `.vercel` folders by default (can be overridden by `@source …` rules) ([#18855](https://github.com/tailwindlabs/tailwindcss/pull/18855))
- Consider variants starting with `@-` to be invalid (e.g. `@-2xl:flex`) ([#18869](https://github.com/tailwindlabs/tailwindcss/pull/18869))
- Do not allow custom variants to start or end with a `-` or `_` ([#18867](https://github.com/tailwindlabs/tailwindcss/pull/18867), [#18872](https://github.com/tailwindlabs/tailwindcss/pull/18872))
- Upgrade: Migrate `aria` theme keys to `@custom-variant` ([#18815](https://github.com/tailwindlabs/tailwindcss/pull/18815))
- Upgrade: Migrate `data` theme keys to `@custom-variant` ([#18816](https://github.com/tailwindlabs/tailwindcss/pull/18816))
- Upgrade: Migrate `supports` theme keys to `@custom-variant` ([#18817](https://github.com/tailwindlabs/tailwindcss/pull/18817))
## [4.1.12] - 2025-08-13
### Fixed
- Don't consider the global important state in `@apply` ([#18404](https://github.com/tailwindlabs/tailwindcss/pull/18404))
- Add missing suggestions for `flex-<number>` utilities ([#18642](https://github.com/tailwindlabs/tailwindcss/pull/18642))
- Fix trailing `)` from interfering with extraction in Clojure keywords ([#18345](https://github.com/tailwindlabs/tailwindcss/pull/18345))
- Detect classes inside Elixir charlist, word list, and string sigils ([#18432](https://github.com/tailwindlabs/tailwindcss/pull/18432))
- Track source locations through `@plugin` and `@config` ([#18345](https://github.com/tailwindlabs/tailwindcss/pull/18345))
- Allow boolean values of `process.env.DEBUG` in `@tailwindcss/node` ([#18485](https://github.com/tailwindlabs/tailwindcss/pull/18485))
- Ignore consecutive semicolons in the CSS parser ([#18532](https://github.com/tailwindlabs/tailwindcss/pull/18532))
- Center the dropdown icon added to an input with a paired datalist by default ([#18511](https://github.com/tailwindlabs/tailwindcss/pull/18511))
- Extract candidates in Slang templates ([#18565](https://github.com/tailwindlabs/tailwindcss/pull/18565))
- Improve error messages when encountering invalid functional utility names ([#18568](https://github.com/tailwindlabs/tailwindcss/pull/18568))
- Discard CSS AST objects with `false` or `undefined` properties ([#18571](https://github.com/tailwindlabs/tailwindcss/pull/18571))
- Allow users to disable URL rebasing in `@tailwindcss/postcss` via `transformAssetUrls: false` ([#18321](https://github.com/tailwindlabs/tailwindcss/pull/18321))
- Fix false-positive migrations in `addEventListener` and JavaScript variable names ([#18718](https://github.com/tailwindlabs/tailwindcss/pull/18718))
- Fix Standalone CLI showing default Bun help when run via symlink on Windows ([#18723](https://github.com/tailwindlabs/tailwindcss/pull/18723))
- Read from `--border-color-*` theme keys in `divide-*` utilities for backwards compatibility ([#18704](https://github.com/tailwindlabs/tailwindcss/pull/18704/))
- Don't scan `.hdr` and `.exr` files for classes by default ([#18734](https://github.com/tailwindlabs/tailwindcss/pull/18734))
## [4.1.11] - 2025-06-26
### Fixed
- Add heuristic to skip candidate migrations inside `emit(…)` ([#18330](https://github.com/tailwindlabs/tailwindcss/pull/18330))
- Extract candidates with variants in Clojure/ClojureScript keywords ([#18338](https://github.com/tailwindlabs/tailwindcss/pull/18338))
- Document `--watch=always` in the CLI's usage ([#18337](https://github.com/tailwindlabs/tailwindcss/pull/18337))
- Add support for Vite 7 to `@tailwindcss/vite` ([#18384](https://github.com/tailwindlabs/tailwindcss/pull/18384))
## [4.1.10] - 2025-06-11
### Fixed
- Fix incorrectly generated CSS when using percentages in arbitrary values with calc (e.g. `w-[calc(100%-var(--offset))]`) ([#18289](https://github.com/tailwindlabs/tailwindcss/pull/18289))
## [4.1.9] - 2025-06-11
### Fixed
- Correctly parse custom properties with strings containing semicolons ([#18251](https://github.com/tailwindlabs/tailwindcss/pull/18251))
- Upgrade: Migrate arbitrary modifiers without percentage signs to bare values (e.g. `/[0.16]` → `/16`) ([#18184](https://github.com/tailwindlabs/tailwindcss/pull/18184))
- Upgrade: Migrate CSS variable shorthands where fallback value contains function call ([#18184](https://github.com/tailwindlabs/tailwindcss/pull/18184))
- Upgrade: Migrate negative arbitrary values to negative bare values (e.g. `mb-[-32rem]` → `-mb-128`) ([#18212](https://github.com/tailwindlabs/tailwindcss/pull/18212))
- Upgrade: Do not migrate `blur` in `wire:model.blur` ([#18216](https://github.com/tailwindlabs/tailwindcss/pull/18216))
- Don't add spaces around CSS dashed idents when formatting math expressions ([#18220](https://github.com/tailwindlabs/tailwindcss/pull/18220))
## [4.1.8] - 2025-05-27
### Added
- Improve error messages when `@apply` fails ([#18059](https://github.com/tailwindlabs/tailwindcss/pull/18059))
### Fixed
- Upgrade: Do not migrate declarations that look like candidates in `<style>` blocks ([#18057](https://github.com/tailwindlabs/tailwindcss/pull/18057), [18068](https://github.com/tailwindlabs/tailwindcss/pull/18068))
- Upgrade: Don't error when looking for `tailwindcss` in pnpm monorepos ([#18065](https://github.com/tailwindlabs/tailwindcss/pull/18065))
- Upgrade: Don't error when updating dependencies in pnpm monorepos ([#18065](https://github.com/tailwindlabs/tailwindcss/pull/18065))
- Upgrade: Migrate deprecated `order-none` to `order-0` ([#18126](https://github.com/tailwindlabs/tailwindcss/pull/18126))
- Support Leptos `class:` attributes when extracting classes ([#18093](https://github.com/tailwindlabs/tailwindcss/pull/18093))
- Fix "Cannot read properties of undefined" crash on malformed arbitrary value ([#18133](https://github.com/tailwindlabs/tailwindcss/pull/18133))
- Upgrade: Migrate `-mt-[0px]` to `mt-[0px]` instead of the other way around ([#18154](https://github.com/tailwindlabs/tailwindcss/pull/18154))
- Fix Haml pre-processing crash when there is no `\n` at the end of the file ([#18155](https://github.com/tailwindlabs/tailwindcss/pull/18155))
- Ignore `.pnpm-store` folders by default (can be overridden by `@source …` rules) ([#18163](https://github.com/tailwindlabs/tailwindcss/pull/18163))
- Fix PostCSS crash when calling `toJSON()` ([#18083](https://github.com/tailwindlabs/tailwindcss/pull/18083))
## [4.1.7] - 2025-05-15
### Added
- Upgrade: Migrate bare values to named values ([#18000](https://github.com/tailwindlabs/tailwindcss/pull/18000))
- Upgrade: Added cache to improve template migration performance ([#18025](https://github.com/tailwindlabs/tailwindcss/pull/18025))
### Fixed
- Allow `_` before numbers during candidate extraction ([#17961](https://github.com/tailwindlabs/tailwindcss/pull/17961))
- Prevent duplicate suggestions when using `@theme` and `@utility` together ([#17675](https://github.com/tailwindlabs/tailwindcss/pull/17675))
- Ensure that media queries within `::before` and `::after` pseudo selectors create valid CSS rules in production builds ([#17979](https://github.com/tailwindlabs/tailwindcss/pull/17979))
- Ensure that the standalone CLI does not leave temporary files behind ([#17981](https://github.com/tailwindlabs/tailwindcss/pull/17981))
- Ensure `-rotate-*` utilities properly negate arbitrary values ([#18014](https://github.com/tailwindlabs/tailwindcss/pull/18014))
- Ignore custom variants using `:merge(…)` selectors in legacy JS plugins ([#18020](https://github.com/tailwindlabs/tailwindcss/pull/18020))
- Ensure classes containing `.` are properly extracted from Clojure files ([#18038](https://github.com/tailwindlabs/tailwindcss/pull/18038))
- Upgrade: Fix error when using `@import … source(…)` ([#17963](https://github.com/tailwindlabs/tailwindcss/pull/17963))
- Upgrade: Change casing of utilities with named values to kebab-case to match updated theme variables ([#18017](https://github.com/tailwindlabs/tailwindcss/pull/18017))
- Upgrade: Don't migrate strings that match utility names in Vue attribute bindings other than `class` ([#18025](https://github.com/tailwindlabs/tailwindcss/pull/18025))
## [4.1.6] - 2025-05-09
### Added
- Upgrade: Automatically convert arbitrary values to named values when possible (e.g. `h-[1lh]` to `h-lh`) ([#17831](https://github.com/tailwindlabs/tailwindcss/pull/17831), [#17854](https://github.com/tailwindlabs/tailwindcss/pull/17854))
- Upgrade: Update dependencies in parallel for improved performance ([#17898](https://github.com/tailwindlabs/tailwindcss/pull/17898))
- Add detailed logging about `@source` directives, discovered files and scanned files when using `DEBUG=*` ([#17906](https://github.com/tailwindlabs/tailwindcss/pull/17906), [#17952](https://github.com/tailwindlabs/tailwindcss/pull/17952))
- Add support for generating source maps in development ([#17775](https://github.com/tailwindlabs/tailwindcss/pull/17775))
### Fixed
- Ensure negative arbitrary `scale` values generate negative values ([#17831](https://github.com/tailwindlabs/tailwindcss/pull/17831))
- Fix HAML extraction with embedded Ruby ([#17846](https://github.com/tailwindlabs/tailwindcss/pull/17846))
- Don't scan files for utilities when using `@reference` ([#17836](https://github.com/tailwindlabs/tailwindcss/pull/17836))
- Fix incorrectly replacing `_` with ` ` in arbitrary modifier shorthand `bg-red-500/(--my_opacity)` ([#17889](https://github.com/tailwindlabs/tailwindcss/pull/17889))
- Don't scan `.log` files for classes by default ([#17906](https://github.com/tailwindlabs/tailwindcss/pull/17906))
- Ensure that custom utilities applying other custom utilities don't swallow nested `@apply` rules ([#17925](https://github.com/tailwindlabs/tailwindcss/pull/17925))
- Download platform specific package if `optionalDependencies` are skipped ([#17929](https://github.com/tailwindlabs/tailwindcss/pull/17929))
## [4.1.5] - 2025-04-30
### Added
- Support using `@tailwindcss/upgrade` to upgrade between versions of v4.\* ([#17717](https://github.com/tailwindlabs/tailwindcss/pull/17717))
- Add `h-lh` / `min-h-lh` / `max-h-lh` utilities ([#17790](https://github.com/tailwindlabs/tailwindcss/pull/17790))
- Transition `display`, `visibility`, `content-visibility`, `overlay`, and `pointer-events` when using `transition` to simplify `@starting-style` usage ([#17812](https://github.com/tailwindlabs/tailwindcss/pull/17812))
### Fixed
- Don't scan `.geojson` or `.db` files for classes by default ([#17700](https://github.com/tailwindlabs/tailwindcss/pull/17700), [#17711](https://github.com/tailwindlabs/tailwindcss/pull/17711))
- Hide default shadow suggestions when missing default shadow theme keys ([#17743](https://github.com/tailwindlabs/tailwindcss/pull/17743))
- Replace `_` with `.` in theme suggestions for `@utility` if surrounded by digits ([#17733](https://github.com/tailwindlabs/tailwindcss/pull/17733))
- Skip `color-mix(…)` when opacity is `100%` ([#17815](https://github.com/tailwindlabs/tailwindcss/pull/17815))
- PostCSS: Ensure that errors in imported stylesheets are recoverable ([#17754](https://github.com/tailwindlabs/tailwindcss/pull/17754))
- Upgrade: Bump all Tailwind CSS related dependencies during upgrade ([#17763](https://github.com/tailwindlabs/tailwindcss/pull/17763))
- Upgrade: Don't add `-` to variants starting with `@` ([#17814](https://github.com/tailwindlabs/tailwindcss/pull/17814))
- Upgrade: Don't format stylesheets that didn't change when upgrading ([#17824](https://github.com/tailwindlabs/tailwindcss/pull/17824))
### Changed
- Ignore `.hg`, `.svn`, `.venv`, `venv`, `.yarn`, `.next`, `.turbo`, `.parcel-cache`, `__pycache__`, and `.svelte-kit` folders by default (can be overridden by `@source …` rules) ([#17892](https://github.com/tailwindlabs/tailwindcss/pull/17892))
- `@source` rules that point inside `.hg`, `.svn`, `.venv`, `venv`, `.yarn`, `.next`, `.turbo`, `.parcel-cache`, `__pycache__`, and `.svelte-kit` folders no longer consider your `.gitignore` rules ([#17892](https://github.com/tailwindlabs/tailwindcss/pull/17892))
## [4.1.4] - 2025-04-14
### Added
- Add experimental `@tailwindcss/oxide-wasm32-wasi` target for running Tailwind in browser environments like StackBlitz ([#17558](https://github.com/tailwindlabs/tailwindcss/pull/17558))
### Fixed
- Ensure `color-mix(…)` polyfills do not cause used CSS variables to be removed ([#17555](https://github.com/tailwindlabs/tailwindcss/pull/17555))
- Ensure `color-mix(…)` polyfills create fallbacks for theme variables that reference other theme variables ([#17562](https://github.com/tailwindlabs/tailwindcss/pull/17562))
- Fix brace expansion in declining ranges like `{10..0..5}` and `{0..10..-5}` ([#17591](https://github.com/tailwindlabs/tailwindcss/pull/17591))
- Work around a Chrome rendering bug when using the `skew-*` utilities ([#17627](https://github.com/tailwindlabs/tailwindcss/pull/17627))
- Ensure container query variant names can contain hyphens ([#17628](https://github.com/tailwindlabs/tailwindcss/pull/17628))
- Ensure `shadow-inherit`, `inset-shadow-inherit`, `drop-shadow-inherit`, and `text-shadow-inherit` inherit the shadow color ([#17647](https://github.com/tailwindlabs/tailwindcss/pull/17647))
- Ensure compatibility with array tuples used in `fontSize` JS theme keys ([#17630](https://github.com/tailwindlabs/tailwindcss/pull/17630))
- Ensure folders with binary file extensions in their names are scanned for utilities ([#17595](https://github.com/tailwindlabs/tailwindcss/pull/17595))
- Upgrade: Convert `fontSize` array tuple syntax to CSS theme variables ([#17630](https://github.com/tailwindlabs/tailwindcss/pull/17630))
## [4.1.3] - 2025-04-04 ## [4.1.3] - 2025-04-04
@ -3640,7 +4123,33 @@ No release notes
- Everything! - Everything!
[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.3...HEAD [unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v4.3.3...HEAD
[4.3.3]: https://github.com/tailwindlabs/tailwindcss/compare/v4.3.2...v4.3.3
[4.3.2]: https://github.com/tailwindlabs/tailwindcss/compare/v4.3.1...v4.3.2
[4.3.1]: https://github.com/tailwindlabs/tailwindcss/compare/v4.3.0...v4.3.1
[4.3.0]: https://github.com/tailwindlabs/tailwindcss/compare/v4.2.4...v4.3.0
[4.2.4]: https://github.com/tailwindlabs/tailwindcss/compare/v4.2.3...v4.2.4
[4.2.3]: https://github.com/tailwindlabs/tailwindcss/compare/v4.2.2...v4.2.3
[4.2.2]: https://github.com/tailwindlabs/tailwindcss/compare/v4.2.1...v4.2.2
[4.2.1]: https://github.com/tailwindlabs/tailwindcss/compare/v4.2.0...v4.2.1
[4.2.0]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.18...v4.2.0
[4.1.18]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.17...v4.1.18
[3.4.19]: https://github.com/tailwindlabs/tailwindcss/compare/v3.4.18...v3.4.19
[4.1.17]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.16...v4.1.17
[4.1.16]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.15...v4.1.16
[4.1.15]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.14...v4.1.15
[4.1.14]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.13...v4.1.14
[3.4.18]: https://github.com/tailwindlabs/tailwindcss/compare/v3.4.17...v3.4.18
[4.1.13]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.12...v4.1.13
[4.1.12]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.11...v4.1.12
[4.1.11]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.10...v4.1.11
[4.1.10]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.9...v4.1.10
[4.1.9]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.8...v4.1.9
[4.1.8]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.7...v4.1.8
[4.1.7]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.6...v4.1.7
[4.1.6]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.5...v4.1.6
[4.1.5]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.4...v4.1.5
[4.1.4]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.3...v4.1.4
[4.1.3]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.2...v4.1.3 [4.1.3]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.2...v4.1.3
[4.1.2]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.1...v4.1.2 [4.1.2]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.1...v4.1.2
[4.1.1]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.0...v4.1.1 [4.1.1]: https://github.com/tailwindlabs/tailwindcss/compare/v4.1.0...v4.1.1

311
Cargo.lock generated
View file

@ -40,7 +40,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
dependencies = [ dependencies = [
"memchr", "memchr",
"regex-automata 0.4.8", "regex-automata 0.4.18",
"serde", "serde",
] ]
@ -60,32 +60,30 @@ dependencies = [
] ]
[[package]] [[package]]
name = "convert_case" name = "console"
version = "0.6.0" version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" checksum = "4fe5f465a4f6fee88fad41b85d990f84c835335e85b5d9e6e63e0d06d28cba7c"
dependencies = [
"encode_unicode",
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "convert_case"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49"
dependencies = [ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "crossbeam"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-queue",
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.13" version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
@ -109,15 +107,6 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.20" version = "0.8.20"
@ -126,13 +115,9 @@ checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]] [[package]]
name = "ctor" name = "ctor"
version = "0.2.1" version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990a40740adf249724a6000c0fc4bd574712f50bb17c2d6f6cec837ae2f0ee75" checksum = "2d83cb7e7a873830708d6b02a78cd36a592c6fa14bf267b68725103b85c0d77f"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "diff" name = "diff"
@ -152,6 +137,12 @@ version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.9" version = "0.3.9"
@ -178,15 +169,103 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
[[package]] [[package]]
name = "globset" name = "futures"
version = "0.4.16" version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-executor"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-macro"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"slab",
]
[[package]]
name = "globset"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"bstr", "bstr",
"log", "log",
"regex-automata 0.4.8", "regex-automata 0.4.18",
"regex-syntax 0.8.5", "regex-syntax 0.8.5",
] ]
@ -197,27 +276,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"ignore 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)", "ignore 0.4.23",
"walkdir", "walkdir",
] ]
[[package]]
name = "ignore"
version = "0.4.23"
dependencies = [
"bstr",
"crossbeam-channel",
"crossbeam-deque",
"dunce",
"globset",
"log",
"memchr",
"regex-automata 0.4.8",
"same-file",
"walkdir",
"winapi-util",
]
[[package]] [[package]]
name = "ignore" name = "ignore"
version = "0.4.23" version = "0.4.23"
@ -228,12 +290,41 @@ dependencies = [
"globset", "globset",
"log", "log",
"memchr", "memchr",
"regex-automata 0.4.8", "regex-automata 0.4.18",
"same-file", "same-file",
"walkdir", "walkdir",
"winapi-util", "winapi-util",
] ]
[[package]]
name = "ignore"
version = "0.4.33"
dependencies = [
"bstr",
"crossbeam-channel",
"crossbeam-deque",
"dunce",
"globset",
"log",
"memchr",
"regex-automata 0.4.18",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "insta"
version = "1.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0f8fee8c926415c58d6ae43a08523a26faccb2323f5e6b644fe7dd4ef6b82"
dependencies = [
"console",
"once_cell",
"similar",
"tempfile",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.11.0" version = "0.11.0"
@ -257,12 +348,12 @@ checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
[[package]] [[package]]
name = "libloading" name = "libloading"
version = "0.8.5" version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets", "windows-link",
] ]
[[package]] [[package]]
@ -300,31 +391,33 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "napi" name = "napi"
version = "2.16.16" version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "839ae2ee5e62c6348669c50098b187c08115bd3cced658c9c0bf945fca0fec83" checksum = "de33522036981030a75c231829566bc63414e08101a6f5ff4ac6cef19c8e0941"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"ctor", "ctor",
"napi-derive", "futures",
"napi-build",
"napi-sys", "napi-sys",
"once_cell", "nohash-hasher",
"rustc-hash",
] ]
[[package]] [[package]]
name = "napi-build" name = "napi-build"
version = "2.1.4" version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db836caddef23662b94e16bf1f26c40eceb09d6aee5d5b06a7ac199320b69b19" checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1"
[[package]] [[package]]
name = "napi-derive" name = "napi-derive"
version = "2.16.13" version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cbe2585d8ac223f7d34f13701434b9d5f4eb9c332cccce8dee57ea18ab8ab0c" checksum = "a49c513341a61a16a10af6efcce46b30d0822ba2d4fb197d24d33dfc199c78d5"
dependencies = [ dependencies = [
"cfg-if",
"convert_case", "convert_case",
"ctor",
"napi-derive-backend", "napi-derive-backend",
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -333,28 +426,32 @@ dependencies = [
[[package]] [[package]]
name = "napi-derive-backend" name = "napi-derive-backend"
version = "1.0.75" version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1639aaa9eeb76e91c6ae66da8ce3e89e921cd3885e99ec85f4abacae72fc91bf" checksum = "d60b5d773ad46c698c8cc2cd9fde0b283d39cbb7f71c04bee633c7bdba4423bd"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"regex",
"semver", "semver",
"syn", "syn",
] ]
[[package]] [[package]]
name = "napi-sys" name = "napi-sys"
version = "2.4.0" version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427802e8ec3a734331fec1035594a210ce1ff4dc5bc1950530920ab717964ea3" checksum = "85fbf1fa9f1babfe396d74bbbf52b3643770243e8f5b0b46715d4caf7f0dfc9a"
dependencies = [ dependencies = [
"libloading", "libloading",
] ]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -377,9 +474,9 @@ dependencies = [
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]] [[package]]
name = "overload" name = "overload"
@ -414,18 +511,18 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.28" version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.10.0" version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
dependencies = [ dependencies = [
"either", "either",
"rayon-core", "rayon-core",
@ -433,9 +530,9 @@ dependencies = [
[[package]] [[package]]
name = "rayon-core" name = "rayon-core"
version = "1.12.1" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
dependencies = [ dependencies = [
"crossbeam-deque", "crossbeam-deque",
"crossbeam-utils", "crossbeam-utils",
@ -449,7 +546,7 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
"regex-automata 0.4.8", "regex-automata 0.4.18",
"regex-syntax 0.8.5", "regex-syntax 0.8.5",
] ]
@ -464,9 +561,9 @@ dependencies = [
[[package]] [[package]]
name = "regex-automata" name = "regex-automata"
version = "0.4.8" version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -534,6 +631,18 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.10.0" version = "1.10.0"
@ -542,9 +651,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.18" version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -569,11 +678,11 @@ dependencies = [
"bexpand", "bexpand",
"bstr", "bstr",
"classification-macros", "classification-macros",
"crossbeam",
"dunce", "dunce",
"fast-glob", "fast-glob",
"globwalk", "globwalk",
"ignore 0.4.23", "ignore 0.4.33",
"insta",
"log", "log",
"pretty_assertions", "pretty_assertions",
"rayon", "rayon",
@ -582,6 +691,7 @@ dependencies = [
"tempfile", "tempfile",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"unicode-width",
"walkdir", "walkdir",
] ]
@ -681,6 +791,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
[[package]] [[package]]
name = "valuable" name = "valuable"
version = "0.1.0" version = "0.1.0"
@ -728,6 +844,12 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -746,6 +868,15 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.6"

View file

@ -13,10 +13,10 @@
</p> </p>
<p align="center"> <p align="center">
<a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/actions/workflow/status/tailwindlabs/tailwindcss/ci.yml?branch=next" alt="Build Status"></a> <a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/actions/workflow/status/tailwindlabs/tailwindcss/ci.yml?branch=main" alt="Build Status"></a>
<a href="https://www.npmjs.com/package/tailwindcss"><img src="https://img.shields.io/npm/dt/tailwindcss.svg" alt="Total Downloads"></a> <a href="https://www.npmjs.com/package/tailwindcss"><img src="https://img.shields.io/npm/dt/tailwindcss.svg" alt="Total Downloads"></a>
<a href="https://github.com/tailwindcss/tailwindcss/releases"><img src="https://img.shields.io/npm/v/tailwindcss.svg" alt="Latest Release"></a> <a href="https://github.com/tailwindlabs/tailwindcss/releases"><img src="https://img.shields.io/npm/v/tailwindcss.svg" alt="Latest Release"></a>
<a href="https://github.com/tailwindcss/tailwindcss/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/tailwindcss.svg" alt="License"></a> <a href="https://github.com/tailwindlabs/tailwindcss/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/tailwindcss.svg" alt="License"></a>
</p> </p>
--- ---
@ -27,14 +27,10 @@ For full documentation, visit [tailwindcss.com](https://tailwindcss.com).
## Community ## Community
For help, discussion about best practices, or any other conversation that would benefit from being searchable: For help, discussion about best practices, or feature ideas:
[Discuss Tailwind CSS on GitHub](https://github.com/tailwindcss/tailwindcss/discussions) [Discuss Tailwind CSS on GitHub](https://github.com/tailwindlabs/tailwindcss/discussions)
For chatting with others using the framework:
[Join the Tailwind CSS Discord Server](https://discord.gg/7NF8GNe)
## Contributing ## Contributing
If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindcss/tailwindcss/blob/next/.github/CONTRIBUTING.md) **before submitting a pull request**. If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindlabs/tailwindcss/blob/main/.github/CONTRIBUTING.md) **before submitting a pull request**.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "ignore" name = "ignore"
version = "0.4.23" #:version version = "0.4.33" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"] authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = """ description = """
A fast library for efficiently matching ignore files such as `.gitignore` A fast library for efficiently matching ignore files such as `.gitignore`
@ -12,7 +12,10 @@ repository = "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore"
readme = "README.md" readme = "README.md"
keywords = ["glob", "ignore", "gitignore", "pattern", "file"] keywords = ["glob", "ignore", "gitignore", "pattern", "file"]
license = "Unlicense OR MIT" license = "Unlicense OR MIT"
edition = "2021" # CHANGED: Use an explicit edition instead of `edition.workspace = true` since this crate is
# vendored into the Tailwind CSS workspace.
edition = "2024"
rust-version = "1.88"
[lib] [lib]
name = "ignore" name = "ignore"
@ -20,15 +23,17 @@ bench = false
[dependencies] [dependencies]
crossbeam-deque = "0.8.3" crossbeam-deque = "0.8.3"
globset = "0.4.16" # CHANGED: Use the published globset crate instead of a path dependency.
globset = "0.4.20"
log = "0.4.20" log = "0.4.20"
memchr = "2.6.3" memchr = "2.6.3"
same-file = "1.0.6" same-file = "1.0.6"
walkdir = "2.4.0" walkdir = "2.4.0"
# CHANGED: Added `dunce` to canonicalize paths without UNC prefixes on Windows.
dunce = "1.0.5" dunce = "1.0.5"
[dependencies.regex-automata] [dependencies.regex-automata]
version = "0.4.0" version = "0.4.18"
default-features = false default-features = false
features = ["std", "perf", "syntax", "meta", "nfa", "hybrid", "dfa-onepass"] features = ["std", "perf", "syntax", "meta", "nfa", "hybrid", "dfa-onepass"]
@ -37,7 +42,7 @@ version = "0.1.2"
[dev-dependencies] [dev-dependencies]
bstr = { version = "1.6.2", default-features = false, features = ["std"] } bstr = { version = "1.6.2", default-features = false, features = ["std"] }
crossbeam-channel = "0.5.8" crossbeam-channel = "0.5.15"
[features] [features]
# DEPRECATED. It is a no-op. SIMD is done automatically through runtime # DEPRECATED. It is a no-op. SIMD is done automatically through runtime

View file

@ -1,5 +1,5 @@
# ignore ignore
======
The ignore crate provides a fast recursive directory iterator that respects The ignore crate provides a fast recursive directory iterator that respects
various filters such as globs, file types and `.gitignore` files. This crate various filters such as globs, file types and `.gitignore` files. This crate
also provides lower level direct access to gitignore and file type matchers. also provides lower level direct access to gitignore and file type matchers.
@ -29,6 +29,7 @@ recursively traverse the current directory while automatically filtering out
files and directories according to ignore globs found in files like files and directories according to ignore globs found in files like
`.ignore` and `.gitignore`: `.ignore` and `.gitignore`:
```rust,no_run ```rust,no_run
use ignore::Walk; use ignore::Walk;

View file

@ -18,8 +18,8 @@ fn main() {
let stdout_thread = std::thread::spawn(move || { let stdout_thread = std::thread::spawn(move || {
let mut stdout = std::io::BufWriter::new(std::io::stdout()); let mut stdout = std::io::BufWriter::new(std::io::stdout());
for dent in rx { for dent in rx {
stdout.write(&*Vec::from_path_lossy(dent.path())).unwrap(); stdout.write_all(&Vec::from_path_lossy(dent.path())).unwrap();
stdout.write(b"\n").unwrap(); stdout.write_all(b"\n").unwrap();
} }
}); });

View file

@ -27,9 +27,10 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["bat", "batch"], &["*.bat"]), (&["bat", "batch"], &["*.bat"]),
(&["bazel"], &[ (&["bazel"], &[
"*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "MODULE.bazel", "*.bazel", "*.bzl", "*.BUILD", "*.bazelrc", "BUILD", "MODULE.bazel",
"WORKSPACE", "WORKSPACE.bazel", "WORKSPACE", "WORKSPACE.bazel", "WORKSPACE.bzlmod",
]), ]),
(&["bitbake"], &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]), (&["bitbake"], &["*.bb", "*.bbappend", "*.bbclass", "*.conf", "*.inc"]),
(&["boxlang"], &["*.bx", "*.bxm", "*.bxs"]),
(&["brotli"], &["*.br"]), (&["brotli"], &["*.br"]),
(&["buildstream"], &["*.bst"]), (&["buildstream"], &["*.bst"]),
(&["bzip2"], &["*.bz2", "*.tbz2"]), (&["bzip2"], &["*.bz2", "*.tbz2"]),
@ -39,12 +40,14 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["carp"], &["*.carp"]), (&["carp"], &["*.carp"]),
(&["cbor"], &["*.cbor"]), (&["cbor"], &["*.cbor"]),
(&["ceylon"], &["*.ceylon"]), (&["ceylon"], &["*.ceylon"]),
(&["cfml"], &["*.cfc", "*.cfm"]),
(&["clojure"], &["*.clj", "*.cljc", "*.cljs", "*.cljx"]), (&["clojure"], &["*.clj", "*.cljc", "*.cljs", "*.cljx"]),
(&["cmake"], &["*.cmake", "CMakeLists.txt"]), (&["cmake"], &["*.cmake", "CMakeLists.txt"]),
(&["cmd"], &["*.bat", "*.cmd"]), (&["cmd"], &["*.bat", "*.cmd"]),
(&["cml"], &["*.cml"]), (&["cml"], &["*.cml"]),
(&["coffeescript"], &["*.coffee"]), (&["coffeescript"], &["*.coffee"]),
(&["config"], &["*.cfg", "*.conf", "*.config", "*.ini"]), (&["config"], &["*.cfg", "*.conf", "*.config", "*.ini"]),
(&["container"], &["*Containerfile*", "*Dockerfile*"]),
(&["coq"], &["*.v"]), (&["coq"], &["*.v"]),
(&["cpp"], &[ (&["cpp"], &[
"*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh", "*.inl", "*.[ChH]", "*.cc", "*.[ch]pp", "*.[ch]xx", "*.hh", "*.inl",
@ -62,7 +65,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["cython"], &["*.pyx", "*.pxi", "*.pxd"]), (&["cython"], &["*.pyx", "*.pxi", "*.pxd"]),
(&["d"], &["*.d"]), (&["d"], &["*.d"]),
(&["dart"], &["*.dart"]), (&["dart"], &["*.dart"]),
(&["devicetree"], &["*.dts", "*.dtsi"]), (&["devicetree"], &["*.dts", "*.dtsi", "*.dtso"]),
(&["dhall"], &["*.dhall"]), (&["dhall"], &["*.dhall"]),
(&["diff"], &["*.patch", "*.diff"]), (&["diff"], &["*.patch", "*.diff"]),
(&["dita"], &["*.dita", "*.ditamap", "*.ditaval"]), (&["dita"], &["*.dita", "*.ditamap", "*.ditaval"]),
@ -88,6 +91,8 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["fsharp"], &["*.fs", "*.fsx", "*.fsi"]), (&["fsharp"], &["*.fs", "*.fsx", "*.fsi"]),
(&["fut"], &["*.fut"]), (&["fut"], &["*.fut"]),
(&["gap"], &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]), (&["gap"], &["*.g", "*.gap", "*.gi", "*.gd", "*.tst"]),
(&["gdscript"], &["*.gd"]),
(&["gleam"], &["*.gleam"]),
(&["gn"], &["*.gn", "*.gni"]), (&["gn"], &["*.gn", "*.gni"]),
(&["go"], &["*.go"]), (&["go"], &["*.go"]),
(&["gprbuild"], &["*.gpr"]), (&["gprbuild"], &["*.gpr"]),
@ -105,6 +110,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["hbs"], &["*.hbs"]), (&["hbs"], &["*.hbs"]),
(&["hs"], &["*.hs", "*.lhs"]), (&["hs"], &["*.hs", "*.lhs"]),
(&["html"], &["*.htm", "*.html", "*.ejs"]), (&["html"], &["*.htm", "*.html", "*.ejs"]),
(&["hurl"], &["*.hurl"]),
(&["hy"], &["*.hy"]), (&["hy"], &["*.hy"]),
(&["idris"], &["*.idr", "*.lidr"]), (&["idris"], &["*.idr", "*.lidr"]),
(&["janet"], &["*.janet"]), (&["janet"], &["*.janet"]),
@ -117,6 +123,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["julia"], &["*.jl"]), (&["julia"], &["*.jl"]),
(&["jupyter"], &["*.ipynb", "*.jpynb"]), (&["jupyter"], &["*.ipynb", "*.jpynb"]),
(&["k"], &["*.k"]), (&["k"], &["*.k"]),
(&["kconfig"], &["Kconfig", "Kconfig.*"]),
(&["kotlin"], &["*.kt", "*.kts"]), (&["kotlin"], &["*.kt", "*.kts"]),
(&["lean"], &["*.lean"]), (&["lean"], &["*.lean"]),
(&["less"], &["*.less"]), (&["less"], &["*.less"]),
@ -149,6 +156,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
]), ]),
(&["lilypond"], &["*.ly", "*.ily"]), (&["lilypond"], &["*.ly", "*.ily"]),
(&["lisp"], &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]), (&["lisp"], &["*.el", "*.jl", "*.lisp", "*.lsp", "*.sc", "*.scm"]),
(&["llvm"], &["*.ll"]),
(&["lock"], &["*.lock", "package-lock.json"]), (&["lock"], &["*.lock", "package-lock.json"]),
(&["log"], &["*.log"]), (&["log"], &["*.log"]),
(&["lua"], &["*.lua"]), (&["lua"], &["*.lua"]),
@ -159,6 +167,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
"[Gg][Nn][Uu]makefile", "[Mm]akefile", "[Gg][Nn][Uu]makefile", "[Mm]akefile",
"[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am", "[Gg][Nn][Uu]makefile.am", "[Mm]akefile.am",
"[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in", "[Gg][Nn][Uu]makefile.in", "[Mm]akefile.in",
"Makefile.*",
"*.mk", "*.mak" "*.mk", "*.mak"
]), ]),
(&["mako"], &["*.mako", "*.mao"]), (&["mako"], &["*.mako", "*.mao"]),
@ -178,10 +187,11 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["mint"], &["*.mint"]), (&["mint"], &["*.mint"]),
(&["mk"], &["mkfile"]), (&["mk"], &["mkfile"]),
(&["ml"], &["*.ml"]), (&["ml"], &["*.ml"]),
(&["mojo"], &["*.mojo"]),
(&["motoko"], &["*.mo"]), (&["motoko"], &["*.mo"]),
(&["msbuild"], &[ (&["msbuild"], &[
"*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets", "*.csproj", "*.fsproj", "*.vcxproj", "*.proj", "*.props", "*.targets",
"*.sln", "*.sln", "*.slnf"
]), ]),
(&["nim"], &["*.nim", "*.nimf", "*.nimble", "*.nims"]), (&["nim"], &["*.nim", "*.nimf", "*.nimble", "*.nims"]),
(&["nix"], &["*.nix"]), (&["nix"], &["*.nix"]),
@ -199,18 +209,21 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
"*.php", "*.php3", "*.php4", "*.php5", "*.php7", "*.php8", "*.php", "*.php3", "*.php4", "*.php5", "*.php7", "*.php8",
"*.pht", "*.phtml" "*.pht", "*.phtml"
]), ]),
(&["pkgbuild"], &["PKGBUILD"]),
(&["po"], &["*.po"]), (&["po"], &["*.po"]),
(&["pod"], &["*.pod"]), (&["pod"], &["*.pod"]),
(&["postscript"], &["*.eps", "*.ps"]), (&["postscript"], &["*.eps", "*.ps"]),
(&["prolog"], &["*.pl", "*.pro", "*.prolog", "*.P"]), (&["prolog"], &["*.pl", "*.pro", "*.prolog", "*.P"]),
(&["protobuf"], &["*.proto"]), (&["proto", "protobuf"], &["*.proto"]),
(&["ps"], &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]), (&["ps"], &["*.cdxml", "*.ps1", "*.ps1xml", "*.psd1", "*.psm1"]),
(&["puppet"], &["*.epp", "*.erb", "*.pp", "*.rb"]), (&["puppet"], &["*.epp", "*.erb", "*.pp", "*.rb"]),
(&["purs"], &["*.purs"]), (&["purs"], &["*.purs"]),
(&["py", "python"], &["*.py", "*.pyi"]), (&["py", "python"], &["*.py", "*.pyi"]),
(&["qmake"], &["*.pro", "*.pri", "*.prf"]), (&["qmake"], &["*.pro", "*.pri", "*.prf"]),
(&["qml"], &["*.qml"]), (&["qml"], &["*.qml"]),
(&["r"], &["*.R", "*.r", "*.Rmd", "*.Rnw"]), (&["qrc"], &["*.qrc"]),
(&["qui"], &["*.ui"]),
(&["r"], &["*.R", "*.r", "*.Rmd", "*.rmd", "*.Rnw", "*.rnw"]),
(&["racket"], &["*.rkt"]), (&["racket"], &["*.rkt"]),
(&["raku"], &[ (&["raku"], &[
"*.raku", "*.rakumod", "*.rakudoc", "*.rakutest", "*.raku", "*.rakumod", "*.rakudoc", "*.rakutest",
@ -222,19 +235,22 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["red"], &["*.r", "*.red", "*.reds"]), (&["red"], &["*.r", "*.red", "*.reds"]),
(&["rescript"], &["*.res", "*.resi"]), (&["rescript"], &["*.res", "*.resi"]),
(&["robot"], &["*.robot"]), (&["robot"], &["*.robot"]),
(&["rocq"], &["*.v"]),
(&["rst"], &["*.rst"]), (&["rst"], &["*.rst"]),
(&["ruby"], &[ (&["ruby"], &[
// Idiomatic files // Idiomatic files
"config.ru", "Gemfile", ".irbrc", "Rakefile", "config.ru", "Gemfile", ".irbrc", "Rakefile",
// Extensions // Extensions
"*.gemspec", "*.rb", "*.rbw" "*.gemspec", "*.rb", "*.rbw", "*.rake"
]), ]),
(&["rust"], &["*.rs"]), (&["rust"], &["*.rs"]),
(&["sass"], &["*.sass", "*.scss"]), (&["sass"], &["*.sass", "*.scss"]),
(&["scala"], &["*.scala", "*.sbt"]), (&["scala"], &["*.scala", "*.sbt"]),
(&["scdoc"], &["*.scd", "*.scdoc"]),
(&["seed7"], &["*.sd7", "*.s7i"]),
(&["sh"], &[ (&["sh"], &[
// Portable/misc. init files // Portable/misc. init files
".login", ".logout", ".profile", "profile", ".env", ".login", ".logout", ".profile", "profile",
// bash-specific init files // bash-specific init files
".bash_login", "bash_login", ".bash_login", "bash_login",
".bash_logout", "bash_logout", ".bash_logout", "bash_logout",
@ -253,7 +269,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
".zprofile", "zprofile", ".zprofile", "zprofile",
".zshrc", "zshrc", ".zshrc", "zshrc",
// Extensions // Extensions
"*.bash", "*.csh", "*.ksh", "*.sh", "*.tcsh", "*.zsh", "*.bash", "*.csh", "*.env", "*.ksh", "*.sh", "*.tcsh", "*.zsh",
]), ]),
(&["slim"], &["*.skim", "*.slim", "*.slime"]), (&["slim"], &["*.skim", "*.slim", "*.slime"]),
(&["smarty"], &["*.tpl"]), (&["smarty"], &["*.tpl"]),
@ -263,9 +279,10 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["spark"], &["*.spark"]), (&["spark"], &["*.spark"]),
(&["spec"], &["*.spec"]), (&["spec"], &["*.spec"]),
(&["sql"], &["*.sql", "*.psql"]), (&["sql"], &["*.sql", "*.psql"]),
(&["ssa"], &["*.ssa"]),
(&["stylus"], &["*.styl"]), (&["stylus"], &["*.styl"]),
(&["sv"], &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]), (&["sv"], &["*.v", "*.vg", "*.sv", "*.svh", "*.h"]),
(&["svelte"], &["*.svelte"]), (&["svelte"], &["*.svelte", "*.svelte.ts"]),
(&["svg"], &["*.svg"]), (&["svg"], &["*.svg"]),
(&["swift"], &["*.swift"]), (&["swift"], &["*.swift"]),
(&["swig"], &["*.def", "*.i"]), (&["swig"], &["*.def", "*.i"]),
@ -280,9 +297,8 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["texinfo"], &["*.texi"]), (&["texinfo"], &["*.texi"]),
(&["textile"], &["*.textile"]), (&["textile"], &["*.textile"]),
(&["tf"], &[ (&["tf"], &[
"*.tf", "*.auto.tfvars", "terraform.tfvars", "*.tf.json", "*.tf", "*.tf.json", "*.tfvars", "*.tfvars.json",
"*.auto.tfvars.json", "terraform.tfvars.json", "*.terraformrc", "*.terraformrc", "terraform.rc", "*.tfrc", "*.terraform.lock.hcl",
"terraform.rc", "*.tfrc", "*.terraform.lock.hcl",
]), ]),
(&["thrift"], &["*.thrift"]), (&["thrift"], &["*.thrift"]),
(&["toml"], &["*.toml", "Cargo.lock"]), (&["toml"], &["*.toml", "Cargo.lock"]),
@ -290,6 +306,7 @@ pub(crate) const DEFAULT_TYPES: &[(&[&str], &[&str])] = &[
(&["twig"], &["*.twig"]), (&["twig"], &["*.twig"]),
(&["txt"], &["*.txt"]), (&["txt"], &["*.txt"]),
(&["typoscript"], &["*.typoscript", "*.ts"]), (&["typoscript"], &["*.typoscript", "*.ts"]),
(&["typst"], &["*.typ"]),
(&["usd"], &["*.usd", "*.usda", "*.usdc"]), (&["usd"], &["*.usd", "*.usda", "*.usdc"]),
(&["v"], &["*.v", "*.vsh"]), (&["v"], &["*.v", "*.vsh"]),
(&["vala"], &["*.vala"]), (&["vala"], &["*.vala"]),
@ -348,4 +365,14 @@ mod tests {
previous_name = name; previous_name = name;
} }
} }
#[test]
fn default_types_aliases_are_sorted() {
for (aliases, _) in DEFAULT_TYPES.iter() {
assert!(
aliases.is_sorted(),
"this alias list is not sorted: {aliases:?}",
);
}
}
} }

File diff suppressed because it is too large Load diff

View file

@ -20,8 +20,8 @@ use {
}; };
use crate::{ use crate::{
pathutil::{is_file_name, strip_prefix},
Error, Match, PartialErrorBuilder, Error, Match, PartialErrorBuilder,
pathutil::{is_file_name, strip_prefix},
}; };
/// Glob represents a single glob in a gitignore file. /// Glob represents a single glob in a gitignore file.
@ -85,6 +85,8 @@ pub struct Gitignore {
num_ignores: u64, num_ignores: u64,
num_whitelists: u64, num_whitelists: u64,
matches: Option<Arc<Pool<Vec<usize>>>>, matches: Option<Arc<Pool<Vec<usize>>>>,
// CHANGED: Add a flag to have Gitignore rules that apply only to files.
only_on_files: bool,
} }
impl Gitignore { impl Gitignore {
@ -100,7 +102,9 @@ impl Gitignore {
/// ///
/// Note that I/O errors are ignored. For more granular control over /// Note that I/O errors are ignored. For more granular control over
/// errors, use `GitignoreBuilder`. /// errors, use `GitignoreBuilder`.
pub fn new<P: AsRef<Path>>(gitignore_path: P) -> (Gitignore, Option<Error>) { pub fn new<P: AsRef<Path>>(
gitignore_path: P,
) -> (Gitignore, Option<Error>) {
let path = gitignore_path.as_ref(); let path = gitignore_path.as_ref();
let parent = path.parent().unwrap_or(Path::new("/")); let parent = path.parent().unwrap_or(Path::new("/"));
let mut builder = GitignoreBuilder::new(parent); let mut builder = GitignoreBuilder::new(parent);
@ -121,12 +125,26 @@ impl Gitignore {
/// The global config file path is specified by git's `core.excludesFile` /// The global config file path is specified by git's `core.excludesFile`
/// config option. /// config option.
/// ///
/// # Behavior
///
/// This routine does its best to discover any global git exclude files.
/// This will try to parse out the `excludesFile` config option in your
/// global git configuration, if necessary.
///
/// The specific things this routine tries (which are subject to change
/// based on how git behaves) are:
///
///
///
/// Git's config file location is `$HOME/.gitconfig`. If `$HOME/.gitconfig` /// Git's config file location is `$HOME/.gitconfig`. If `$HOME/.gitconfig`
/// does not exist or does not specify `core.excludesFile`, then /// does not exist or does not specify `core.excludesFile`, then
/// `$XDG_CONFIG_HOME/git/ignore` is read. If `$XDG_CONFIG_HOME` is not /// `$XDG_CONFIG_HOME/git/ignore` is read. If `$XDG_CONFIG_HOME` is not
/// set or is empty, then `$HOME/.config/git/ignore` is used instead. /// set or is empty, then `$HOME/.config/git/ignore` is used instead.
pub fn global() -> (Gitignore, Option<Error>) { pub fn global() -> (Gitignore, Option<Error>) {
GitignoreBuilder::new("").build_global() match std::env::current_dir() {
Ok(cwd) => GitignoreBuilder::new(cwd).build_global(),
Err(err) => (Gitignore::empty(), Some(err.into())),
}
} }
/// Creates a new empty gitignore matcher that never matches anything. /// Creates a new empty gitignore matcher that never matches anything.
@ -140,6 +158,9 @@ impl Gitignore {
num_ignores: 0, num_ignores: 0,
num_whitelists: 0, num_whitelists: 0,
matches: None, matches: None,
// CHANGED: Add a flag to have Gitignore rules that apply only to
// files.
only_on_files: false,
} }
} }
@ -183,7 +204,11 @@ impl Gitignore {
/// determined by a common suffix of the directory containing this /// determined by a common suffix of the directory containing this
/// gitignore) is stripped. If there is no common suffix/prefix overlap, /// gitignore) is stripped. If there is no common suffix/prefix overlap,
/// then `path` is assumed to be relative to this matcher. /// then `path` is assumed to be relative to this matcher.
pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match<&Glob> { pub fn matched<P: AsRef<Path>>(
&self,
path: P,
is_dir: bool,
) -> Match<&Glob> {
if self.is_empty() { if self.is_empty() {
return Match::None; return Match::None;
} }
@ -236,10 +261,19 @@ impl Gitignore {
} }
/// Like matched, but takes a path that has already been stripped. /// Like matched, but takes a path that has already been stripped.
fn matched_stripped<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match<&Glob> { fn matched_stripped<P: AsRef<Path>>(
&self,
path: P,
is_dir: bool,
) -> Match<&Glob> {
if self.is_empty() { if self.is_empty() {
return Match::None; return Match::None;
} }
// CHANGED: Rules marked as only_on_files can not match against
// directories.
if self.only_on_files && is_dir {
return Match::None;
}
let path = path.as_ref(); let path = path.as_ref();
let mut matches = self.matches.as_ref().unwrap().get(); let mut matches = self.matches.as_ref().unwrap().get();
let candidate = Candidate::new(path); let candidate = Candidate::new(path);
@ -259,7 +293,10 @@ impl Gitignore {
/// Strips the given path such that it's suitable for matching with this /// Strips the given path such that it's suitable for matching with this
/// gitignore matcher. /// gitignore matcher.
fn strip<'a, P: 'a + AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> &'a Path { fn strip<'a, P: 'a + AsRef<Path> + ?Sized>(
&'a self,
path: &'a P,
) -> &'a Path {
let mut path = path.as_ref(); let mut path = path.as_ref();
// A leading ./ is completely superfluous. We also strip it from // A leading ./ is completely superfluous. We also strip it from
// our gitignore root path, so we need to strip it from our candidate // our gitignore root path, so we need to strip it from our candidate
@ -295,6 +332,9 @@ pub struct GitignoreBuilder {
root: PathBuf, root: PathBuf,
globs: Vec<Glob>, globs: Vec<Glob>,
case_insensitive: bool, case_insensitive: bool,
allow_unclosed_class: bool,
// CHANGED: Add a flag to have Gitignore rules that apply only to files.
only_on_files: bool,
} }
impl GitignoreBuilder { impl GitignoreBuilder {
@ -311,6 +351,10 @@ impl GitignoreBuilder {
root: strip_prefix("./", root).unwrap_or(root).to_path_buf(), root: strip_prefix("./", root).unwrap_or(root).to_path_buf(),
globs: vec![], globs: vec![],
case_insensitive: false, case_insensitive: false,
allow_unclosed_class: true,
// CHANGED: Add a flag to have Gitignore rules that apply only to
// files.
only_on_files: false,
} }
} }
@ -320,17 +364,22 @@ impl GitignoreBuilder {
pub fn build(&self) -> Result<Gitignore, Error> { pub fn build(&self) -> Result<Gitignore, Error> {
let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count(); let nignore = self.globs.iter().filter(|g| !g.is_whitelist()).count();
let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count(); let nwhite = self.globs.iter().filter(|g| g.is_whitelist()).count();
let set = self.builder.build().map_err(|err| Error::Glob { let set = self
glob: None, .builder
err: err.to_string(), .build()
})?; .map_err(|err| Error::Glob { glob: None, err: err.to_string() })?;
Ok(Gitignore { Ok(Gitignore {
set, set,
root: self.root.clone(), root: self.root.clone(),
globs: self.globs.clone(), globs: self.globs.clone(),
num_ignores: nignore as u64, num_ignores: nignore as u64,
num_whitelists: nwhite as u64, num_whitelists: nwhite as u64,
matches: Some(Arc::new(Pool::new(|| vec![]))), matches: Some(Arc::new(
Pool::with_available_parallelism_capacity(|| vec![]),
)),
// CHANGED: Add a flag to have Gitignore rules that apply only to
// files.
only_on_files: self.only_on_files,
}) })
} }
@ -389,6 +438,12 @@ impl GitignoreBuilder {
break; break;
} }
}; };
// Match Git's handling of .gitignore files that begin with the Unicode BOM
const UTF8_BOM: &str = "\u{feff}";
let line =
if i == 0 { line.trim_start_matches(UTF8_BOM) } else { &line };
if let Err(err) = self.add_line(Some(path.to_path_buf()), &line) { if let Err(err) = self.add_line(Some(path.to_path_buf()), &line) {
errs.push(err.tagged(path, lineno)); errs.push(err.tagged(path, lineno));
} }
@ -492,6 +547,7 @@ impl GitignoreBuilder {
.literal_separator(true) .literal_separator(true)
.case_insensitive(self.case_insensitive) .case_insensitive(self.case_insensitive)
.backslash_escape(true) .backslash_escape(true)
.allow_unclosed_class(self.allow_unclosed_class)
.build() .build()
.map_err(|err| Error::Glob { .map_err(|err| Error::Glob {
glob: Some(glob.original.clone()), glob: Some(glob.original.clone()),
@ -508,44 +564,101 @@ impl GitignoreBuilder {
/// affected. /// affected.
/// ///
/// This is disabled by default. /// This is disabled by default.
pub fn case_insensitive(&mut self, yes: bool) -> Result<&mut GitignoreBuilder, Error> { pub fn case_insensitive(
&mut self,
yes: bool,
) -> Result<&mut GitignoreBuilder, Error> {
// TODO: This should not return a `Result`. Fix this in the next semver // TODO: This should not return a `Result`. Fix this in the next semver
// release. // release.
self.case_insensitive = yes; self.case_insensitive = yes;
Ok(self) Ok(self)
} }
/// Toggle whether unclosed character classes are allowed. When allowed,
/// a `[` without a matching `]` is treated literally instead of resulting
/// in a parse error.
///
/// For example, if this is set then the glob `[abc` will be treated as the
/// literal string `[abc` instead of returning an error.
///
/// By default, this is true in order to match established `gitignore`
/// semantics. Generally speaking, enabling this leads to worse failure
/// modes since the glob parser becomes more permissive. You might want to
/// enable this when compatibility (e.g., with POSIX glob implementations)
/// is more important than good error messages.
pub fn allow_unclosed_class(
&mut self,
yes: bool,
) -> &mut GitignoreBuilder {
self.allow_unclosed_class = yes;
self
}
/// CHANGED: Add a flag to have Gitignore rules that apply only to files.
///
/// If this is set, then the globs will only be matched against file paths.
/// This will ensure that ignore rules like `*.pages` will _only_ ignore
/// files ending in `.pages` and not folders ending in `.pages`.
pub fn only_on_files(&mut self, yes: bool) -> &mut GitignoreBuilder {
self.only_on_files = yes;
self
}
} }
/// Return the file path of the current environment's global gitignore file. /// Return the file path of the current environment's global gitignore file.
/// ///
/// Note that the file path returned may not exist. /// Note that the file path returned may not exist.
pub fn gitconfig_excludes_path() -> Option<PathBuf> { pub fn gitconfig_excludes_path() -> Option<PathBuf> {
// git supports $HOME/.gitconfig and $XDG_CONFIG_HOME/git/config. Notably, // When GIT_CONFIG_GLOBAL is set, it replaces both $HOME/.gitconfig and
// both can be active at the same time, where $HOME/.gitconfig takes // $XDG_CONFIG_HOME/git/config (per git 2.32+). Otherwise, git supports
// precedent. So if $HOME/.gitconfig defines a `core.excludesFile`, then // $HOME/.gitconfig and $XDG_CONFIG_HOME/git/config simultaneously, where
// we're done. // $HOME/.gitconfig takes precedent.
match gitconfig_home_contents().and_then(|x| parse_excludes_file(&x)) { gitconfig_global_env_contents()
Some(path) => return Some(path), .and_then(|x| parse_excludes_file(&x))
None => {} .or_else(|| {
gitconfig_home_contents().and_then(|x| parse_excludes_file(&x))
})
.or_else(|| {
gitconfig_xdg_contents().and_then(|x| parse_excludes_file(&x))
})
// System-level config has the lowest priority for core.excludesFile.
// GIT_CONFIG_SYSTEM overrides the default /etc/gitconfig path.
.or_else(|| {
gitconfig_system_contents().and_then(|x| parse_excludes_file(&x))
})
.or_else(excludes_file_default)
}
/// Returns the file contents of git's global config file from the path
/// specified by the `GIT_CONFIG_GLOBAL` environment variable.
fn gitconfig_global_env_contents() -> Option<Vec<u8>> {
let path = std::env::var_os("GIT_CONFIG_GLOBAL").map(PathBuf::from)?;
if path.as_os_str().is_empty() {
return None;
} }
match gitconfig_xdg_contents().and_then(|x| parse_excludes_file(&x)) { let mut file = BufReader::new(File::open(path).ok()?);
Some(path) => return Some(path), let mut contents = vec![];
None => {} file.read_to_end(&mut contents).ok().map(|_| contents)
} }
excludes_file_default()
/// Returns the file contents of git's system-level config file.
///
/// Checks `GIT_CONFIG_SYSTEM` first, then falls back to `/etc/gitconfig`.
fn gitconfig_system_contents() -> Option<Vec<u8>> {
let path = std::env::var_os("GIT_CONFIG_SYSTEM")
.map(PathBuf::from)
.filter(|x| !x.as_os_str().is_empty())
.unwrap_or_else(|| PathBuf::from("/etc/gitconfig"));
let mut file = BufReader::new(File::open(path).ok()?);
let mut contents = vec![];
file.read_to_end(&mut contents).ok().map(|_| contents)
} }
/// Returns the file contents of git's global config file, if one exists, in /// Returns the file contents of git's global config file, if one exists, in
/// the user's home directory. /// the user's home directory.
fn gitconfig_home_contents() -> Option<Vec<u8>> { fn gitconfig_home_contents() -> Option<Vec<u8>> {
let home = match home_dir() { let home = home_dir()?;
None => return None, let mut file = BufReader::new(File::open(home.join(".gitconfig")).ok()?);
Some(home) => home,
};
let mut file = match File::open(home.join(".gitconfig")) {
Err(_) => return None,
Ok(file) => BufReader::new(file),
};
let mut contents = vec![]; let mut contents = vec![];
file.read_to_end(&mut contents).ok().map(|_| contents) file.read_to_end(&mut contents).ok().map(|_| contents)
} }
@ -554,19 +667,11 @@ fn gitconfig_home_contents() -> Option<Vec<u8>> {
/// the user's XDG_CONFIG_HOME directory. /// the user's XDG_CONFIG_HOME directory.
fn gitconfig_xdg_contents() -> Option<Vec<u8>> { fn gitconfig_xdg_contents() -> Option<Vec<u8>> {
let path = std::env::var_os("XDG_CONFIG_HOME") let path = std::env::var_os("XDG_CONFIG_HOME")
.and_then(|x| { .map(PathBuf::from)
if x.is_empty() { .filter(|x| !x.as_os_str().is_empty())
None
} else {
Some(PathBuf::from(x))
}
})
.or_else(|| home_dir().map(|p| p.join(".config"))) .or_else(|| home_dir().map(|p| p.join(".config")))
.map(|x| x.join("git/config")); .map(|x| x.join("git/config"))?;
let mut file = match path.and_then(|p| File::open(p).ok()) { let mut file = BufReader::new(File::open(path).ok()?);
None => return None,
Some(file) => BufReader::new(file),
};
let mut contents = vec![]; let mut contents = vec![];
file.read_to_end(&mut contents).ok().map(|_| contents) file.read_to_end(&mut contents).ok().map(|_| contents)
} }
@ -576,13 +681,8 @@ fn gitconfig_xdg_contents() -> Option<Vec<u8>> {
/// Specifically, this respects XDG_CONFIG_HOME. /// Specifically, this respects XDG_CONFIG_HOME.
fn excludes_file_default() -> Option<PathBuf> { fn excludes_file_default() -> Option<PathBuf> {
std::env::var_os("XDG_CONFIG_HOME") std::env::var_os("XDG_CONFIG_HOME")
.and_then(|x| { .map(PathBuf::from)
if x.is_empty() { .filter(|x| !x.as_os_str().is_empty())
None
} else {
Some(PathBuf::from(x))
}
})
.or_else(|| home_dir().map(|p| p.join(".config"))) .or_else(|| home_dir().map(|p| p.join(".config")))
.map(|x| x.join("git/ignore")) .map(|x| x.join("git/ignore"))
} }
@ -611,9 +711,7 @@ fn parse_excludes_file(data: &[u8]) -> Option<PathBuf> {
re.captures(data, &mut caps); re.captures(data, &mut caps);
let span = caps.get_group(1)?; let span = caps.get_group(1)?;
let candidate = &data[span]; let candidate = &data[span];
std::str::from_utf8(candidate) std::str::from_utf8(candidate).ok().map(|s| PathBuf::from(expand_tilde(s)))
.ok()
.map(|s| PathBuf::from(expand_tilde(s)))
} }
/// Expands ~ in file paths to the value of $HOME. /// Expands ~ in file paths to the value of $HOME.
@ -775,7 +873,10 @@ mod tests {
fn parse_excludes_file4() { fn parse_excludes_file4() {
let data = bytes("[core]\nexcludesFile = \"~/foo/bar\""); let data = bytes("[core]\nexcludesFile = \"~/foo/bar\"");
let got = super::parse_excludes_file(&data); let got = super::parse_excludes_file(&data);
assert_eq!(path_string(got.unwrap()), super::expand_tilde("~/foo/bar")); assert_eq!(
path_string(got.unwrap()),
super::expand_tilde("~/foo/bar")
);
} }
#[test] #[test]

File diff suppressed because it is too large Load diff

View file

@ -48,13 +48,16 @@ See the documentation for `WalkBuilder` for many other options.
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
pub use crate::incremental::{IncrementalIgnore, IncrementalMatch};
pub use crate::walk::{ pub use crate::walk::{
DirEntry, ParallelVisitor, ParallelVisitorBuilder, Walk, WalkBuilder, WalkParallel, WalkState, DirEntry, ParallelVisitor, ParallelVisitorBuilder, Walk, WalkBuilder,
WalkParallel, WalkState,
}; };
mod default_types; mod default_types;
mod dir; mod dir;
pub mod gitignore; pub mod gitignore;
mod incremental;
pub mod overrides; pub mod overrides;
mod pathutil; mod pathutil;
pub mod types; pub mod types;
@ -120,34 +123,31 @@ impl Clone for Error {
fn clone(&self) -> Error { fn clone(&self) -> Error {
match *self { match *self {
Error::Partial(ref errs) => Error::Partial(errs.clone()), Error::Partial(ref errs) => Error::Partial(errs.clone()),
Error::WithLineNumber { line, ref err } => Error::WithLineNumber { Error::WithLineNumber { line, ref err } => {
line, Error::WithLineNumber { line, err: err.clone() }
err: err.clone(), }
}, Error::WithPath { ref path, ref err } => {
Error::WithPath { ref path, ref err } => Error::WithPath { Error::WithPath { path: path.clone(), err: err.clone() }
path: path.clone(), }
err: err.clone(), Error::WithDepth { depth, ref err } => {
}, Error::WithDepth { depth, err: err.clone() }
Error::WithDepth { depth, ref err } => Error::WithDepth { }
depth, Error::Loop { ref ancestor, ref child } => Error::Loop {
err: err.clone(),
},
Error::Loop {
ref ancestor,
ref child,
} => Error::Loop {
ancestor: ancestor.clone(), ancestor: ancestor.clone(),
child: child.clone(), child: child.clone(),
}, },
Error::Io(ref err) => match err.raw_os_error() { Error::Io(ref err) => match err.raw_os_error() {
Some(e) => Error::Io(std::io::Error::from_raw_os_error(e)), Some(e) => Error::Io(std::io::Error::from_raw_os_error(e)),
None => Error::Io(std::io::Error::new(err.kind(), err.to_string())), None => {
Error::Io(std::io::Error::new(err.kind(), err.to_string()))
}
}, },
Error::Glob { ref glob, ref err } => Error::Glob { Error::Glob { ref glob, ref err } => {
glob: glob.clone(), Error::Glob { glob: glob.clone(), err: err.clone() }
err: err.clone(), }
}, Error::UnrecognizedFileType(ref err) => {
Error::UnrecognizedFileType(ref err) => Error::UnrecognizedFileType(err.clone()), Error::UnrecognizedFileType(err.clone())
}
Error::InvalidDefinition => Error::InvalidDefinition, Error::InvalidDefinition => Error::InvalidDefinition,
} }
} }
@ -269,19 +269,14 @@ impl Error {
/// Turn an error into a tagged error with the given depth. /// Turn an error into a tagged error with the given depth.
fn with_depth(self, depth: usize) -> Error { fn with_depth(self, depth: usize) -> Error {
Error::WithDepth { Error::WithDepth { depth, err: Box::new(self) }
depth,
err: Box::new(self),
}
} }
/// Turn an error into a tagged error with the given file path and line /// Turn an error into a tagged error with the given file path and line
/// number. If path is empty, then it is omitted from the error. /// number. If path is empty, then it is omitted from the error.
fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error { fn tagged<P: AsRef<Path>>(self, path: P, lineno: u64) -> Error {
let errline = Error::WithLineNumber { let errline =
line: lineno, Error::WithLineNumber { line: lineno, err: Box::new(self) };
err: Box::new(self),
};
if path.as_ref().as_os_str().is_empty() { if path.as_ref().as_os_str().is_empty() {
return errline; return errline;
} }
@ -301,12 +296,12 @@ impl Error {
}; };
} }
let path = err.path().map(|p| p.to_path_buf()); let path = err.path().map(|p| p.to_path_buf());
let mut ig_err = Error::Io(std::io::Error::from(err)); let mut ig_err = Error::WithDepth {
depth,
err: Box::new(Error::Io(std::io::Error::from(err))),
};
if let Some(path) = path { if let Some(path) = path {
ig_err = Error::WithPath { ig_err = Error::WithPath { path, err: Box::new(ig_err) };
path,
err: Box::new(ig_err),
};
} }
ig_err ig_err
} }
@ -333,7 +328,8 @@ impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self { match *self {
Error::Partial(ref errs) => { Error::Partial(ref errs) => {
let msgs: Vec<String> = errs.iter().map(|err| err.to_string()).collect(); let msgs: Vec<String> =
errs.iter().map(|err| err.to_string()).collect();
write!(f, "{}", msgs.join("\n")) write!(f, "{}", msgs.join("\n"))
} }
Error::WithLineNumber { line, ref err } => { Error::WithLineNumber { line, ref err } => {
@ -343,10 +339,7 @@ impl std::fmt::Display for Error {
write!(f, "{}: {}", path.display(), err) write!(f, "{}: {}", path.display(), err)
} }
Error::WithDepth { ref err, .. } => err.fmt(f), Error::WithDepth { ref err, .. } => err.fmt(f),
Error::Loop { Error::Loop { ref ancestor, ref child } => write!(
ref ancestor,
ref child,
} => write!(
f, f,
"File system loop found: \ "File system loop found: \
{} points to an ancestor {}", {} points to an ancestor {}",
@ -354,14 +347,8 @@ impl std::fmt::Display for Error {
ancestor.display() ancestor.display()
), ),
Error::Io(ref err) => err.fmt(f), Error::Io(ref err) => err.fmt(f),
Error::Glob { Error::Glob { glob: None, ref err } => write!(f, "{}", err),
glob: None, Error::Glob { glob: Some(ref glob), ref err } => {
ref err,
} => write!(f, "{}", err),
Error::Glob {
glob: Some(ref glob),
ref err,
} => {
write!(f, "error parsing glob '{}': {}", glob, err) write!(f, "error parsing glob '{}': {}", glob, err)
} }
Error::UnrecognizedFileType(ref ty) => { Error::UnrecognizedFileType(ref ty) => {
@ -495,11 +482,7 @@ impl<T> Match<T> {
/// Return the match if it is not none. Otherwise, return other. /// Return the match if it is not none. Otherwise, return other.
pub fn or(self, other: Self) -> Self { pub fn or(self, other: Self) -> Self {
if self.is_none() { if self.is_none() { other } else { self }
other
} else {
self
}
} }
} }
@ -511,7 +494,8 @@ mod tests {
}; };
/// A convenient result type alias. /// A convenient result type alias.
pub(crate) type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; pub(crate) type Result<T> =
std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
macro_rules! err { macro_rules! err {
($($tt:tt)*) => { ($($tt:tt)*) => {
@ -544,13 +528,14 @@ mod tests {
let tmpdir = env::temp_dir(); let tmpdir = env::temp_dir();
for _ in 0..TRIES { for _ in 0..TRIES {
let count = COUNTER.fetch_add(1, Ordering::SeqCst); let count = COUNTER.fetch_add(1, Ordering::Relaxed);
let path = tmpdir.join("rust-ignore").join(count.to_string()); let path = tmpdir.join("rust-ignore").join(count.to_string());
if path.is_dir() { if path.is_dir() {
continue; continue;
} }
fs::create_dir_all(&path) fs::create_dir_all(&path).map_err(|e| {
.map_err(|e| err!("failed to create {}: {}", path.display(), e))?; err!("failed to create {}: {}", path.display(), e)
})?;
return Ok(TempDir(path)); return Ok(TempDir(path));
} }
Err(err!("failed to create temp dir after {} tries", TRIES)) Err(err!("failed to create temp dir after {} tries", TRIES))

View file

@ -1,5 +1,6 @@
/*! /*!
The overrides module provides a way to specify a set of override globs. The overrides module provides a way to specify a set of override globs.
This provides functionality similar to `--include` or `--exclude` in command This provides functionality similar to `--include` or `--exclude` in command
line tools. line tools.
*/ */
@ -7,8 +8,8 @@ line tools.
use std::path::Path; use std::path::Path;
use crate::{ use crate::{
gitignore::{self, Gitignore, GitignoreBuilder},
Error, Match, Error, Match,
gitignore::{self, Gitignore, GitignoreBuilder},
}; };
/// Glob represents a single glob in an override matcher. /// Glob represents a single glob in an override matcher.
@ -93,7 +94,11 @@ impl Override {
/// given) is stripped. If there is no common suffix/prefix overlap, then /// given) is stripped. If there is no common suffix/prefix overlap, then
/// `path` is assumed to reside in the same directory as the root path for /// `path` is assumed to reside in the same directory as the root path for
/// this set of overrides. /// this set of overrides.
pub fn matched<'a, P: AsRef<Path>>(&'a self, path: P, is_dir: bool) -> Match<Glob<'a>> { pub fn matched<'a, P: AsRef<Path>>(
&'a self,
path: P,
is_dir: bool,
) -> Match<Glob<'a>> {
if self.is_empty() { if self.is_empty() {
return Match::None; return Match::None;
} }
@ -116,9 +121,9 @@ impl OverrideBuilder {
/// ///
/// Matching is done relative to the directory path provided. /// Matching is done relative to the directory path provided.
pub fn new<P: AsRef<Path>>(path: P) -> OverrideBuilder { pub fn new<P: AsRef<Path>>(path: P) -> OverrideBuilder {
OverrideBuilder { let mut builder = GitignoreBuilder::new(path);
builder: GitignoreBuilder::new(path), builder.allow_unclosed_class(false);
} OverrideBuilder { builder }
} }
/// Builds a new override matcher from the globs added so far. /// Builds a new override matcher from the globs added so far.
@ -141,15 +146,41 @@ impl OverrideBuilder {
/// Toggle whether the globs should be matched case insensitively or not. /// Toggle whether the globs should be matched case insensitively or not.
/// ///
/// When this option is changed, only globs added after the change will be affected. /// When this option is changed, only globs added after the change will be
/// affected.
/// ///
/// This is disabled by default. /// This is disabled by default.
pub fn case_insensitive(&mut self, yes: bool) -> Result<&mut OverrideBuilder, Error> { pub fn case_insensitive(
&mut self,
yes: bool,
) -> Result<&mut OverrideBuilder, Error> {
// TODO: This should not return a `Result`. Fix this in the next semver // TODO: This should not return a `Result`. Fix this in the next semver
// release. // release.
self.builder.case_insensitive(yes)?; self.builder.case_insensitive(yes)?;
Ok(self) Ok(self)
} }
/// Toggle whether unclosed character classes are allowed. When allowed,
/// a `[` without a matching `]` is treated literally instead of resulting
/// in a parse error.
///
/// For example, if this is set then the glob `[abc` will be treated as the
/// literal string `[abc` instead of returning an error.
///
/// By default, this is false. Generally speaking, enabling this leads to
/// worse failure modes since the glob parser becomes more permissive. You
/// might want to enable this when compatibility (e.g., with POSIX glob
/// implementations) is more important than good error messages.
///
/// This default is different from the default for [`Gitignore`]. Namely,
/// [`Gitignore`] is intended to match git's behavior as-is. But this
/// abstraction for "override" globs does not necessarily conform to any
/// other known specification and instead prioritizes better error
/// messages.
pub fn allow_unclosed_class(&mut self, yes: bool) -> &mut OverrideBuilder {
self.builder.allow_unclosed_class(yes);
self
}
} }
#[cfg(test)] #[cfg(test)]
@ -252,11 +283,8 @@ mod tests {
#[test] #[test]
fn default_case_sensitive() { fn default_case_sensitive() {
let ov = OverrideBuilder::new(ROOT) let ov =
.add("*.html") OverrideBuilder::new(ROOT).add("*.html").unwrap().build().unwrap();
.unwrap()
.build()
.unwrap();
assert!(ov.matched("foo.html", false).is_whitelist()); assert!(ov.matched("foo.html", false).is_whitelist());
assert!(ov.matched("foo.HTML", false).is_ignore()); assert!(ov.matched("foo.HTML", false).is_ignore());
assert!(ov.matched("foo.htm", false).is_ignore()); assert!(ov.matched("foo.htm", false).is_ignore());

View file

@ -2,55 +2,89 @@ use std::{ffi::OsStr, path::Path};
use crate::walk::DirEntry; use crate::walk::DirEntry;
/// Returns true if and only if this entry is considered to be hidden. /// Returns true if and only if this path is considered to be hidden.
/// ///
/// This only returns true if the base name of the path starts with a `.`. /// # Platform behavior
/// ///
/// On Unix, this implements a more optimized check. /// ## Windows
#[cfg(unix)]
pub(crate) fn is_hidden(dent: &DirEntry) -> bool {
use std::os::unix::ffi::OsStrExt;
if let Some(name) = file_name(dent.path()) {
name.as_bytes().get(0) == Some(&b'.')
} else {
false
}
}
/// Returns true if and only if this entry is considered to be hidden.
/// ///
/// On Windows, this returns true if one of the following is true: /// This returns true if one of the following is true:
/// ///
/// * The base name of the path starts with a `.`. /// * The base name of the path starts with a `.`.
/// * The file attributes have the `HIDDEN` property set. /// * The file attributes have the `HIDDEN` property set.
#[cfg(windows)] ///
pub(crate) fn is_hidden(dent: &DirEntry) -> bool { /// ## All other platforms
use std::os::windows::fs::MetadataExt;
use winapi_util::file;
// This looks like we're doing an extra stat call, but on Windows, the
// directory traverser reuses the metadata retrieved from each directory
// entry and stores it on the DirEntry itself. So this is "free."
if let Ok(md) = dent.metadata() {
if file::is_hidden(md.file_attributes() as u64) {
return true;
}
}
if let Some(name) = file_name(dent.path()) {
name.to_str().map(|s| s.starts_with(".")).unwrap_or(false)
} else {
false
}
}
/// Returns true if and only if this entry is considered to be hidden.
/// ///
/// This only returns true if the base name of the path starts with a `.`. /// This only returns true if the base name of the path starts with a `.`.
#[cfg(not(any(unix, windows)))] pub(crate) fn is_hidden_path(dent: &Path) -> bool {
pub(crate) fn is_hidden(dent: &DirEntry) -> bool { #[cfg(not(windows))]
if let Some(name) = file_name(dent.path()) { fn imp(path: &Path) -> bool {
name.to_str().map(|s| s.starts_with(".")).unwrap_or(false) is_hidden_path_only(path)
}
#[cfg(windows)]
fn imp(path: &Path) -> bool {
use std::os::windows::fs::MetadataExt;
use winapi_util::file;
if let Ok(md) = path.metadata() {
if file::is_hidden(md.file_attributes() as u64) {
return true;
}
}
is_hidden_path_only(path)
}
imp(dent)
}
/// Returns true if and only if this directory entry is considered to be
/// hidden.
///
/// # Platform behavior
///
/// ## Windows
///
/// This returns true if one of the following is true:
///
/// * The base name of the path starts with a `.`.
/// * The file attributes have the `HIDDEN` property set.
///
/// ## All other platforms
///
/// This only returns true if the base name of the path starts with a `.`.
pub(crate) fn is_hidden_entry(dent: &DirEntry) -> bool {
#[cfg(not(windows))]
fn imp(dent: &DirEntry) -> bool {
is_hidden_path_only(dent.path())
}
#[cfg(windows)]
fn imp(dent: &DirEntry) -> bool {
use std::os::windows::fs::MetadataExt;
use winapi_util::file;
// This looks like we're doing an extra stat call, but on Windows, the
// directory traverser reuses the metadata retrieved from each directory
// entry and stores it on the DirEntry itself. So this is "free."
if let Ok(md) = dent.metadata() {
if file::is_hidden(md.file_attributes() as u64) {
return true;
}
}
is_hidden_path_only(dent.path())
}
imp(dent)
}
/// Returns true if and only if this path is considered to be hidden from only
/// the path itself.
///
/// This has the same behavior on all platforms.
fn is_hidden_path_only(path: &Path) -> bool {
if let Some(name) = file_name(path) {
name.as_encoded_bytes().starts_with(b".")
} else { } else {
false false
} }
@ -59,83 +93,79 @@ pub(crate) fn is_hidden(dent: &DirEntry) -> bool {
/// Strip `prefix` from the `path` and return the remainder. /// Strip `prefix` from the `path` and return the remainder.
/// ///
/// If `path` doesn't have a prefix `prefix`, then return `None`. /// If `path` doesn't have a prefix `prefix`, then return `None`.
#[cfg(unix)]
pub(crate) fn strip_prefix<'a, P: AsRef<Path> + ?Sized>( pub(crate) fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
prefix: &'a P, prefix: &'a P,
path: &'a Path, path: &'a Path,
) -> Option<&'a Path> { ) -> Option<&'a Path> {
use std::os::unix::ffi::OsStrExt; #[cfg(unix)]
fn imp<'a>(prefix: &'a Path, path: &'a Path) -> Option<&'a Path> {
use std::os::unix::ffi::OsStrExt;
let prefix = prefix.as_ref().as_os_str().as_bytes(); let prefix = prefix.as_os_str().as_bytes();
let path = path.as_os_str().as_bytes(); let path = path.as_os_str().as_bytes();
if prefix.len() > path.len() || prefix != &path[0..prefix.len()] { if prefix.len() > path.len() || prefix != &path[0..prefix.len()] {
None None
} else { } else {
Some(&Path::new(OsStr::from_bytes(&path[prefix.len()..]))) Some(&Path::new(OsStr::from_bytes(&path[prefix.len()..])))
}
} }
}
/// Strip `prefix` from the `path` and return the remainder. #[cfg(not(unix))]
/// fn imp<'a>(prefix: &'a Path, path: &'a Path) -> Option<&'a Path> {
/// If `path` doesn't have a prefix `prefix`, then return `None`. path.strip_prefix(prefix).ok()
#[cfg(not(unix))] }
pub(crate) fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
prefix: &'a P, imp(prefix.as_ref(), path)
path: &'a Path,
) -> Option<&'a Path> {
path.strip_prefix(prefix).ok()
} }
/// Returns true if this file path is just a file name. i.e., Its parent is /// Returns true if this file path is just a file name. i.e., Its parent is
/// the empty string. /// the empty string.
#[cfg(unix)]
pub(crate) fn is_file_name<P: AsRef<Path>>(path: P) -> bool { pub(crate) fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
use std::os::unix::ffi::OsStrExt; #[cfg(unix)]
{
use memchr::memchr; memchr::memchr(b'/', path.as_ref().as_os_str().as_encoded_bytes())
.is_none()
let path = path.as_ref().as_os_str().as_bytes(); }
memchr(b'/', path).is_none() #[cfg(not(unix))]
} {
path.as_ref()
/// Returns true if this file path is just a file name. i.e., Its parent is .parent()
/// the empty string. .map(|p| p.as_os_str().is_empty())
#[cfg(not(unix))] .unwrap_or(false)
pub(crate) fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
path.as_ref()
.parent()
.map(|p| p.as_os_str().is_empty())
.unwrap_or(false)
}
/// The final component of the path, if it is a normal file.
///
/// If the path terminates in ., .., or consists solely of a root of prefix,
/// file_name will return None.
#[cfg(unix)]
pub(crate) fn file_name<'a, P: AsRef<Path> + ?Sized>(path: &'a P) -> Option<&'a OsStr> {
use memchr::memrchr;
use std::os::unix::ffi::OsStrExt;
let path = path.as_ref().as_os_str().as_bytes();
if path.is_empty() {
return None;
} else if path.len() == 1 && path[0] == b'.' {
return None;
} else if path.last() == Some(&b'.') {
return None;
} else if path.len() >= 2 && &path[path.len() - 2..] == &b".."[..] {
return None;
} }
let last_slash = memrchr(b'/', path).map(|i| i + 1).unwrap_or(0);
Some(OsStr::from_bytes(&path[last_slash..]))
} }
/// The final component of the path, if it is a normal file. /// The final component of the path, if it is a normal file.
/// ///
/// If the path terminates in ., .., or consists solely of a root of prefix, /// If the path terminates in `.`, `..`, or consists solely of a root of
/// file_name will return None. /// prefix, this will return `None`.
#[cfg(not(unix))] pub(crate) fn file_name<'a, P: AsRef<Path> + ?Sized>(
pub(crate) fn file_name<'a, P: AsRef<Path> + ?Sized>(path: &'a P) -> Option<&'a OsStr> { path: &'a P,
path.as_ref().file_name() ) -> Option<&'a OsStr> {
#[cfg(unix)]
fn imp(path: &Path) -> Option<&OsStr> {
use std::os::unix::ffi::OsStrExt;
use memchr::memrchr;
let path = path.as_os_str().as_bytes();
if path.is_empty() {
return None;
} else if path.len() == 1 && path[0] == b'.' {
return None;
} else if path.last() == Some(&b'.') {
return None;
} else if path.len() >= 2 && &path[path.len() - 2..] == &b".."[..] {
return None;
}
let last_slash = memrchr(b'/', path).map(|i| i + 1).unwrap_or(0);
Some(OsStr::from_bytes(&path[last_slash..]))
}
#[cfg(not(unix))]
fn imp(path: &Path) -> Option<&OsStr> {
path.file_name()
}
imp(path.as_ref())
} }

View file

@ -91,7 +91,7 @@ use {
regex_automata::util::pool::Pool, regex_automata::util::pool::Pool,
}; };
use crate::{default_types::DEFAULT_TYPES, pathutil::file_name, Error, Match}; use crate::{Error, Match, default_types::DEFAULT_TYPES, pathutil::file_name};
/// Glob represents a single glob in a set of file type definitions. /// Glob represents a single glob in a set of file type definitions.
/// ///
@ -204,8 +204,12 @@ impl<T> Selection<T> {
fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Selection<U> { fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Selection<U> {
match self { match self {
Selection::Select(name, inner) => Selection::Select(name, f(inner)), Selection::Select(name, inner) => {
Selection::Negate(name, inner) => Selection::Negate(name, f(inner)), Selection::Select(name, f(inner))
}
Selection::Negate(name, inner) => {
Selection::Negate(name, f(inner))
}
} }
} }
@ -227,7 +231,9 @@ impl Types {
has_selected: false, has_selected: false,
glob_to_selection: vec![], glob_to_selection: vec![],
set: GlobSetBuilder::new().build().unwrap(), set: GlobSetBuilder::new().build().unwrap(),
matches: Arc::new(Pool::new(|| vec![])), matches: Arc::new(Pool::with_available_parallelism_capacity(
|| vec![],
)),
} }
} }
@ -254,7 +260,11 @@ impl Types {
/// The path is considered ignored if it matches a negated file type. /// The path is considered ignored if it matches a negated file type.
/// If at least one file type is selected and `path` doesn't match, then /// If at least one file type is selected and `path` doesn't match, then
/// the path is also considered ignored. /// the path is also considered ignored.
pub fn matched<'a, P: AsRef<Path>>(&'a self, path: P, is_dir: bool) -> Match<Glob<'a>> { pub fn matched<'a, P: AsRef<Path>>(
&'a self,
path: P,
is_dir: bool,
) -> Match<Glob<'a>> {
// File types don't apply to directories, and we can't do anything // File types don't apply to directories, and we can't do anything
// if our glob set is empty. // if our glob set is empty.
if is_dir || self.set.is_empty() { if is_dir || self.set.is_empty() {
@ -306,10 +316,7 @@ impl TypesBuilder {
/// of default type definitions can be added with `add_defaults`, and /// of default type definitions can be added with `add_defaults`, and
/// additional type definitions can be added with `select` and `negate`. /// additional type definitions can be added with `select` and `negate`.
pub fn new() -> TypesBuilder { pub fn new() -> TypesBuilder {
TypesBuilder { TypesBuilder { types: HashMap::new(), selections: vec![] }
types: HashMap::new(),
selections: vec![],
}
} }
/// Build the current set of file type definitions *and* selections into /// Build the current set of file type definitions *and* selections into
@ -343,17 +350,18 @@ impl TypesBuilder {
} }
selections.push(selection.clone().map(move |_| def)); selections.push(selection.clone().map(move |_| def));
} }
let set = build_set.build().map_err(|err| Error::Glob { let set = build_set
glob: None, .build()
err: err.to_string(), .map_err(|err| Error::Glob { glob: None, err: err.to_string() })?;
})?;
Ok(Types { Ok(Types {
defs, defs,
selections, selections,
has_selected, has_selected,
glob_to_selection, glob_to_selection,
set, set,
matches: Arc::new(Pool::new(|| vec![])), matches: Arc::new(Pool::with_available_parallelism_capacity(
|| vec![],
)),
}) })
} }
@ -377,12 +385,10 @@ impl TypesBuilder {
pub fn select(&mut self, name: &str) -> &mut TypesBuilder { pub fn select(&mut self, name: &str) -> &mut TypesBuilder {
if name == "all" { if name == "all" {
for name in self.types.keys() { for name in self.types.keys() {
self.selections self.selections.push(Selection::Select(name.to_string(), ()));
.push(Selection::Select(name.to_string(), ()));
} }
} else { } else {
self.selections self.selections.push(Selection::Select(name.to_string(), ()));
.push(Selection::Select(name.to_string(), ()));
} }
self self
} }
@ -393,12 +399,10 @@ impl TypesBuilder {
pub fn negate(&mut self, name: &str) -> &mut TypesBuilder { pub fn negate(&mut self, name: &str) -> &mut TypesBuilder {
if name == "all" { if name == "all" {
for name in self.types.keys() { for name in self.types.keys() {
self.selections self.selections.push(Selection::Negate(name.to_string(), ()));
.push(Selection::Negate(name.to_string(), ()));
} }
} else { } else {
self.selections self.selections.push(Selection::Negate(name.to_string(), ()));
.push(Selection::Negate(name.to_string(), ()));
} }
self self
} }
@ -453,7 +457,10 @@ impl TypesBuilder {
3 => { 3 => {
let name = parts[0]; let name = parts[0];
let types_string = parts[2]; let types_string = parts[2];
if name.is_empty() || parts[1] != "include" || types_string.is_empty() { if name.is_empty()
|| parts[1] != "include"
|| types_string.is_empty()
{
return Err(Error::InvalidDefinition); return Err(Error::InvalidDefinition);
} }
let types = types_string.split(','); let types = types_string.split(',');
@ -463,7 +470,8 @@ impl TypesBuilder {
return Err(Error::InvalidDefinition); return Err(Error::InvalidDefinition);
} }
for type_name in types { for type_name in types {
let globs = self.types.get(type_name).unwrap().globs.clone(); let globs =
self.types.get(type_name).unwrap().globs.clone();
for glob in globs { for glob in globs {
self.add(name, &glob)?; self.add(name, &glob)?;
} }
@ -549,30 +557,9 @@ mod tests {
matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html"); matched!(not, matchnot1, types(), vec!["rust"], vec![], "index.html");
matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs"); matched!(not, matchnot2, types(), vec![], vec!["rust"], "main.rs");
matched!( matched!(not, matchnot3, types(), vec!["foo"], vec!["rust"], "main.rs");
not, matched!(not, matchnot4, types(), vec!["rust"], vec!["foo"], "main.rs");
matchnot3, matched!(not, matchnot5, types(), vec!["rust"], vec!["foo"], "main.foo");
types(),
vec!["foo"],
vec!["rust"],
"main.rs"
);
matched!(
not,
matchnot4,
types(),
vec!["rust"],
vec!["foo"],
"main.rs"
);
matched!(
not,
matchnot5,
types(),
vec!["rust"],
vec!["foo"],
"main.foo"
);
matched!(not, matchnot6, types(), vec!["combo"], vec![], "leftpad.js"); matched!(not, matchnot6, types(), vec!["combo"], vec![], "leftpad.js");
matched!(not, matchnot7, types(), vec!["py"], vec![], "index.html"); matched!(not, matchnot7, types(), vec!["py"], vec![], "index.html");
matched!(not, matchnot8, types(), vec!["python"], vec![], "doc.md"); matched!(not, matchnot8, types(), vec!["python"], vec![], "doc.md");

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,8 @@ use std::path::Path;
use ignore::gitignore::{Gitignore, GitignoreBuilder}; use ignore::gitignore::{Gitignore, GitignoreBuilder};
const IGNORE_FILE: &'static str = "tests/gitignore_matched_path_or_any_parents_tests.gitignore"; const IGNORE_FILE: &'static str =
"tests/gitignore_matched_path_or_any_parents_tests.gitignore";
fn get_gitignore() -> Gitignore { fn get_gitignore() -> Gitignore {
let mut builder = GitignoreBuilder::new("ROOT"); let mut builder = GitignoreBuilder::new("ROOT");
@ -23,7 +24,9 @@ fn test_path_should_be_under_root() {
#[test] #[test]
fn test_files_in_root() { fn test_files_in_root() {
let gitignore = get_gitignore(); let gitignore = get_gitignore();
let m = |path: &str| gitignore.matched_path_or_any_parents(Path::new(path), false); let m = |path: &str| {
gitignore.matched_path_or_any_parents(Path::new(path), false)
};
// 0x // 0x
assert!(m("ROOT/file_root_00").is_ignore()); assert!(m("ROOT/file_root_00").is_ignore());
@ -53,7 +56,9 @@ fn test_files_in_root() {
#[test] #[test]
fn test_files_in_deep() { fn test_files_in_deep() {
let gitignore = get_gitignore(); let gitignore = get_gitignore();
let m = |path: &str| gitignore.matched_path_or_any_parents(Path::new(path), false); let m = |path: &str| {
gitignore.matched_path_or_any_parents(Path::new(path), false)
};
// 0x // 0x
assert!(m("ROOT/parent_dir/file_deep_00").is_ignore()); assert!(m("ROOT/parent_dir/file_deep_00").is_ignore());
@ -83,8 +88,9 @@ fn test_files_in_deep() {
#[test] #[test]
fn test_dirs_in_root() { fn test_dirs_in_root() {
let gitignore = get_gitignore(); let gitignore = get_gitignore();
let m = let m = |path: &str, is_dir: bool| {
|path: &str, is_dir: bool| gitignore.matched_path_or_any_parents(Path::new(path), is_dir); gitignore.matched_path_or_any_parents(Path::new(path), is_dir)
};
// 00 // 00
assert!(m("ROOT/dir_root_00", true).is_ignore()); assert!(m("ROOT/dir_root_00", true).is_ignore());
@ -186,20 +192,25 @@ fn test_dirs_in_root() {
#[test] #[test]
fn test_dirs_in_deep() { fn test_dirs_in_deep() {
let gitignore = get_gitignore(); let gitignore = get_gitignore();
let m = let m = |path: &str, is_dir: bool| {
|path: &str, is_dir: bool| gitignore.matched_path_or_any_parents(Path::new(path), is_dir); gitignore.matched_path_or_any_parents(Path::new(path), is_dir)
};
// 00 // 00
assert!(m("ROOT/parent_dir/dir_deep_00", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_00", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_00/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_00/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_00/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_00/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_00/child_dir/file", false).is_ignore()
);
// 01 // 01
assert!(m("ROOT/parent_dir/dir_deep_01", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_01", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_01/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_01/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_01/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_01/child_dir/file", false).is_ignore()
);
// 02 // 02
assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none()); assert!(m("ROOT/parent_dir/dir_deep_02", true).is_none());
@ -241,51 +252,67 @@ fn test_dirs_in_deep() {
assert!(m("ROOT/parent_dir/dir_deep_20", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_20", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_20/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_20/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_20/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_20/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_20/child_dir/file", false).is_ignore()
);
// 21 // 21
assert!(m("ROOT/parent_dir/dir_deep_21", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_21", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_21/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_21/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_21/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_21/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_21/child_dir/file", false).is_ignore()
);
// 22 // 22
// dir itself doesn't match // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_22", true).is_none()); assert!(m("ROOT/parent_dir/dir_deep_22", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_22/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_22/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_22/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_22/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_22/child_dir/file", false).is_ignore()
);
// 23 // 23
// dir itself doesn't match // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_23", true).is_none()); assert!(m("ROOT/parent_dir/dir_deep_23", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_23/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_23/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_23/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_23/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_23/child_dir/file", false).is_ignore()
);
// 30 // 30
assert!(m("ROOT/parent_dir/dir_deep_30", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_30", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_30/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_30/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_30/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_30/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_30/child_dir/file", false).is_ignore()
);
// 31 // 31
assert!(m("ROOT/parent_dir/dir_deep_31", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_31", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_31/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_31/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_31/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_31/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_31/child_dir/file", false).is_ignore()
);
// 32 // 32
// dir itself doesn't match // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_32", true).is_none()); assert!(m("ROOT/parent_dir/dir_deep_32", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_32/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_32/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_32/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_32/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_32/child_dir/file", false).is_ignore()
);
// 33 // 33
// dir itself doesn't match // dir itself doesn't match
assert!(m("ROOT/parent_dir/dir_deep_33", true).is_none()); assert!(m("ROOT/parent_dir/dir_deep_33", true).is_none());
assert!(m("ROOT/parent_dir/dir_deep_33/file", false).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_33/file", false).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir", true).is_ignore()); assert!(m("ROOT/parent_dir/dir_deep_33/child_dir", true).is_ignore());
assert!(m("ROOT/parent_dir/dir_deep_33/child_dir/file", false).is_ignore()); assert!(
m("ROOT/parent_dir/dir_deep_33/child_dir/file", false).is_ignore()
);
} }

View file

@ -0,0 +1,2 @@
ignore/this/path
# This file begins with a BOM (U+FEFF)

View file

@ -0,0 +1,17 @@
use ignore::gitignore::GitignoreBuilder;
const IGNORE_FILE: &'static str = "tests/gitignore_skip_bom.gitignore";
/// Skip a Byte-Order Mark (BOM) at the beginning of the file, matching Git's
/// behavior.
///
/// Ref: <https://github.com/BurntSushi/ripgrep/issues/2177>
#[test]
fn gitignore_skip_bom() {
let mut builder = GitignoreBuilder::new("ROOT");
let error = builder.add(IGNORE_FILE);
assert!(error.is_none(), "failed to open gitignore file");
let g = builder.build().unwrap();
assert!(g.matched("ignore/this/path", false).is_ignore());
}

View file

@ -121,7 +121,7 @@ dist
.AppleDouble .AppleDouble
.LSOverride .LSOverride
# Icon must end with two # Icon must end with two
Icon Icon
@ -194,8 +194,14 @@ Cargo.lock
!.yarn/sdks !.yarn/sdks
!.yarn/versions !.yarn/versions
*.node
# Generated # Generated
*.node
*.wasm
index.d.ts index.d.ts
index.js index.js
browser.js
tailwindcss-oxide.wasi-browser.js
tailwindcss-oxide.wasi.cjs
tailwindcss-oxide.wasi.d.cts
wasi-worker-browser.mjs
wasi-worker.mjs

View file

@ -8,10 +8,10 @@ crate-type = ["cdylib"]
[dependencies] [dependencies]
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.16.16", default-features = false, features = ["napi4"] } napi = { version = "3.11.0", default-features = false, features = ["napi4"] }
napi-derive = "2.16.13" napi-derive = "3.6.0"
tailwindcss-oxide = { path = "../oxide" } tailwindcss-oxide = { path = "../oxide" }
rayon = "1.10.0" rayon = "1.12.0"
[build-dependencies] [build-dependencies]
napi-build = "2.1.4" napi-build = "2.3.2"

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-android-arm-eabi", "name": "@tailwindcss/oxide-android-arm-eabi",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-android-arm64", "name": "@tailwindcss/oxide-android-arm64",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-darwin-arm64", "name": "@tailwindcss/oxide-darwin-arm64",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-darwin-x64", "name": "@tailwindcss/oxide-darwin-x64",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-freebsd-x64", "name": "@tailwindcss/oxide-freebsd-x64",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-linux-arm-gnueabihf", "name": "@tailwindcss/oxide-linux-arm-gnueabihf",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-linux-arm64-gnu", "name": "@tailwindcss/oxide-linux-arm64-gnu",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,7 +22,7 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
}, },
"libc": [ "libc": [
"glibc" "glibc"

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-linux-arm64-musl", "name": "@tailwindcss/oxide-linux-arm64-musl",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,7 +22,7 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
}, },
"libc": [ "libc": [
"musl" "musl"

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-linux-x64-gnu", "name": "@tailwindcss/oxide-linux-x64-gnu",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,7 +22,7 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
}, },
"libc": [ "libc": [
"glibc" "glibc"

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-linux-x64-musl", "name": "@tailwindcss/oxide-linux-x64-musl",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,7 +22,7 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
}, },
"libc": [ "libc": [
"musl" "musl"

View file

@ -0,0 +1 @@
node_modules/

View file

@ -0,0 +1,3 @@
# `@tailwindcss/oxide-wasm32-wasi`
This is the **wasm32-wasip1-threads** build of `@tailwindcss/oxide`

View file

@ -0,0 +1,42 @@
{
"name": "@tailwindcss/oxide-wasm32-wasi",
"version": "4.3.3",
"main": "tailwindcss-oxide.wasi.cjs",
"files": [
"tailwindcss-oxide.wasm32-wasi.wasm",
"tailwindcss-oxide.wasi.cjs",
"tailwindcss-oxide.wasi-browser.js",
"wasi-worker.mjs",
"wasi-worker-browser.mjs"
],
"license": "MIT",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=23.5.0"
},
"publishConfig": {
"provenance": true,
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git",
"directory": "crates/node"
},
"browser": "tailwindcss-oxide.wasi-browser.js",
"dependencies": {
"@napi-rs/wasm-runtime": "^1.2.2",
"@emnapi/core": "^1.11.3",
"@emnapi/runtime": "^1.11.3",
"@tybys/wasm-util": "^0.10.3",
"@emnapi/wasi-threads": "^1.2.3",
"tslib": "^2.8.1"
},
"bundledDependencies": [
"@napi-rs/wasm-runtime",
"@emnapi/core",
"@emnapi/runtime",
"@tybys/wasm-util",
"@emnapi/wasi-threads",
"tslib"
]
}

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-win32-arm64-msvc", "name": "@tailwindcss/oxide-win32-arm64-msvc",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide-win32-x64-msvc", "name": "@tailwindcss/oxide-win32-x64-msvc",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -22,6 +22,6 @@
}, },
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@tailwindcss/oxide", "name": "@tailwindcss/oxide",
"version": "4.1.3", "version": "4.3.3",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/tailwindlabs/tailwindcss.git", "url": "git+https://github.com/tailwindlabs/tailwindcss.git",
@ -9,28 +9,38 @@
"main": "index.js", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
"napi": { "napi": {
"name": "tailwindcss-oxide", "binaryName": "tailwindcss-oxide",
"triples": { "packageName": "@tailwindcss/oxide",
"additional": [ "targets": [
"armv7-linux-androideabi", "armv7-linux-androideabi",
"aarch64-linux-android", "aarch64-linux-android",
"aarch64-apple-darwin", "aarch64-apple-darwin",
"aarch64-unknown-linux-gnu", "aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl", "aarch64-unknown-linux-musl",
"armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabihf",
"x86_64-unknown-linux-musl", "x86_64-unknown-linux-musl",
"x86_64-unknown-freebsd", "x86_64-unknown-freebsd",
"i686-pc-windows-msvc", "i686-pc-windows-msvc",
"aarch64-pc-windows-msvc" "aarch64-pc-windows-msvc",
] "wasm32-wasip1-threads"
],
"wasm": {
"initialMemory": 16384,
"browser": {
"fs": true
}
} }
}, },
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@napi-rs/cli": "^2.18.4" "@emnapi/core": "1.11.3",
"@emnapi/runtime": "1.11.3",
"@napi-rs/cli": "3.7.4",
"@napi-rs/wasm-runtime": "^1.2.2",
"emnapi": "1.11.3"
}, },
"engines": { "engines": {
"node": ">= 10" "node": ">= 20"
}, },
"files": [ "files": [
"index.js", "index.js",
@ -41,10 +51,13 @@
"access": "public" "access": "public"
}, },
"scripts": { "scripts": {
"artifacts": "napi artifacts", "build": "pnpm run build:platform && pnpm run build:wasm",
"build": "napi build --platform --release --no-const-enum", "build:platform": "napi build --platform --release",
"postbuild:platform": "node ./scripts/move-artifacts.mjs",
"build:wasm": "napi build --release --target wasm32-wasip1-threads",
"postbuild:wasm": "node ./scripts/move-artifacts.mjs",
"dev": "cargo watch --quiet --shell 'npm run build'", "dev": "cargo watch --quiet --shell 'npm run build'",
"build:debug": "napi build --platform --no-const-enum", "build:debug": "napi build --platform",
"version": "napi version" "version": "napi version"
}, },
"optionalDependencies": { "optionalDependencies": {
@ -57,7 +70,8 @@
"@tailwindcss/oxide-linux-arm64-musl": "workspace:*", "@tailwindcss/oxide-linux-arm64-musl": "workspace:*",
"@tailwindcss/oxide-linux-x64-gnu": "workspace:*", "@tailwindcss/oxide-linux-x64-gnu": "workspace:*",
"@tailwindcss/oxide-linux-x64-musl": "workspace:*", "@tailwindcss/oxide-linux-x64-musl": "workspace:*",
"@tailwindcss/oxide-win32-x64-msvc": "workspace:*", "@tailwindcss/oxide-wasm32-wasi": "workspace:*",
"@tailwindcss/oxide-win32-arm64-msvc": "workspace:*" "@tailwindcss/oxide-win32-arm64-msvc": "workspace:*",
"@tailwindcss/oxide-win32-x64-msvc": "workspace:*"
} }
} }

View file

@ -0,0 +1,37 @@
import fs from 'node:fs/promises'
import path from 'node:path'
import url from 'node:url'
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
let root = path.resolve(__dirname, '..')
const tailwindcssOxideRoot = path.join(root)
// Move napi artifacts into sub packages
for (let file of await fs.readdir(tailwindcssOxideRoot)) {
if (file.startsWith('tailwindcss-oxide.') && file.endsWith('.node')) {
let target = file.split('.')[1]
await fs.cp(
path.join(tailwindcssOxideRoot, file),
path.join(tailwindcssOxideRoot, 'npm', target, file),
)
console.log(`Moved ${file} to npm/${target}`)
}
}
// Move napi wasm artifacts into sub package
let wasmArtifacts = {
'tailwindcss-oxide.debug.wasm': 'tailwindcss-oxide.wasm32-wasi.debug.wasm',
'tailwindcss-oxide.wasm': 'tailwindcss-oxide.wasm32-wasi.wasm',
'tailwindcss-oxide.wasi-browser.js': 'tailwindcss-oxide.wasi-browser.js',
'tailwindcss-oxide.wasi.cjs': 'tailwindcss-oxide.wasi.cjs',
'wasi-worker-browser.mjs': 'wasi-worker-browser.mjs',
'wasi-worker.mjs': 'wasi-worker.mjs',
}
for (let file of await fs.readdir(tailwindcssOxideRoot)) {
if (!wasmArtifacts[file]) continue
await fs.cp(
path.join(tailwindcssOxideRoot, file),
path.join(tailwindcssOxideRoot, 'npm', 'wasm32-wasi', wasmArtifacts[file]),
)
console.log(`Moved ${file} to npm/wasm32-wasi`)
}

View file

@ -165,6 +165,11 @@ impl Scanner {
self.scanner.get_files() self.scanner.get_files()
} }
#[napi(getter)]
pub fn scanned_files(&self) -> Vec<String> {
self.scanner.get_scanned_files()
}
#[napi(getter)] #[napi(getter)]
pub fn globs(&mut self) -> Vec<GlobEntry> { pub fn globs(&mut self) -> Vec<GlobEntry> {
self self

View file

@ -9,7 +9,6 @@ globwalk = "0.9.1"
log = "0.4.22" log = "0.4.22"
rayon = "1.10.0" rayon = "1.10.0"
fxhash = { package = "rustc-hash", version = "2.1.1" } fxhash = { package = "rustc-hash", version = "2.1.1" }
crossbeam = "0.8.4"
tracing = { version = "0.1.40", features = [] } tracing = { version = "0.1.40", features = [] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
walkdir = "2.5.0" walkdir = "2.5.0"
@ -21,5 +20,7 @@ ignore = { path = "../ignore" }
regex = "1.11.1" regex = "1.11.1"
[dev-dependencies] [dev-dependencies]
insta = "1.48.0"
tempfile = "3.13.0" tempfile = "3.13.0"
pretty_assertions = "1.4.1" pretty_assertions = "1.4.1"
unicode-width = "0.2.0"

View file

@ -1,44 +1,49 @@
use std::{ascii::escape_default, fmt::Display}; use std::{ascii::escape_default, fmt::Display};
#[derive(Debug, Clone)] #[derive(Debug, Clone, Copy)]
pub struct Cursor<'a> { pub struct Cursor<'a> {
// The input we're scanning // The input we're scanning
pub input: &'a [u8], pub input: &'a [u8],
// The location of the cursor in the input // The location of the cursor in the input
pub pos: usize, pub pos: usize,
/// Is the cursor at the start of the input
pub at_start: bool,
/// Is the cursor at the end of the input
pub at_end: bool,
/// The previously consumed character
/// If `at_start` is true, this will be NUL
pub prev: u8,
/// The current character
pub curr: u8,
/// The upcoming character (if any)
/// If `at_end` is true, this will be NUL
pub next: u8,
} }
impl<'a> Cursor<'a> { impl<'a> Cursor<'a> {
#[inline(always)]
pub fn new(input: &'a [u8]) -> Self { pub fn new(input: &'a [u8]) -> Self {
let mut cursor = Self { Self { input, pos: 0 }
input, }
pos: 0,
at_start: true, /// The current byte at `pos`, or 0x00 if past the end.
at_end: false, #[inline(always)]
prev: 0x00, pub fn curr(&self) -> u8 {
curr: 0x00, if self.pos < self.input.len() {
next: 0x00, unsafe { *self.input.get_unchecked(self.pos) }
}; } else {
cursor.move_to(0); 0x00
cursor }
}
/// The next byte at `pos + 1`, or 0x00 if past the end.
#[inline(always)]
pub fn next(&self) -> u8 {
let next_pos = self.pos + 1;
if next_pos < self.input.len() {
unsafe { *self.input.get_unchecked(next_pos) }
} else {
0x00
}
}
/// The previous byte at `pos - 1`, or 0x00 if at the start.
#[inline(always)]
pub fn prev(&self) -> u8 {
if self.pos > 0 {
unsafe { *self.input.get_unchecked(self.pos - 1) }
} else {
0x00
}
} }
pub fn advance_by(&mut self, amount: usize) { pub fn advance_by(&mut self, amount: usize) {
@ -48,38 +53,15 @@ impl<'a> Cursor<'a> {
#[inline(always)] #[inline(always)]
pub fn advance(&mut self) { pub fn advance(&mut self) {
self.pos += 1; self.pos += 1;
self.prev = self.curr;
self.curr = self.next;
self.next = *self
.input
.get(self.pos.saturating_add(1))
.unwrap_or(&0x00u8);
} }
#[inline(always)] #[inline(always)]
pub fn advance_twice(&mut self) { pub fn advance_twice(&mut self) {
self.pos += 2; self.pos += 2;
self.prev = self.next;
self.curr = *self.input.get(self.pos).unwrap_or(&0x00u8);
self.next = *self
.input
.get(self.pos.saturating_add(1))
.unwrap_or(&0x00u8);
} }
pub fn move_to(&mut self, pos: usize) { pub fn move_to(&mut self, pos: usize) {
let len = self.input.len(); self.pos = pos.min(self.input.len());
let pos = pos.clamp(0, len);
self.pos = pos;
self.at_start = pos == 0;
self.at_end = pos + 1 >= len;
self.prev = *self.input.get(pos.wrapping_sub(1)).unwrap_or(&0x00u8);
self.curr = *self.input.get(pos).unwrap_or(&0x00u8);
self.next = *self.input.get(pos.saturating_add(1)).unwrap_or(&0x00u8);
} }
} }
@ -90,9 +72,9 @@ impl Display for Cursor<'_> {
let pos = format!("{: >len_count$}", self.pos, len_count = len.len()); let pos = format!("{: >len_count$}", self.pos, len_count = len.len());
write!(f, "{}/{} ", pos, len)?; write!(f, "{}/{} ", pos, len)?;
if self.at_start { if self.pos == 0 {
write!(f, "S ")?; write!(f, "S ")?;
} else if self.at_end { } else if self.pos + 1 >= self.input.len() {
write!(f, "E ")?; write!(f, "E ")?;
} else { } else {
write!(f, "M ")?; write!(f, "M ")?;
@ -109,9 +91,9 @@ impl Display for Cursor<'_> {
write!( write!(
f, f,
"[{} {} {}]", "[{} {} {}]",
to_str(self.prev), to_str(self.prev()),
to_str(self.curr), to_str(self.curr()),
to_str(self.next) to_str(self.next())
) )
} }
} }
@ -125,36 +107,28 @@ mod test {
fn test_cursor() { fn test_cursor() {
let mut cursor = Cursor::new(b"hello world"); let mut cursor = Cursor::new(b"hello world");
assert_eq!(cursor.pos, 0); assert_eq!(cursor.pos, 0);
assert!(cursor.at_start); assert_eq!(cursor.prev(), 0x00);
assert!(!cursor.at_end); assert_eq!(cursor.curr(), b'h');
assert_eq!(cursor.prev, 0x00); assert_eq!(cursor.next(), b'e');
assert_eq!(cursor.curr, b'h');
assert_eq!(cursor.next, b'e');
cursor.advance_by(1); cursor.advance_by(1);
assert_eq!(cursor.pos, 1); assert_eq!(cursor.pos, 1);
assert!(!cursor.at_start); assert_eq!(cursor.prev(), b'h');
assert!(!cursor.at_end); assert_eq!(cursor.curr(), b'e');
assert_eq!(cursor.prev, b'h'); assert_eq!(cursor.next(), b'l');
assert_eq!(cursor.curr, b'e');
assert_eq!(cursor.next, b'l');
// Advancing too far should stop at the end // Advancing too far should stop at the end
cursor.advance_by(10); cursor.advance_by(10);
assert_eq!(cursor.pos, 11); assert_eq!(cursor.pos, 11);
assert!(!cursor.at_start); assert_eq!(cursor.prev(), b'd');
assert!(cursor.at_end); assert_eq!(cursor.curr(), 0x00);
assert_eq!(cursor.prev, b'd'); assert_eq!(cursor.next(), 0x00);
assert_eq!(cursor.curr, 0x00);
assert_eq!(cursor.next, 0x00);
// Can't advance past the end // Can't advance past the end
cursor.advance_by(1); cursor.advance_by(1);
assert_eq!(cursor.pos, 11); assert_eq!(cursor.pos, 11);
assert!(!cursor.at_start); assert_eq!(cursor.prev(), b'd');
assert!(cursor.at_end); assert_eq!(cursor.curr(), 0x00);
assert_eq!(cursor.prev, b'd'); assert_eq!(cursor.next(), 0x00);
assert_eq!(cursor.curr, 0x00);
assert_eq!(cursor.next, 0x00);
} }
} }

View file

@ -74,7 +74,7 @@ impl Machine for ArbitraryPropertyMachine<IdleState> {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.curr.into() { match cursor.curr().into() {
// Start of an arbitrary property // Start of an arbitrary property
Class::OpenBracket => { Class::OpenBracket => {
self.start_pos = cursor.pos; self.start_pos = cursor.pos;
@ -97,8 +97,8 @@ impl Machine for ArbitraryPropertyMachine<ParsingPropertyState> {
let len = cursor.input.len(); let len = cursor.input.len();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
Class::Dash => match cursor.next.into() { Class::Dash => match cursor.next().into() {
// Start of a CSS variable // Start of a CSS variable
// //
// E.g.: `[--my-color:red]` // E.g.: `[--my-color:red]`
@ -137,7 +137,7 @@ impl ArbitraryPropertyMachine<ParsingPropertyState> {
fn parse_property_variable(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn parse_property_variable(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match self.css_variable_machine.next(cursor) { match self.css_variable_machine.next(cursor) {
MachineState::Idle => self.restart(), MachineState::Idle => self.restart(),
MachineState::Done(_) => match cursor.next.into() { MachineState::Done(_) => match cursor.next().into() {
// End of the CSS variable, must be followed by a `:` // End of the CSS variable, must be followed by a `:`
// //
// E.g.: `[--my-color:red]` // E.g.: `[--my-color:red]`
@ -165,8 +165,8 @@ impl Machine for ArbitraryPropertyMachine<ParsingValueState> {
let len = cursor.input.len(); let len = cursor.input.len();
let start_of_value_pos = cursor.pos; let start_of_value_pos = cursor.pos;
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
Class::Escape => match cursor.next.into() { Class::Escape => match cursor.next().into() {
// An escaped whitespace character is not allowed // An escaped whitespace character is not allowed
// //
// E.g.: `[color:var(--my-\ color)]` // E.g.: `[color:var(--my-\ color)]`
@ -181,7 +181,7 @@ impl Machine for ArbitraryPropertyMachine<ParsingValueState> {
}, },
Class::OpenParen | Class::OpenBracket | Class::OpenCurly => { Class::OpenParen | Class::OpenBracket | Class::OpenCurly => {
if !self.bracket_stack.push(cursor.curr) { if !self.bracket_stack.push(cursor.curr()) {
return self.restart(); return self.restart();
} }
cursor.advance(); cursor.advance();
@ -190,7 +190,7 @@ impl Machine for ArbitraryPropertyMachine<ParsingValueState> {
Class::CloseParen | Class::CloseBracket | Class::CloseCurly Class::CloseParen | Class::CloseBracket | Class::CloseCurly
if !self.bracket_stack.is_empty() => if !self.bracket_stack.is_empty() =>
{ {
if !self.bracket_stack.pop(cursor.curr) { if !self.bracket_stack.pop(cursor.curr()) {
return self.restart(); return self.restart();
} }
cursor.advance(); cursor.advance();
@ -227,10 +227,16 @@ impl Machine for ArbitraryPropertyMachine<ParsingValueState> {
Class::Slash if start_of_value_pos == cursor.pos => return self.restart(), Class::Slash if start_of_value_pos == cursor.pos => return self.restart(),
// String interpolation-like syntax is not allowed. E.g.: `[${x}]` // String interpolation-like syntax is not allowed. E.g.: `[${x}]`
Class::Dollar if matches!(cursor.next.into(), Class::OpenCurly) => { Class::Dollar if matches!(cursor.next().into(), Class::OpenCurly) => {
return self.restart() return self.restart()
} }
// An `!` at the top-level is invalid. We don't allow things to end with
// `!important` either as we have dedicated syntax for this.
Class::Exclamation if self.bracket_stack.is_empty() => {
return self.restart();
}
// Everything else is valid // Everything else is valid
_ => cursor.advance(), _ => cursor.advance(),
}; };
@ -293,6 +299,9 @@ enum Class {
#[bytes(b'/')] #[bytes(b'/')]
Slash, Slash,
#[bytes(b'!')]
Exclamation,
#[bytes(b' ', b'\t', b'\n', b'\r', b'\x0C')] #[bytes(b' ', b'\t', b'\n', b'\r', b'\x0C')]
Whitespace, Whitespace,
@ -369,6 +378,9 @@ mod tests {
"[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]", "[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]",
vec!["[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]"], vec!["[background:url(https://example.com?q={[{[([{[[2]]}])]}]})]"],
), ),
// A property containing `!` at the top-level is invalid
("[color:red!]", vec![]),
("[color:red!important]", vec![]),
] { ] {
for wrapper in [ for wrapper in [
// No wrapper // No wrapper

View file

@ -32,7 +32,7 @@ impl Machine for ArbitraryValueMachine {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
// An arbitrary value must start with an open bracket // An arbitrary value must start with an open bracket
if Class::OpenBracket != cursor.curr.into() { if Class::OpenBracket != cursor.curr().into() {
return MachineState::Idle; return MachineState::Idle;
} }
@ -42,8 +42,8 @@ impl Machine for ArbitraryValueMachine {
let len = cursor.input.len(); let len = cursor.input.len();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
Class::Escape => match cursor.next.into() { Class::Escape => match cursor.next().into() {
// An escaped whitespace character is not allowed // An escaped whitespace character is not allowed
// //
// E.g.: `[color:var(--my-\ color)]` // E.g.: `[color:var(--my-\ color)]`
@ -61,7 +61,7 @@ impl Machine for ArbitraryValueMachine {
}, },
Class::OpenParen | Class::OpenBracket | Class::OpenCurly => { Class::OpenParen | Class::OpenBracket | Class::OpenCurly => {
if !self.bracket_stack.push(cursor.curr) { if !self.bracket_stack.push(cursor.curr()) {
return self.restart(); return self.restart();
} }
cursor.advance(); cursor.advance();
@ -70,7 +70,7 @@ impl Machine for ArbitraryValueMachine {
Class::CloseParen | Class::CloseBracket | Class::CloseCurly Class::CloseParen | Class::CloseBracket | Class::CloseCurly
if !self.bracket_stack.is_empty() => if !self.bracket_stack.is_empty() =>
{ {
if !self.bracket_stack.pop(cursor.curr) { if !self.bracket_stack.pop(cursor.curr()) {
return self.restart(); return self.restart();
} }
cursor.advance(); cursor.advance();
@ -96,7 +96,7 @@ impl Machine for ArbitraryValueMachine {
Class::Whitespace => return self.restart(), Class::Whitespace => return self.restart(),
// String interpolation-like syntax is not allowed. E.g.: `[${x}]` // String interpolation-like syntax is not allowed. E.g.: `[${x}]`
Class::Dollar if matches!(cursor.next.into(), Class::OpenCurly) => { Class::Dollar if matches!(cursor.next().into(), Class::OpenCurly) => {
return self.restart() return self.restart()
} }

View file

@ -80,13 +80,13 @@ impl Machine for ArbitraryVariableMachine<IdleState> {
#[inline(always)] #[inline(always)]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.curr.into() { match cursor.curr().into() {
// Arbitrary variables start with `(` followed by a CSS variable // Arbitrary variables start with `(` followed by a CSS variable
// //
// E.g.: `(--my-variable)` // E.g.: `(--my-variable)`
// ^^ // ^^
// //
Class::OpenParen => match cursor.next.into() { Class::OpenParen => match cursor.next().into() {
Class::Dash => { Class::Dash => {
self.start_pos = cursor.pos; self.start_pos = cursor.pos;
cursor.advance(); cursor.advance();
@ -117,7 +117,7 @@ impl Machine for ArbitraryVariableMachine<ParsingState> {
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match self.css_variable_machine.next(cursor) { match self.css_variable_machine.next(cursor) {
MachineState::Idle => self.restart(), MachineState::Idle => self.restart(),
MachineState::Done(_) => match cursor.next.into() { MachineState::Done(_) => match cursor.next().into() {
// A CSS variable followed by a `,` means that there is a fallback // A CSS variable followed by a `,` means that there is a fallback
// //
// E.g.: `(--my-color,red)` // E.g.: `(--my-color,red)`
@ -134,7 +134,7 @@ impl Machine for ArbitraryVariableMachine<ParsingState> {
_ => { _ => {
cursor.advance(); cursor.advance();
match cursor.curr.into() { match cursor.curr().into() {
// End of an arbitrary variable, must be followed by `)` // End of an arbitrary variable, must be followed by `)`
Class::CloseParen => self.done(self.start_pos, cursor), Class::CloseParen => self.done(self.start_pos, cursor),
@ -156,7 +156,7 @@ impl Machine for ArbitraryVariableMachine<ParsingDataTypeState> {
let len = cursor.input.len(); let len = cursor.input.len();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
// Valid data type characters // Valid data type characters
// //
// E.g.: `(length:--my-length)` // E.g.: `(length:--my-length)`
@ -169,7 +169,7 @@ impl Machine for ArbitraryVariableMachine<ParsingDataTypeState> {
// //
// E.g.: `(length:--my-length)` // E.g.: `(length:--my-length)`
// ^ // ^
Class::Colon => match cursor.next.into() { Class::Colon => match cursor.next().into() {
Class::Dash => { Class::Dash => {
cursor.advance(); cursor.advance();
return self.transition::<ParsingState>().next(cursor); return self.transition::<ParsingState>().next(cursor);
@ -196,8 +196,8 @@ impl Machine for ArbitraryVariableMachine<ParsingFallbackState> {
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
let len = cursor.input.len(); let len = cursor.input.len();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
Class::Escape => match cursor.next.into() { Class::Escape => match cursor.next().into() {
// An escaped whitespace character is not allowed // An escaped whitespace character is not allowed
// //
// E.g.: `(--my-\ color)` // E.g.: `(--my-\ color)`
@ -212,7 +212,7 @@ impl Machine for ArbitraryVariableMachine<ParsingFallbackState> {
}, },
Class::OpenParen | Class::OpenBracket | Class::OpenCurly => { Class::OpenParen | Class::OpenBracket | Class::OpenCurly => {
if !self.bracket_stack.push(cursor.curr) { if !self.bracket_stack.push(cursor.curr()) {
return self.restart(); return self.restart();
} }
cursor.advance(); cursor.advance();
@ -221,7 +221,7 @@ impl Machine for ArbitraryVariableMachine<ParsingFallbackState> {
Class::CloseParen | Class::CloseBracket | Class::CloseCurly Class::CloseParen | Class::CloseBracket | Class::CloseCurly
if !self.bracket_stack.is_empty() => if !self.bracket_stack.is_empty() =>
{ {
if !self.bracket_stack.pop(cursor.curr) { if !self.bracket_stack.pop(cursor.curr()) {
return self.restart(); return self.restart();
} }
cursor.advance(); cursor.advance();
@ -253,7 +253,7 @@ impl Machine for ArbitraryVariableMachine<ParsingFallbackState> {
Class::Whitespace => return self.restart(), Class::Whitespace => return self.restart(),
// String interpolation-like syntax is not allowed. E.g.: `[${x}]` // String interpolation-like syntax is not allowed. E.g.: `[${x}]`
Class::Dollar if matches!(cursor.next.into(), Class::OpenCurly) => { Class::Dollar if matches!(cursor.next().into(), Class::OpenCurly) => {
return self.restart() return self.restart()
} }

View file

@ -25,6 +25,7 @@ impl BracketStack {
b'(' => b')', b'(' => b')',
b'[' => b']', b'[' => b']',
b'{' => b'}', b'{' => b'}',
b'<' => b'>',
_ => std::hint::unreachable_unchecked(), _ => std::hint::unreachable_unchecked(),
}; };
} }

View file

@ -32,14 +32,14 @@ impl Machine for CandidateMachine {
while cursor.pos < len { while cursor.pos < len {
// Skip ahead for known characters that will never be part of a candidate. No need to // Skip ahead for known characters that will never be part of a candidate. No need to
// run any sub-machines. // run any sub-machines.
if cursor.curr.is_ascii_whitespace() { if cursor.curr().is_ascii_whitespace() {
self.reset(); self.reset();
cursor.advance(); cursor.advance();
continue; continue;
} }
// Candidates don't start with these characters, so we can skip ahead. // Candidates don't start with these characters, so we can skip ahead.
if matches!(cursor.curr, b':' | b'"' | b'\'' | b'`') { if matches!(cursor.curr(), b':' | b'"' | b'\'' | b'`') {
self.reset(); self.reset();
cursor.advance(); cursor.advance();
continue; continue;
@ -56,7 +56,7 @@ impl Machine for CandidateMachine {
// E.g.: `Some Class` // E.g.: `Some Class`
// ^ ^ Invalid, we can jump ahead to the next boundary // ^ ^ Invalid, we can jump ahead to the next boundary
// //
if matches!(cursor.curr, b'<' | b'A'..=b'Z') { if matches!(cursor.curr(), b'<' | b'A'..=b'Z') {
if let Some(offset) = cursor.input[cursor.pos..] if let Some(offset) = cursor.input[cursor.pos..]
.iter() .iter()
.position(|&c| is_valid_before_boundary(&c)) .position(|&c| is_valid_before_boundary(&c))

View file

@ -20,7 +20,7 @@ impl Machine for CssVariableMachine {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
// CSS Variables must start with `--` // CSS Variables must start with `--`
if Class::Dash != cursor.curr.into() || Class::Dash != cursor.next.into() { if Class::Dash != cursor.curr().into() || Class::Dash != cursor.next().into() {
return MachineState::Idle; return MachineState::Idle;
} }
@ -30,11 +30,11 @@ impl Machine for CssVariableMachine {
cursor.advance_twice(); cursor.advance_twice();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
// https://drafts.csswg.org/css-syntax-3/#ident-token-diagram // https://drafts.csswg.org/css-syntax-3/#ident-token-diagram
// //
Class::AllowedCharacter | Class::Dash => { Class::AllowedCharacter | Class::Dash => {
match cursor.next.into() { match cursor.next().into() {
// Valid character followed by a valid character or an escape character // Valid character followed by a valid character or an escape character
// //
// E.g.: `--my-variable` // E.g.: `--my-variable`
@ -58,7 +58,7 @@ impl Machine for CssVariableMachine {
} }
} }
Class::Escape => match cursor.next.into() { Class::Escape => match cursor.next().into() {
// An escaped whitespace character is not allowed // An escaped whitespace character is not allowed
// //
// In CSS it is allowed, but in the context of a class it's not because then we // In CSS it is allowed, but in the context of a class it's not because then we

View file

@ -86,7 +86,7 @@ impl<'a> Extractor<'a> {
{ {
let cursor = &mut self.cursor.clone(); let cursor = &mut self.cursor.clone();
while cursor.pos < len { while cursor.pos < len {
if cursor.curr.is_ascii_whitespace() { if cursor.curr().is_ascii_whitespace() {
cursor.advance(); cursor.advance();
continue; continue;
} }
@ -104,7 +104,7 @@ impl<'a> Extractor<'a> {
let cursor = &mut self.cursor.clone(); let cursor = &mut self.cursor.clone();
while cursor.pos < len { while cursor.pos < len {
if cursor.curr.is_ascii_whitespace() { if cursor.curr().is_ascii_whitespace() {
cursor.advance(); cursor.advance();
continue; continue;
} }
@ -147,7 +147,7 @@ impl<'a> Extractor<'a> {
let cursor = &mut self.cursor.clone(); let cursor = &mut self.cursor.clone();
while cursor.pos < len { while cursor.pos < len {
if cursor.curr.is_ascii_whitespace() { if cursor.curr().is_ascii_whitespace() {
cursor.advance(); cursor.advance();
continue; continue;
} }
@ -238,7 +238,7 @@ mod tests {
use std::hint::black_box; use std::hint::black_box;
fn pre_process_input(input: &str, extension: &str) -> String { fn pre_process_input(input: &str, extension: &str) -> String {
let input = crate::scanner::pre_process_input(input.as_bytes(), extension); let input = crate::scanner::pre_process_input(input.as_bytes().to_vec(), extension);
String::from_utf8(input).unwrap() String::from_utf8(input).unwrap()
} }
@ -908,6 +908,18 @@ mod tests {
); );
} }
// https://github.com/tailwindlabs/tailwindcss/issues/20233
#[test]
fn test_template_toolkit_syntax() {
assert_extract_candidates_contains(
&pre_process_input(
r#"<div class="[% IF $is_open %]bg-white/40[% ELSE %]bg-white/10[% END %]"></div>"#,
"tx",
),
vec!["bg-white/40", "bg-white/10"],
);
}
// https://github.com/tailwindlabs/tailwindcss/issues/17050 // https://github.com/tailwindlabs/tailwindcss/issues/17050
#[test] #[test]
fn test_haml_syntax() { fn test_haml_syntax() {
@ -1054,6 +1066,20 @@ mod tests {
); );
} }
// See: https://github.com/tailwindlabs/tailwindcss/issues/18092
#[test]
fn test_leptos_rs_view_class_colon_syntax() {
for (input, expected) in [
(r#"<div class:px-6=true>"#, vec!["class", "px-6"]),
(
r#"view! { <div class:px-6=true> }"#,
vec!["class", "px-6", "view!"],
),
] {
assert_extract_sorted_candidates(&pre_process_input(input, "svelte"), expected);
}
}
#[test] #[test]
fn test_extract_css_variables() { fn test_extract_css_variables() {
for (input, expected) in [ for (input, expected) in [

View file

@ -31,14 +31,14 @@ impl Machine for ModifierMachine {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
// A modifier must start with a `/`, everything else is not a valid start of a modifier // A modifier must start with a `/`, everything else is not a valid start of a modifier
if Class::Slash != cursor.curr.into() { if Class::Slash != cursor.curr().into() {
return MachineState::Idle; return MachineState::Idle;
} }
let start_pos = cursor.pos; let start_pos = cursor.pos;
cursor.advance(); cursor.advance();
match cursor.curr.into() { match cursor.curr().into() {
// Start of an arbitrary value: // Start of an arbitrary value:
// //
// ``` // ```
@ -70,9 +70,9 @@ impl Machine for ModifierMachine {
Class::ValidStart => { Class::ValidStart => {
let len = cursor.input.len(); let len = cursor.input.len();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
Class::ValidStart | Class::ValidInside => { Class::ValidStart | Class::ValidInside => {
match cursor.next.into() { match cursor.next().into() {
// Only valid characters are allowed, if followed by another valid character // Only valid characters are allowed, if followed by another valid character
Class::ValidStart | Class::ValidInside => cursor.advance(), Class::ValidStart | Class::ValidInside => cursor.advance(),

View file

@ -52,8 +52,8 @@ impl Machine for NamedUtilityMachine<IdleState> {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.curr.into() { match cursor.curr().into() {
Class::AlphaLower => match cursor.next.into() { Class::AlphaLower => match cursor.next().into() {
// Valid single character utility in between quotes // Valid single character utility in between quotes
// //
// E.g.: `<div class="a"></div>` // E.g.: `<div class="a"></div>`
@ -90,7 +90,7 @@ impl Machine for NamedUtilityMachine<IdleState> {
// //
// E.g.: `-mx-2.5` // E.g.: `-mx-2.5`
// ^^ // ^^
Class::Dash => match cursor.next.into() { Class::Dash => match cursor.next().into() {
Class::AlphaLower => { Class::AlphaLower => {
self.start_pos = cursor.pos; self.start_pos = cursor.pos;
cursor.advance(); cursor.advance();
@ -118,7 +118,7 @@ impl Machine for NamedUtilityMachine<ParsingState> {
let len = cursor.input.len(); let len = cursor.input.len();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
// Followed by a boundary character, we are at the end of the utility. // Followed by a boundary character, we are at the end of the utility.
// //
// E.g.: `'flex'` // E.g.: `'flex'`
@ -132,14 +132,14 @@ impl Machine for NamedUtilityMachine<ParsingState> {
// E.g.: `:div="{ flex: true }"` (JavaScript object syntax) // E.g.: `:div="{ flex: true }"` (JavaScript object syntax)
// ^ // ^
Class::AlphaLower | Class::AlphaUpper => { Class::AlphaLower | Class::AlphaUpper => {
if is_valid_after_boundary(&cursor.next) || { if is_valid_after_boundary(&cursor.next()) || {
// Or any of these characters // Or any of these characters
// //
// - `:`, because of JS object keys // - `:`, because of JS object keys
// - `/`, because of modifiers // - `/`, because of modifiers
// - `!`, because of important // - `!`, because of important
matches!( matches!(
cursor.next.into(), cursor.next().into(),
Class::Colon | Class::Slash | Class::Exclamation Class::Colon | Class::Slash | Class::Exclamation
) )
} { } {
@ -150,7 +150,7 @@ impl Machine for NamedUtilityMachine<ParsingState> {
cursor.advance() cursor.advance()
} }
Class::Dash => match cursor.next.into() { Class::Dash => match cursor.next().into() {
// Start of an arbitrary value // Start of an arbitrary value
// //
// E.g.: `bg-[#0088cc]` // E.g.: `bg-[#0088cc]`
@ -196,7 +196,7 @@ impl Machine for NamedUtilityMachine<ParsingState> {
_ => return self.restart(), _ => return self.restart(),
}, },
Class::Underscore => match cursor.next.into() { Class::Underscore => match cursor.next().into() {
// Valid characters _if_ followed by another valid character. These characters are // Valid characters _if_ followed by another valid character. These characters are
// only valid inside of the utility but not at the end of the utility. // only valid inside of the utility but not at the end of the utility.
// //
@ -225,14 +225,14 @@ impl Machine for NamedUtilityMachine<ParsingState> {
// ^ // ^
// E.g.: `:div="{ flex: true }"` (JavaScript object syntax) // E.g.: `:div="{ flex: true }"` (JavaScript object syntax)
// ^ // ^
_ if is_valid_after_boundary(&cursor.next) || { _ if is_valid_after_boundary(&cursor.next()) || {
// Or any of these characters // Or any of these characters
// //
// - `:`, because of JS object keys // - `:`, because of JS object keys
// - `/`, because of modifiers // - `/`, because of modifiers
// - `!`, because of important // - `!`, because of important
matches!( matches!(
cursor.next.into(), cursor.next().into(),
Class::Colon | Class::Slash | Class::Exclamation Class::Colon | Class::Slash | Class::Exclamation
) )
} => } =>
@ -249,11 +249,11 @@ impl Machine for NamedUtilityMachine<ParsingState> {
// E.g.: `px-2.5` // E.g.: `px-2.5`
// ^^^ // ^^^
Class::Dot => { Class::Dot => {
if !matches!(cursor.prev.into(), Class::Number) { if !matches!(cursor.prev().into(), Class::Number) {
return self.restart(); return self.restart();
} }
if !matches!(cursor.next.into(), Class::Number) { if !matches!(cursor.next().into(), Class::Number) {
return self.restart(); return self.restart();
} }
@ -278,14 +278,19 @@ impl Machine for NamedUtilityMachine<ParsingState> {
// //
Class::Number => { Class::Number => {
if !matches!( if !matches!(
cursor.prev.into(), cursor.prev().into(),
Class::Dash | Class::Dot | Class::Number | Class::AlphaLower Class::Dash
| Class::Underscore
| Class::Dot
| Class::Number
| Class::AlphaLower
| Class::AlphaUpper
) { ) {
return self.restart(); return self.restart();
} }
if !matches!( if !matches!(
cursor.next.into(), cursor.next().into(),
Class::Dot Class::Dot
| Class::Number | Class::Number
| Class::AlphaLower | Class::AlphaLower
@ -309,7 +314,7 @@ impl Machine for NamedUtilityMachine<ParsingState> {
// ^^ // ^^
// ``` // ```
Class::Percent => { Class::Percent => {
if !matches!(cursor.prev.into(), Class::Number) { if !matches!(cursor.prev().into(), Class::Number) {
return self.restart(); return self.restart();
} }
@ -415,9 +420,14 @@ mod tests {
// With numbers // With numbers
("px-5", vec!["px-5"]), ("px-5", vec!["px-5"]),
("px-2.5", vec!["px-2.5"]), ("px-2.5", vec!["px-2.5"]),
// Underscores followed by numbers
("header_1", vec!["header_1"]),
("header_1_2", vec!["header_1_2"]),
// With number followed by dash or underscore // With number followed by dash or underscore
("text-title1-strong", vec!["text-title1-strong"]), ("text-title1-strong", vec!["text-title1-strong"]),
("text-title1_strong", vec!["text-title1_strong"]), ("text-title1_strong", vec!["text-title1_strong"]),
// With capital letter followed by number
("text-titleV1-strong", vec!["text-titleV1-strong"]),
// With trailing % sign // With trailing % sign
("from-15%", vec!["from-15%"]), ("from-15%", vec!["from-15%"]),
// Arbitrary value with bracket notation // Arbitrary value with bracket notation

View file

@ -81,8 +81,8 @@ impl Machine for NamedVariantMachine<IdleState> {
#[inline(always)] #[inline(always)]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.curr.into() { match cursor.curr().into() {
Class::AlphaLower | Class::Star => match cursor.next.into() { Class::AlphaLower | Class::Star => match cursor.next().into() {
// Valid single character variant, must be followed by a `:` // Valid single character variant, must be followed by a `:`
// //
// E.g.: `<div class="x:flex"></div>` // E.g.: `<div class="x:flex"></div>`
@ -134,8 +134,8 @@ impl Machine for NamedVariantMachine<ParsingState> {
let len = cursor.input.len(); let len = cursor.input.len();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
Class::Dash => match cursor.next.into() { Class::Dash => match cursor.next().into() {
// Start of an arbitrary value // Start of an arbitrary value
// //
// E.g.: `data-[state=pending]:`. // E.g.: `data-[state=pending]:`.
@ -192,7 +192,7 @@ impl Machine for NamedVariantMachine<ParsingState> {
}; };
} }
Class::Underscore => match cursor.next.into() { Class::Underscore => match cursor.next().into() {
// Valid characters _if_ followed by another valid character. These characters are // Valid characters _if_ followed by another valid character. These characters are
// only valid inside of the variant but not at the end of the variant. // only valid inside of the variant but not at the end of the variant.
// //
@ -240,11 +240,11 @@ impl Machine for NamedVariantMachine<ParsingState> {
// E.g.: `2.5xl:flex` // E.g.: `2.5xl:flex`
// ^^^ // ^^^
Class::Dot => { Class::Dot => {
if !matches!(cursor.prev.into(), Class::Number) { if !matches!(cursor.prev().into(), Class::Number) {
return self.restart(); return self.restart();
} }
if !matches!(cursor.next.into(), Class::Number) { if !matches!(cursor.next().into(), Class::Number) {
return self.restart(); return self.restart();
} }
@ -263,7 +263,7 @@ impl Machine for NamedVariantMachine<ParsingState> {
impl NamedVariantMachine<ParsingState> { impl NamedVariantMachine<ParsingState> {
#[inline(always)] #[inline(always)]
fn parse_arbitrary_end(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn parse_arbitrary_end(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.next.into() { match cursor.next().into() {
Class::Slash => { Class::Slash => {
cursor.advance(); cursor.advance();
self.transition::<ParsingModifierState>().next(cursor) self.transition::<ParsingModifierState>().next(cursor)
@ -285,7 +285,7 @@ impl Machine for NamedVariantMachine<ParsingModifierState> {
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match self.modifier_machine.next(cursor) { match self.modifier_machine.next(cursor) {
MachineState::Idle => self.restart(), MachineState::Idle => self.restart(),
MachineState::Done(_) => match cursor.next.into() { MachineState::Done(_) => match cursor.next().into() {
// Modifier must be followed by a `:` // Modifier must be followed by a `:`
// //
// E.g.: `group-hover/name:` // E.g.: `group-hover/name:`
@ -308,7 +308,7 @@ impl Machine for NamedVariantMachine<ParsingEndState> {
#[inline(always)] #[inline(always)]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.curr.into() { match cursor.curr().into() {
// The end of a variant must be the `:` // The end of a variant must be the `:`
// //
// E.g.: `hover:` // E.g.: `hover:`

View file

@ -5,6 +5,23 @@ use bstr::ByteSlice;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Clojure; pub struct Clojure;
/// This is meant to be a rough estimate of a valid ClojureScript keyword
///
/// This can be approximated by the following regex:
/// /::?[a-zA-Z0-9!#$%&*+./:<=>?_|-]+/
///
/// However, keywords are intended to be detected as utilities. Since the set
/// of valid characters in a utility (outside of arbitrary values) is smaller,
/// along with the fact that neither `[]` nor `()` are allowed in keywords we
/// can simplify this list quite a bit.
#[inline]
fn is_keyword_character(byte: u8) -> bool {
(matches!(
byte,
b'!' | b'#' | b'%' | b'*' | b'+' | b'-' | b'.' | b'/' | b':' | b'_'
) | byte.is_ascii_alphanumeric())
}
impl PreProcessor for Clojure { impl PreProcessor for Clojure {
fn process(&self, content: &[u8]) -> Vec<u8> { fn process(&self, content: &[u8]) -> Vec<u8> {
let content = content let content = content
@ -15,18 +32,22 @@ impl PreProcessor for Clojure {
let mut cursor = cursor::Cursor::new(&content); let mut cursor = cursor::Cursor::new(&content);
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Consume strings as-is // Consume strings as-is
b'"' => { b'"' => {
result[cursor.pos] = b' ';
cursor.advance(); cursor.advance();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Escaped character, skip ahead to the next character // Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(), b'\\' => cursor.advance_twice(),
// End of the string // End of the string
b'"' => break, b'"' => {
result[cursor.pos] = b' ';
break;
}
// Everything else is valid // Everything else is valid
_ => cursor.advance(), _ => cursor.advance(),
@ -34,20 +55,140 @@ impl PreProcessor for Clojure {
} }
} }
// Consume comments as-is until the end of the line. // Discard line comments until the end of the line.
// Comments start with `;;` // Comments start with `;;`
b';' if matches!(cursor.next, b';') => { b';' if matches!(cursor.next(), b';') => {
while cursor.pos < len && cursor.curr != b'\n' { while cursor.pos < len && cursor.curr() != b'\n' {
result[cursor.pos] = b' ';
cursor.advance(); cursor.advance();
} }
} }
b':' | b'.' => { // Consume keyword until a terminating character is reached.
b':' => {
result[cursor.pos] = b' '; result[cursor.pos] = b' ';
cursor.advance();
while cursor.pos < len {
match cursor.curr() {
// A `.` surrounded by digits is a decimal number, so we don't want to replace it.
//
// E.g.:
// ```
// gap-1.5
// ^
// ```
b'.' if cursor.prev().is_ascii_digit()
&& cursor.next().is_ascii_digit() =>
{
// Keep the `.` as-is
}
// A `.` not surrounded by digits denotes the start of a new class name in a
// dot-delimited keyword.
//
// E.g.:
// ```
// flex.gap-1.5
// ^
// ```
b'.' => {
result[cursor.pos] = b' ';
}
// End of keyword.
_ if !is_keyword_character(cursor.curr()) => {
result[cursor.pos] = b' ';
break;
}
// Consume everything else.
_ => {}
};
cursor.advance();
}
} }
// Consume everything else // Handle quote with a list, e.g.: `'(…)`
_ => {} // and with a vector, e.g.: `'[…]`
b'\'' if matches!(cursor.next(), b'[' | b'(') => {
result[cursor.pos] = b' ';
cursor.advance();
result[cursor.pos] = b' ';
let end = match cursor.curr() {
b'[' => b']',
b'(' => b')',
_ => unreachable!(),
};
// Consume until the closing `]`
while cursor.pos < len {
match cursor.curr() {
x if x == end => {
result[cursor.pos] = b' ';
break;
}
// Consume strings as-is
b'"' => {
result[cursor.pos] = b' ';
cursor.advance();
while cursor.pos < len {
match cursor.curr() {
// Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(),
// End of the string
b'"' => {
result[cursor.pos] = b' ';
break;
}
// Everything else is valid
_ => cursor.advance(),
};
}
}
_ => {}
};
cursor.advance();
}
}
// Handle quote with a keyword, e.g.: `'bg-white`
b'\'' if !cursor.next().is_ascii_whitespace() => {
result[cursor.pos] = b' ';
cursor.advance();
while cursor.pos < len {
match cursor.curr() {
// End of keyword.
_ if !is_keyword_character(cursor.curr()) => {
result[cursor.pos] = b' ';
break;
}
// Consume everything else.
_ => {}
};
cursor.advance();
}
}
// Aggressively discard everything else, reducing false positives and preventing
// characters surrounding keywords from producing false negatives.
// E.g.:
// ```
// (when condition :bg-white)
// ^
// ```
// A ')' is never a valid part of a keyword, but will nonetheless prevent 'bg-white'
// from being extracted if not discarded.
_ => {
result[cursor.pos] = b' ';
}
}; };
cursor.advance(); cursor.advance();
@ -68,19 +209,23 @@ mod tests {
(":div.flex-1.flex-2", " div flex-1 flex-2"), (":div.flex-1.flex-2", " div flex-1 flex-2"),
( (
":.flex-3.flex-4 ;defaults to div", ":.flex-3.flex-4 ;defaults to div",
" flex-3 flex-4 ;defaults to div", " flex-3 flex-4 ",
), ),
("{:class :flex-5.flex-6", "{ flex-5 flex-6"), ("{:class :flex-5.flex-6", " flex-5 flex-6"),
(r#"{:class "flex-7 flex-8"}"#, r#"{ "flex-7 flex-8"}"#), (r#"{:class "flex-7 flex-8"}"#, r#" flex-7 flex-8 "#),
( (
r#"{:class ["flex-9" :flex-10]}"#, r#"{:class ["flex-9" :flex-10]}"#,
r#"{ ["flex-9" flex-10]}"#, r#" flex-9 flex-10 "#,
), ),
( (
r#"(dom/div {:class "flex-11 flex-12"})"#, r#"(dom/div {:class "flex-11 flex-12"})"#,
r#"(dom/div { "flex-11 flex-12"})"#, r#" flex-11 flex-12 "#,
),
("(dom/div :.flex-13.flex-14", " flex-13 flex-14"),
(
r#"[:div#hello.bg-white.pr-1.5 {:class ["grid grid-cols-[auto,1fr] grid-rows-2"]}]"#,
r#" div#hello bg-white pr-1.5 grid grid-cols-[auto,1fr] grid-rows-2 "#,
), ),
("(dom/div :.flex-13.flex-14", "(dom/div flex-13 flex-14"),
] { ] {
Clojure::test(input, expected); Clojure::test(input, expected);
} }
@ -156,4 +301,136 @@ mod tests {
Clojure::test_extract_contains(input, vec!["hover:flex", "px-1.5"]); Clojure::test_extract_contains(input, vec!["hover:flex", "px-1.5"]);
} }
// https://github.com/tailwindlabs/tailwindcss/issues/17760
#[test]
fn test_extraction_of_classes_with_dots() {
let input = r#"
($ :div {:class [:flex :gap-1.5 :p-1]} …)
"#;
Clojure::test_extract_contains(input, vec!["flex", "gap-1.5", "p-1"]);
}
// https://github.com/tailwindlabs/tailwindcss/issues/18336
#[test]
fn test_extraction_of_pseudoclasses_from_keywords() {
let input = r#"
($ :div {:class [:flex :first:lg:pr-6 :first:2xl:pl-6 :group-hover/2:2xs:pt-6]} …)
:.hover:bg-white
[:div#hello.bg-white.pr-1.5]
"#;
Clojure::test_extract_contains(
input,
vec![
"flex",
"first:lg:pr-6",
"first:2xl:pl-6",
"group-hover/2:2xs:pt-6",
"hover:bg-white",
"bg-white",
"pr-1.5",
],
);
}
// https://github.com/tailwindlabs/tailwindcss/issues/18344
#[test]
fn test_noninterference_of_parens_on_keywords() {
let input = r#"
(get props :y-padding :py-5)
($ :div {:class [:flex.pr-1.5 (if condition :bg-white :bg-black)]})
"#;
Clojure::test_extract_contains(
input,
vec!["py-5", "flex", "pr-1.5", "bg-white", "bg-black"],
);
}
// https://github.com/tailwindlabs/tailwindcss/issues/18882
#[test]
fn test_extract_from_symbol_list() {
let input = r#"
[:div {:class '[z-1 z-2
z-3 z-4]}]
"#;
Clojure::test_extract_contains(input, vec!["z-1", "z-2", "z-3", "z-4"]);
// https://github.com/tailwindlabs/tailwindcss/pull/18345#issuecomment-3253403847
let input = r#"
(def hl-class-names '[ring ring-blue-500])
[:div
{:class (cond-> '[input w-full]
textarea? (conj 'textarea)
(seq errors) (concat '[border-red-500 bg-red-100])
highlight? (concat hl-class-names))}]
"#;
Clojure::test_extract_contains(
input,
vec![
"ring",
"ring-blue-500",
"input",
"w-full",
"textarea",
"border-red-500",
"bg-red-100",
],
);
let input = r#"
[:div
{:class '[h-100 lg:h-200 max-w-32 mx-auto py-60
flex flex-col justify-end items-center
lg:flex-row lg:justify-between
bg-cover bg-center bg-no-repeat rounded-3xl overflow-hidden
font-semibold text-gray-900]}]
"#;
Clojure::test_extract_contains(
input,
vec![
"h-100",
"lg:h-200",
"max-w-32",
"mx-auto",
"py-60",
"flex",
"flex-col",
"justify-end",
"items-center",
"lg:flex-row",
"lg:justify-between",
"bg-cover",
"bg-center",
"bg-no-repeat",
"rounded-3xl",
"overflow-hidden",
"font-semibold",
"text-gray-900",
],
);
// `/` is invalid and requires explicit quoting
let input = r#"
'[p-32 "text-black/50"]
"#;
Clojure::test_extract_contains(input, vec!["p-32", "text-black/50"]);
// `[…]` is invalid and requires explicit quoting
let input = r#"
(print '[ring ring-blue-500 "bg-[#0088cc]"])
"#;
Clojure::test_extract_contains(input, vec!["ring", "ring-blue-500", "bg-[#0088cc]"]);
// `'(…)` looks similar to `[…]` but uses parentheses instead of brackets
let input = r#"
(print '(ring ring-blue-500 "bg-[#0088cc]"))
"#;
Clojure::test_extract_contains(input, vec!["ring", "ring-blue-500", "bg-[#0088cc]"]);
}
} }

View file

@ -0,0 +1,154 @@
use crate::cursor;
use crate::extractor::bracket_stack::BracketStack;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[derive(Debug, Default)]
pub struct Elixir;
impl PreProcessor for Elixir {
fn process(&self, content: &[u8]) -> Vec<u8> {
let mut cursor = cursor::Cursor::new(content);
let mut result = content.to_vec();
let mut bracket_stack = BracketStack::default();
while cursor.pos < content.len() {
// Look for a sigil marker
if cursor.curr() != b'~' {
cursor.advance();
continue;
}
// Scan charlists, strings, and wordlists
if !matches!(cursor.next(), b'c' | b'C' | b's' | b'S' | b'w' | b'W') {
cursor.advance();
continue;
}
cursor.advance_twice();
// Match the opening for a sigil
if !matches!(cursor.curr(), b'(' | b'[' | b'{') {
continue;
}
// Replace the opening bracket with a space
result[cursor.pos] = b' ';
// Scan until we find a balanced closing one and replace it too
bracket_stack.push(cursor.curr());
while cursor.pos < content.len() {
cursor.advance();
match cursor.curr() {
// Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(),
b'(' | b'[' | b'{' => {
bracket_stack.push(cursor.curr());
}
b')' | b']' | b'}' if !bracket_stack.is_empty() => {
bracket_stack.pop(cursor.curr());
if bracket_stack.is_empty() {
// Replace the closing bracket with a space
result[cursor.pos] = b' ';
break;
}
}
_ => {}
}
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::Elixir;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[test]
fn test_elixir_pre_processor() {
for (input, expected) in [
// Simple sigils
("~W(flex underline)", "~W flex underline "),
("~W[flex underline]", "~W flex underline "),
("~W{flex underline}", "~W flex underline "),
// Sigils with nested brackets
(
"~W(text-(--my-color) bg-(--my-color))",
"~W text-(--my-color) bg-(--my-color) ",
),
("~W[text-[red] bg-[red]]", "~W text-[red] bg-[red] "),
// Word sigils with modifiers
("~W(flex underline)a", "~W flex underline a"),
("~W(flex underline)c", "~W flex underline c"),
("~W(flex underline)s", "~W flex underline s"),
// Other sigil types
("~w(flex underline)", "~w flex underline "),
("~c(flex underline)", "~c flex underline "),
("~C(flex underline)", "~C flex underline "),
("~s(flex underline)", "~s flex underline "),
("~S(flex underline)", "~S flex underline "),
] {
Elixir::test(input, expected);
}
}
#[test]
fn test_extract_candidates() {
let input = r#"
~W(c1 c2)
~W[c3 c4]
~W{c5 c6}
~W(text-(--c7) bg-(--c8))
~W[text-[c9] bg-[c10]]
~W(c13 c14)a
~W(c15 c16)c
~W(c17 c18)s
~w(c19 c20)
~c(c21 c22)
~C(c23 c24)
~s(c25 c26)
~S(c27 c28)
~W"c29 c30"
~W'c31 c32'
"#;
Elixir::test_extract_contains(
input,
vec![
"c1",
"c2",
"c3",
"c4",
"c5",
"c6",
"text-(--c7)",
"bg-(--c8)",
"c13",
"c14",
"c15",
"c16",
"c17",
"c18",
"c19",
"c20",
"c21",
"c22",
"c23",
"c24",
"c25",
"c26",
"c27",
"c28",
"c29",
"c30",
"c31",
"c32",
],
);
}
}

View file

@ -3,6 +3,8 @@ use crate::extractor::bracket_stack::BracketStack;
use crate::extractor::machine::{Machine, MachineState}; use crate::extractor::machine::{Machine, MachineState};
use crate::extractor::pre_processors::pre_processor::PreProcessor; use crate::extractor::pre_processors::pre_processor::PreProcessor;
use crate::extractor::variant_machine::VariantMachine; use crate::extractor::variant_machine::VariantMachine;
use crate::scanner::pre_process_input;
use bstr::ByteVec;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Haml; pub struct Haml;
@ -14,8 +16,153 @@ impl PreProcessor for Haml {
let mut cursor = cursor::Cursor::new(content); let mut cursor = cursor::Cursor::new(content);
let mut bracket_stack = BracketStack::default(); let mut bracket_stack = BracketStack::default();
// Haml Comments: -#
// https://haml.info/docs/yardoc/file.REFERENCE.html#ruby-evaluation
//
// > The hyphen followed immediately by the pound sign signifies a silent comment. Any text
// > following this isn’t rendered in the resulting document at all.
//
// ```haml
// %p foo
// -# This is a comment
// %p bar
// ```
//
// > You can also nest text beneath a silent comment. None of this text will be rendered.
//
// ```haml
// %p foo
// -#
// This won't be displayed
// Nor will this
// Nor will this.
// %p bar
// ```
//
// Ruby Evaluation
// https://haml.info/docs/yardoc/file.REFERENCE.html#ruby-evaluation
//
// When any of the following characters are the first non-whitespace character on the line,
// then the line is treated as Ruby code:
//
// - Inserting Ruby: =
// https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby
//
// ```haml
// %p
// = ['hi', 'there', 'reader!'].join " "
// = "yo"
// ```
//
// - Running Ruby: -
// https://haml.info/docs/yardoc/file.REFERENCE.html#running-ruby--
//
// ```haml
// - foo = "hello"
// - foo << " there"
// - foo << " you!"
// %p= foo
// ```
//
// - Whitespace Preservation: ~
// https://haml.info/docs/yardoc/file.REFERENCE.html#tilde
//
// > ~ works just like =, except that it runs Haml::Helpers.preserve on its input.
//
// ```haml
// ~ "Foo\n<pre>Bar\nBaz</pre>"
// ```
//
// Important note:
//
// > A line of Ruby code can be stretched over multiple lines as long as each line but the
// > last ends with a comma.
//
// ```haml
// - links = {:home => "/",
// :docs => "/docs",
// :about => "/about"}
// ```
//
// Ruby Blocks:
// https://haml.info/docs/yardoc/file.REFERENCE.html#ruby-blocks
//
// > Ruby blocks, like XHTML tags, don’t need to be explicitly closed in Haml. Rather,
// > they’re automatically closed, based on indentation. A block begins whenever the
// > indentation is increased after a Ruby evaluation command. It ends when the indentation
// > decreases (as long as it’s not an else clause or something similar).
//
// ```haml
// - (42...47).each do |i|
// %p= i
// %p See, I can count!
// ```
//
let mut last_newline_position = 0;
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Escape the next character
b'\\' => {
cursor.advance_twice();
continue;
}
// Track the last newline position
b'\n' => {
last_newline_position = cursor.pos;
cursor.advance();
continue;
}
// Skip HAML comments. `-#`
b'-' if cursor.input[last_newline_position..cursor.pos]
.iter()
.all(u8::is_ascii_whitespace)
&& matches!(cursor.next(), b'#') =>
{
// Just consume the comment
let updated_last_newline_position =
self.skip_indented_block(&mut cursor, last_newline_position);
// Override the last known newline position
last_newline_position = updated_last_newline_position;
}
// Skip HTML comments. `/`
b'/' if cursor.input[last_newline_position..cursor.pos]
.iter()
.all(u8::is_ascii_whitespace) =>
{
// Just consume the comment
let updated_last_newline_position =
self.skip_indented_block(&mut cursor, last_newline_position);
// Override the last known newline position
last_newline_position = updated_last_newline_position;
}
// Ruby evaluation
b'-' | b'=' | b'~'
if cursor.input[last_newline_position..cursor.pos]
.iter()
.all(u8::is_ascii_whitespace) =>
{
let mut start = cursor.pos;
let end = self.skip_indented_block(&mut cursor, last_newline_position);
// Increment start with 1 character to skip the `=` or `-` character
start += 1;
let ruby_code = &cursor.input[start..end];
// Override the last known newline position
last_newline_position = end;
let replaced = pre_process_input(ruby_code.to_vec(), "rb");
result.replace_range(start..end, replaced);
}
// Only replace `.` with a space if it's not surrounded by numbers. E.g.: // Only replace `.` with a space if it's not surrounded by numbers. E.g.:
// //
// ```diff // ```diff
@ -43,7 +190,7 @@ impl PreProcessor for Haml {
// digit. // digit.
// E.g.: `bg-red-500.2xl:flex` // E.g.: `bg-red-500.2xl:flex`
// ^^^ // ^^^
if cursor.prev.is_ascii_digit() && cursor.next.is_ascii_digit() { if cursor.prev().is_ascii_digit() && cursor.next().is_ascii_digit() {
let mut next_cursor = cursor.clone(); let mut next_cursor = cursor.clone();
next_cursor.advance(); next_cursor.advance();
@ -56,6 +203,81 @@ impl PreProcessor for Haml {
} }
} }
// Handle Ruby syntax with `%w[]` arrays embedded in Haml attribute hashes. E.g.:
//
// ```haml
// %div{class: %w[bg-blue-500 w-10 h-10]}
// ```
//
// A `%` that follows a value is not a percent literal. E.g.: the `50%w` in
// `hit rate 50%w.`
b'%' if matches!(cursor.next(), b'w' | b'W')
&& !cursor.prev().is_ascii_alphanumeric()
&& !matches!(cursor.prev(), b'_' | b')' | b']' | b'}') =>
{
// Boundary characters
let (open, close) = match cursor.input.get(cursor.pos + 2) {
Some(b'[') => (b'[', b']'),
Some(b'(') => (b'(', b')'),
Some(b'{') => (b'{', b'}'),
Some(b'<') => (b'<', b'>'),
// Any other ASCII punctuation can be used as a custom delimiter
Some(&c) if c.is_ascii_punctuation() => (c, c),
// Everything else is not a valid delimiter
_ => {
cursor.advance();
continue;
}
};
result[cursor.pos] = b' '; // Replace `%`
cursor.advance();
result[cursor.pos] = b' '; // Replace `w`
cursor.advance();
result[cursor.pos] = b' '; // Replace the opening delimiter
cursor.advance();
// Paired delimiters can be nested as long as they are balanced. E.g.:
// `%w[foo[bar]baz]` produces a flat array.
let mut depth = 1_usize;
while cursor.pos < len {
match cursor.curr() {
// Skip escaped characters, unless the backslash is the delimiter
// itself
b'\\' if close != b'\\' => {
// Use backslash to embed spaces in the strings.
if cursor.next() == b' ' {
result[cursor.pos] = b' ';
}
cursor.advance();
}
// Start of a nested delimiter pair
c if c == open && open != close => depth += 1,
// Closing delimiter
c if c == close => {
depth -= 1;
// End of the literal, replace the closing delimiter with a space
if depth == 0 {
result[cursor.pos] = b' ';
break;
}
}
// Everything else is valid content
_ => {}
}
cursor.advance();
}
}
// Replace following characters with spaces if they are not inside of brackets // Replace following characters with spaces if they are not inside of brackets
b'#' | b'=' if bracket_stack.is_empty() => { b'#' | b'=' if bracket_stack.is_empty() => {
result[cursor.pos] = b' '; result[cursor.pos] = b' ';
@ -66,11 +288,11 @@ impl PreProcessor for Haml {
if bracket_stack.is_empty() { if bracket_stack.is_empty() {
result[cursor.pos] = b' '; result[cursor.pos] = b' ';
} }
bracket_stack.push(cursor.curr); bracket_stack.push(cursor.curr());
} }
b')' | b']' | b'}' if !bracket_stack.is_empty() => { b')' | b']' | b'}' if !bracket_stack.is_empty() => {
bracket_stack.pop(cursor.curr); bracket_stack.pop(cursor.curr());
// Replace closing bracket with a space // Replace closing bracket with a space
if bracket_stack.is_empty() { if bracket_stack.is_empty() {
@ -89,6 +311,112 @@ impl PreProcessor for Haml {
} }
} }
impl Haml {
fn skip_indented_block(
&self,
cursor: &mut cursor::Cursor,
last_known_newline_position: usize,
) -> usize {
let len = cursor.input.len();
// Special case: if the first character of the block is `=`, then newlines are only allowed
// _if_ the last character of the previous line is a comma `,`.
//
// https://haml.info/docs/yardoc/file.REFERENCE.html#inserting_ruby
//
// > A line of Ruby code can be stretched over multiple lines as long as each line but the
// > last ends with a comma. For example:
//
// ```haml
// = link_to_remote "Add to cart",
// :url => { :action => "add", :id => product.id },
// :update => { :success => "cart", :failure => "error" }
// ```
let evaluation_type = cursor.curr();
let block_indentation_level = cursor
.pos
.saturating_sub(last_known_newline_position)
.saturating_sub(1); /* The newline itself */
let mut last_newline_position = last_known_newline_position;
// Consume until the end of the line first
while cursor.pos < len && cursor.curr() != b'\n' {
cursor.advance();
}
// Block is already done, aka just a line
if evaluation_type == b'=' && cursor.prev() != b',' {
return cursor.pos;
}
'outer: while cursor.pos < len {
match cursor.curr() {
// Escape the next character
b'\\' => {
cursor.advance_twice();
continue;
}
// Track the last newline position
b'\n' => {
last_newline_position = cursor.pos;
// We are done with this block
if evaluation_type == b'=' && cursor.prev() != b',' {
break;
}
cursor.advance();
continue;
}
// Skip whitespace and compute the indentation level
x if x.is_ascii_whitespace() => {
// Find first non-whitespace character
while cursor.pos < len && cursor.curr().is_ascii_whitespace() {
if cursor.curr() == b'\n' {
last_newline_position = cursor.pos;
if evaluation_type == b'=' && cursor.prev() != b',' {
// We are done with this block
break 'outer;
}
}
cursor.advance();
}
let indentation = cursor
.pos
.saturating_sub(last_newline_position)
.saturating_sub(1); /* The newline itself */
if indentation < block_indentation_level {
// We are done with this block
break;
}
}
// Not whitespace, end of block
_ => break,
};
cursor.advance();
}
// We didn't find a newline, we reached the end of the input
if last_known_newline_position == last_newline_position {
return cursor.pos;
}
// Move the cursor to the last newline position
cursor.move_to(last_newline_position);
last_newline_position
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::Haml; use super::Haml;
@ -171,14 +499,97 @@ mod tests {
); );
} }
// https://github.com/tailwindlabs/tailwindcss/issues/20386
#[test]
fn test_embedded_ruby_percent_w_delimiters() {
for (input, expected) in [
// %w[…] in an attribute hash
(
"%div{class: %w[flex px-2.5]}",
"%div class: flex px-2.5 ",
),
// %w<…>
(
"%div{class: %w<flex px-2.5>}",
"%div class: flex px-2.5 ",
),
// Nested `<…>` does not end the literal
(
"%div{class: %w<flex <nested> px-2.5>}",
"%div class: flex <nested> px-2.5 ",
),
// Custom delimiters
(
"%div{class: %w|flex px-2.5|}",
"%div class: flex px-2.5 ",
),
(
"%div{class: %W!flex px-2.5!}",
"%div class: flex px-2.5 ",
),
(
"%div{class: %w#text-sm leading-6#}",
"%div class: text-sm leading-6 ",
),
(
"%div{class: %w=italic tracking-wide=}",
"%div class: italic tracking-wide ",
),
// Nested paired delimiters stay balanced inside the literal
(
"%div{class: %w[content-['[hello]'] p-4]}",
"%div class: content-['[hello]'] p-4 ",
),
// Escaped spaces embed a space in a single array element
(
r#"%div{class: %w[foo\ bar baz-1]}"#,
r#"%div class: foo bar baz-1 "#,
),
// A `%` that follows a value is not a percent literal
("%p hit rate 50%w.", "%p hit rate 50%w "),
] {
Haml::test(input, expected);
}
let input = r#"
%div{class: %w[bg-blue-500 w-10 h-10]}
%div{class: %w<flex px-2.5>}
%div{class: %w|underline font-bold|}
- classes = %w<mt-4 grid>
"#;
Haml::test_extract_contains(
input,
vec![
"bg-blue-500",
"w-10",
"h-10",
"flex",
"px-2.5",
"underline",
"font-bold",
"mt-4",
"grid",
],
);
}
// https://github.com/tailwindlabs/tailwindcss/pull/17051#issuecomment-2711181352 // https://github.com/tailwindlabs/tailwindcss/pull/17051#issuecomment-2711181352
#[test] #[test]
fn test_haml_full_file() { fn test_haml_full_file_17051() {
let processed = Haml.process(include_bytes!("./test-fixtures/haml/src-1.haml")); let actual = Haml::extract_annotated(include_bytes!("./test-fixtures/haml/src-17051.haml"));
let actual = std::str::from_utf8(&processed).unwrap(); let expected = include_str!("./test-fixtures/haml/dst-17051.haml");
let expected = include_str!("./test-fixtures/haml/dst-1.haml");
assert_eq!(actual, expected); assert_eq!(actual.replace("\r\n", "\n"), expected.replace("\r\n", "\n"));
}
// https://github.com/tailwindlabs/tailwindcss/issues/17813
#[test]
fn test_haml_full_file_17813() {
let actual = Haml::extract_annotated(include_bytes!("./test-fixtures/haml/src-17813.haml"));
let expected = include_str!("./test-fixtures/haml/dst-17813.haml");
assert_eq!(actual.replace("\r\n", "\n"), expected.replace("\r\n", "\n"));
} }
#[test] #[test]
@ -190,4 +601,16 @@ mod tests {
"#; "#;
Haml::test_extract_contains(input, vec!["flex", "items-center"]); Haml::test_extract_contains(input, vec!["flex", "items-center"]);
} }
// https://github.com/tailwindlabs/tailwindcss/issues/17379#issuecomment-2910108646
#[test]
fn test_crash_missing_newline() {
// The empty `""` will introduce a newline
let good = ["- index = 0", "- index += 1", ""].join("\n");
Haml::test_extract_contains(&good, vec!["index"]);
// This used to crash before the fix
let bad = ["- index = 0", "- index += 1"].join("\n");
Haml::test_extract_contains(&bad, vec!["index"]);
}
} }

View file

@ -11,13 +11,13 @@ impl PreProcessor for Json {
let mut cursor = cursor::Cursor::new(content); let mut cursor = cursor::Cursor::new(content);
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Consume strings as-is // Consume strings as-is
b'"' => { b'"' => {
cursor.advance(); cursor.advance();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Escaped character, skip ahead to the next character // Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(), b'\\' => cursor.advance_twice(),

View file

@ -0,0 +1,83 @@
use crate::cursor;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[derive(Debug, Default)]
pub struct Markdown;
impl PreProcessor for Markdown {
fn process(&self, content: &[u8]) -> Vec<u8> {
let len = content.len();
let mut result = content.to_vec();
let mut cursor = cursor::Cursor::new(content);
let mut bracket_stack = vec![];
let mut in_directive = false;
while cursor.pos < len {
match (in_directive, cursor.curr()) {
(false, b'{') => {
result[cursor.pos] = b' ';
in_directive = true;
}
(true, b'(' | b'[' | b'{' | b'<') => {
bracket_stack.push(cursor.curr());
}
(true, b')' | b']' | b'}' | b'>') if !bracket_stack.is_empty() => {
bracket_stack.pop();
}
(true, b'}') => {
result[cursor.pos] = b' ';
in_directive = false;
}
(true, b'.') if bracket_stack.is_empty() => {
result[cursor.pos] = b' ';
}
_ => {}
}
cursor.advance();
}
result
}
}
#[cfg(test)]
mod tests {
use super::Markdown;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[test]
fn test_markdown_pre_processor() {
for (input, expected) in [
// Convert dots to spaces inside markdown inline directives
(
":span[Some Text]{.text-gray-500}",
":span[Some Text] text-gray-500 ",
),
(
":span[Some Text]{.text-gray-500.bg-red-500}",
":span[Some Text] text-gray-500 bg-red-500 ",
),
(
":span[Some Text]{#myId .my-class key=val key2='val 2'}",
":span[Some Text] #myId my-class key=val key2='val 2' ",
),
] {
Markdown::test(input, expected);
}
}
#[test]
fn test_nested_classes_keep_the_dots() {
for (input, expected) in [
(
r#"{<div class="px-2.5"></div>}"#,
r#" <div class="px-2.5"></div> "#,
),
(r#"{content-['example.js']}"#, r#" content-['example.js'] "#),
] {
Markdown::test(input, expected);
}
}
}

View file

@ -1,21 +1,31 @@
pub mod clojure; pub mod clojure;
pub mod elixir;
pub mod haml; pub mod haml;
pub mod json; pub mod json;
pub mod markdown;
pub mod pre_processor; pub mod pre_processor;
pub mod pug; pub mod pug;
pub mod razor; pub mod razor;
pub mod ruby; pub mod ruby;
pub mod rust;
pub mod slim; pub mod slim;
pub mod svelte; pub mod svelte;
pub mod template_toolkit;
pub mod twig;
pub mod vue; pub mod vue;
pub use clojure::*; pub use clojure::*;
pub use elixir::*;
pub use haml::*; pub use haml::*;
pub use json::*; pub use json::*;
pub use markdown::*;
pub use pre_processor::*; pub use pre_processor::*;
pub use pug::*; pub use pug::*;
pub use razor::*; pub use razor::*;
pub use ruby::*; pub use ruby::*;
pub use rust::*;
pub use slim::*; pub use slim::*;
pub use svelte::*; pub use svelte::*;
pub use template_toolkit::*;
pub use twig::*;
pub use vue::*; pub use vue::*;

View file

@ -25,7 +25,33 @@ pub trait PreProcessor: Sized + Default {
} }
#[cfg(test)] #[cfg(test)]
fn test_extract_contains(input: &str, items: Vec<&str>) { fn test_extract_exact(input: &str, expected: Vec<&str>) {
use crate::extractor::{Extracted, Extractor};
let input = input.as_bytes();
let processor = Self::default();
let transformed = processor.process(input);
let extracted = Extractor::new(&transformed).extract();
// Extract all candidates and css variables.
let candidates = extracted
.iter()
.filter_map(|x| match x {
Extracted::Candidate(bytes) => std::str::from_utf8(bytes).ok(),
Extracted::CssVariable(bytes) => std::str::from_utf8(bytes).ok(),
})
.collect::<Vec<_>>();
if candidates != expected {
dbg!(&candidates, &expected);
panic!("Extracted candidates do not match expected candidates");
}
}
#[cfg(test)]
fn test_extract_contains(input: &str, expected: Vec<&str>) {
use crate::extractor::{Extracted, Extractor}; use crate::extractor::{Extracted, Extractor};
let input = input.as_bytes(); let input = input.as_bytes();
@ -46,7 +72,7 @@ pub trait PreProcessor: Sized + Default {
// Ensure all items are present in the candidates. // Ensure all items are present in the candidates.
let mut missing = vec![]; let mut missing = vec![];
for item in &items { for item in &expected {
if !candidates.contains(item) { if !candidates.contains(item) {
missing.push(item); missing.push(item);
} }
@ -57,4 +83,104 @@ pub trait PreProcessor: Sized + Default {
panic!("Missing some items"); panic!("Missing some items");
} }
} }
#[cfg(test)]
fn extract_annotated(input: &[u8]) -> String {
use crate::extractor::{Extracted, Extractor};
use std::collections::BTreeMap;
use unicode_width::UnicodeWidthStr;
let processor = Self::default();
let transformed = processor.process(input);
let extracted = Extractor::new(&transformed).extract();
// Extract only candidate positions
let byte_ranges = extracted
.iter()
.filter_map(|x| match x {
Extracted::Candidate(bytes) => {
let start = bytes.as_ptr() as usize - transformed.as_ptr() as usize;
let end = start + bytes.len();
Some((start, end))
}
_ => None,
})
.collect::<Vec<_>>();
// Convert byte ranges to (line, start_col, end_col)
let mut annotations = byte_ranges
.into_iter()
.map(|(start, end)| {
let (line, start_col) = byte_offset_to_line_and_column(input, start);
let (_, end_col) = byte_offset_to_line_and_column(input, end);
(line, start_col, end_col)
})
.collect::<Vec<_>>();
// Sort for safe insertion
annotations.sort_by(|a, b| b.0.cmp(&a.0).then(b.1.cmp(&a.1)));
// Convert input to lines
let mut lines = std::str::from_utf8(input)
.expect("Input must be valid UTF-8")
.lines()
.map(|line| line.to_string())
.collect::<Vec<_>>();
// Group annotations per line
let mut grouped = BTreeMap::<usize, Vec<(usize, usize)>>::new();
for (line, start_char, end_char) in annotations {
grouped
.entry(line)
.or_default()
.push((start_char, end_char));
}
// Inject annotation lines
for (line_idx, spans) in grouped.into_iter().rev() {
let display_line = &lines[line_idx];
let width = UnicodeWidthStr::width(display_line.as_str());
let mut annotation = vec![' '; width];
for (start, end) in spans {
for i in start..end.min(annotation.len()) {
annotation[i] = '^';
}
}
let annotation_line: String = annotation
.into_iter()
.collect::<String>()
.trim_end()
.to_owned();
lines.insert(line_idx + 1, annotation_line);
}
lines.join("\n").trim_end().to_string() + "\n"
}
}
#[cfg(test)]
fn byte_offset_to_line_and_column(input: &[u8], offset: usize) -> (usize, usize) {
use unicode_width::UnicodeWidthStr;
let mut line_start = 0;
let mut line = 0;
for (i, &b) in input.iter().enumerate() {
if i >= offset {
break;
}
if b == b'\n' {
line += 1;
line_start = i + 1;
}
}
let slice = &input[line_start..offset];
let column = std::str::from_utf8(slice).expect("Valid UTF-8");
let column = UnicodeWidthStr::width(column);
(line, column)
} }

View file

@ -15,7 +15,7 @@ impl PreProcessor for Pug {
let mut bracket_stack = BracketStack::default(); let mut bracket_stack = BracketStack::default();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Only replace `.` with a space if it's not surrounded by numbers. E.g.: // Only replace `.` with a space if it's not surrounded by numbers. E.g.:
// //
// ```diff // ```diff
@ -43,7 +43,7 @@ impl PreProcessor for Pug {
// digit. // digit.
// E.g.: `bg-red-500.2xl:flex` // E.g.: `bg-red-500.2xl:flex`
// ^^^ // ^^^
if cursor.prev.is_ascii_digit() && cursor.next.is_ascii_digit() { if cursor.prev().is_ascii_digit() && cursor.next().is_ascii_digit() {
let mut next_cursor = cursor.clone(); let mut next_cursor = cursor.clone();
next_cursor.advance(); next_cursor.advance();
@ -68,17 +68,17 @@ impl PreProcessor for Pug {
// //
// However, we also need to make sure that we keep the parens that are part of the // However, we also need to make sure that we keep the parens that are part of the
// utility class. E.g.: `bg-(--my-color)`. // utility class. E.g.: `bg-(--my-color)`.
b'(' if bracket_stack.is_empty() && !matches!(cursor.prev, b'-' | b'/') => { b'(' if bracket_stack.is_empty() && !matches!(cursor.prev(), b'-' | b'/') => {
result[cursor.pos] = b' '; result[cursor.pos] = b' ';
bracket_stack.push(cursor.curr); bracket_stack.push(cursor.curr());
} }
b'(' | b'[' | b'{' => { b'(' | b'[' | b'{' => {
bracket_stack.push(cursor.curr); bracket_stack.push(cursor.curr());
} }
b')' | b']' | b'}' if !bracket_stack.is_empty() => { b')' | b']' | b'}' if !bracket_stack.is_empty() => {
bracket_stack.pop(cursor.curr); bracket_stack.pop(cursor.curr());
} }
// Consume everything else // Consume everything else

View file

@ -34,51 +34,135 @@ impl PreProcessor for Ruby {
// Extract embedded template languages // Extract embedded template languages
// https://viewcomponent.org/guide/templates.html#interpolations // https://viewcomponent.org/guide/templates.html#interpolations
let content_as_str = std::str::from_utf8(content).unwrap(); // Only process if content is valid UTF-8, otherwise skip HEREDOC extraction
// but still perform the byte-level Ruby processing below
if let Ok(content_as_str) = std::str::from_utf8(content) {
let starts = TEMPLATE_START_REGEX
.captures_iter(content_as_str)
.collect::<Vec<_>>();
let ends = TEMPLATE_END_REGEX
.captures_iter(content_as_str)
.collect::<Vec<_>>();
let starts = TEMPLATE_START_REGEX for start in starts.iter() {
.captures_iter(content_as_str) // The language for this block
.collect::<Vec<_>>(); let lang = start.get(1).unwrap().as_str();
let ends = TEMPLATE_END_REGEX
.captures_iter(content_as_str)
.collect::<Vec<_>>();
for start in starts.iter() { // The HEREDOC delimiter
// The language for this block let delimiter_start = start.get(2).unwrap().as_str();
let lang = start.get(1).unwrap().as_str();
// The HEREDOC delimiter // Where the "body" starts for the HEREDOC block
let delimiter_start = start.get(2).unwrap().as_str(); let body_start = start.get(0).unwrap().end();
// Where the "body" starts for the HEREDOC block // Look through all of the ends to find a matching language
let body_start = start.get(0).unwrap().end(); for end in ends.iter() {
// 1. This must appear after the start
let body_end = end.get(0).unwrap().start();
if body_end < body_start {
continue;
}
// Look through all of the ends to find a matching language // The languages must match otherwise we haven't found the end
for end in ends.iter() { let delimiter_end = end.get(1).unwrap().as_str();
// 1. This must appear after the start if delimiter_end != delimiter_start {
let body_end = end.get(0).unwrap().start(); continue;
if body_end < body_start { }
continue;
let body = &content_as_str[body_start..body_end];
let replaced =
pre_process_input(body.as_bytes().to_vec(), &lang.to_ascii_lowercase());
result.replace_range(body_start..body_end, replaced);
break;
} }
// The languages must match otherwise we haven't found the end
let delimiter_end = end.get(1).unwrap().as_str();
if delimiter_end != delimiter_start {
continue;
}
let body = &content_as_str[body_start..body_end];
let replaced = pre_process_input(body.as_bytes(), &lang.to_ascii_lowercase());
result.replace_range(body_start..body_end, replaced);
break;
} }
} }
// Ruby extraction // Ruby extraction
while cursor.pos < len { while cursor.pos < len {
// Looking for `%w` or `%W` match cursor.curr() {
if cursor.curr != b'%' && !matches!(cursor.next, b'w' | b'W') { b'"' => {
cursor.advance();
while cursor.pos < len {
match cursor.curr() {
// Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(),
// End of the string
b'"' => break,
// Everything else is valid
_ => cursor.advance(),
};
}
cursor.advance();
continue;
}
b'\'' => {
cursor.advance();
while cursor.pos < len {
match cursor.curr() {
// Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(),
// End of the string
b'\'' => break,
// Everything else is valid
_ => cursor.advance(),
};
}
cursor.advance();
continue;
}
// Replace comments in Ruby files
//
// Except for strict locals, these are defined in a `<%# locals: … %>`. Checking if
// the comment is preceded by a `%` should be enough without having to perform more
// parsing logic. Worst case we _do_ scan a few comments.
//
// We also want to skip interpolation syntax, which look like `#{…}`.
b'#' if !matches!(cursor.prev(), b'%') && !matches!(cursor.next(), b'{') => {
result[cursor.pos] = b' ';
cursor.advance();
while cursor.pos < len {
match cursor.curr() {
// End of the comment
b'\n' => break,
// Everything else is part of the comment and replaced
_ => {
result[cursor.pos] = b' ';
cursor.advance();
}
};
}
cursor.advance();
continue;
}
_ => {}
}
// Looking for `%w`, `%W`, or `%p`
if cursor.curr() != b'%' || !matches!(cursor.next(), b'w' | b'W' | b'p') {
cursor.advance();
continue;
}
// A `%` that follows a value is a modulo operation, not a percent literal. E.g.: the
// `50%w` in `hit rate 50%w.`
if cursor.prev().is_ascii_alphanumeric()
|| matches!(cursor.prev(), b'_' | b')' | b']' | b'}')
{
cursor.advance(); cursor.advance();
continue; continue;
} }
@ -86,10 +170,17 @@ impl PreProcessor for Ruby {
cursor.advance_twice(); cursor.advance_twice();
// Boundary character // Boundary character
let boundary = match cursor.curr { let boundary = match cursor.curr() {
b'[' => b']', b'[' => b']',
b'(' => b')', b'(' => b')',
b'{' => b'}', b'{' => b'}',
b'<' => b'>',
b' ' => b'\n',
// Any other ASCII punctuation can be used as a custom delimiter
c if c.is_ascii_punctuation() => c,
// Everything else is not a valid delimiter
_ => { _ => {
cursor.advance(); cursor.advance();
continue; continue;
@ -105,11 +196,11 @@ impl PreProcessor for Ruby {
cursor.advance(); cursor.advance();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Skip escaped characters // Skip escaped characters, unless the backslash is the delimiter itself
b'\\' => { b'\\' if boundary != b'\\' => {
// Use backslash to embed spaces in the strings. // Use backslash to embed spaces in the strings.
if cursor.next == b' ' { if cursor.next() == b' ' {
result[cursor.pos] = b' '; result[cursor.pos] = b' ';
} }
@ -118,20 +209,36 @@ impl PreProcessor for Ruby {
// Start of a nested bracket // Start of a nested bracket
b'[' | b'(' | b'{' => { b'[' | b'(' | b'{' => {
bracket_stack.push(cursor.curr); bracket_stack.push(cursor.curr());
}
// Start of a nested `<…>`, which Ruby allows inside a `%w<…>` literal
b'<' if boundary == b'>' => {
bracket_stack.push(cursor.curr());
} }
// End of a nested bracket // End of a nested bracket
b']' | b')' | b'}' if !bracket_stack.is_empty() => { b']' | b')' | b'}' if !bracket_stack.is_empty() => {
if !bracket_stack.pop(cursor.curr) { if !bracket_stack.pop(cursor.curr()) {
// Unbalanced
cursor.advance();
}
}
// End of a nested `<…>`
b'>' if boundary == b'>' && !bracket_stack.is_empty() => {
if !bracket_stack.pop(cursor.curr()) {
// Unbalanced // Unbalanced
cursor.advance(); cursor.advance();
} }
} }
// End of the pattern, replace the boundary character with a space // End of the pattern, replace the boundary character with a space
_ if cursor.curr == boundary => { _ if cursor.curr() == boundary => {
result[cursor.pos] = b' '; if boundary != b'\n' {
result[cursor.pos] = b' ';
}
break; break;
} }
@ -173,12 +280,69 @@ mod tests {
"%w(flex data-[state=pending]:bg-(--my-color) flex-col)", "%w(flex data-[state=pending]:bg-(--my-color) flex-col)",
"%w flex data-[state=pending]:bg-(--my-color) flex-col ", "%w flex data-[state=pending]:bg-(--my-color) flex-col ",
), ),
// %w<…>
("%w<flex px-2.5>", "%w flex px-2.5 "),
(
"%w<flex data-[state=pending]:bg-(--my-color) flex-col>",
"%w flex data-[state=pending]:bg-(--my-color) flex-col ",
),
// Nested `<…>` does not end the literal
("%w<flex <nested> px-2.5>", "%w flex <nested> px-2.5 "),
// %w|…|, %w:…:, %w!…!
("%w|flex px-2.5|", "%w flex px-2.5 "),
("%w:flex px-2.5:", "%w flex px-2.5 "),
("%w!flex px-2.5!", "%w flex px-2.5 "),
(r#"%w\flex px-2.5\"#, r#"%w flex px-2.5 "#),
// A `%` that follows a value is a modulo operation, not a percent literal
(
"hit rate 50%w.\n%w[flex px-2.5]",
"hit rate 50%w.\n%w flex px-2.5 ",
),
// %w …\n
("%w flex px-2.5\n", "%w flex px-2.5\n"),
// Use backslash to embed spaces in the strings. // Use backslash to embed spaces in the strings.
(r#"%w[foo\ bar baz\ bat]"#, r#"%w foo bar baz bat "#), (r#"%w[foo\ bar baz\ bat]"#, r#"%w foo bar baz bat "#),
(r#"%W[foo\ bar baz\ bat]"#, r#"%W foo bar baz bat "#), (r#"%W[foo\ bar baz\ bat]"#, r#"%W foo bar baz bat "#),
// The nested delimiters evaluated to a flat array of strings // The nested delimiters evaluated to a flat array of strings
// (not nested array). // (not nested array).
(r#"%w[foo[bar baz]qux]"#, r#"%w foo[bar baz]qux "#), (r#"%w[foo[bar baz]qux]"#, r#"%w foo[bar baz]qux "#),
(
"# test\n# test\n# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]\n%w[flex px-2.5]",
" \n \n \n%w flex px-2.5 "
),
(r#""foo # bar""#, r#""foo # bar""#),
(r#"'foo # bar'"#, r#"'foo # bar'"#),
(
r#"def call = tag.span "Foo", class: %w[rounded-full h-0.75 w-0.75]"#,
r#"def call = tag.span "Foo", class: %w rounded-full h-0.75 w-0.75 "#
),
(r#"%w[foo ' bar]"#, r#"%w foo ' bar "#),
(r#"%w[foo " bar]"#, r#"%w foo " bar "#),
(r#"%W[foo ' bar]"#, r#"%W foo ' bar "#),
(r#"%W[foo " bar]"#, r#"%W foo " bar "#),
(r#"%p foo ' bar "#, r#"%p foo ' bar "#),
(r#"%p foo " bar "#, r#"%p foo " bar "#),
(
"%p has a ' quote\n# this should be removed\n%p has a ' quote",
"%p has a ' quote\n \n%p has a ' quote"
),
(
"%p has a \" quote\n# this should be removed\n%p has a \" quote",
"%p has a \" quote\n \n%p has a \" quote"
),
(
"%w#this text is kept# # this text is not",
"%w this text is kept ",
),
] { ] {
Ruby::test(input, expected); Ruby::test(input, expected);
} }
@ -211,6 +375,30 @@ mod tests {
"%w(flex data-[state=pending]:bg-(--my-color) flex-col)", "%w(flex data-[state=pending]:bg-(--my-color) flex-col)",
vec!["flex", "data-[state=pending]:bg-(--my-color)", "flex-col"], vec!["flex", "data-[state=pending]:bg-(--my-color)", "flex-col"],
), ),
// %w<…>
("%w<flex px-2.5>", vec!["flex", "px-2.5"]),
("%w<px-2.5 flex>", vec!["flex", "px-2.5"]),
("%w<2xl:flex>", vec!["2xl:flex"]),
(
"%w<flex data-[state=pending]:bg-(--my-color) flex-col>",
vec!["flex", "data-[state=pending]:bg-(--my-color)", "flex-col"],
),
// Nested `<…>` does not end the literal
("%w<flex <nested> px-2.5>", vec!["flex", "px-2.5"]),
// %w|…|, %w:…:, %w!…!
("%w|flex px-2.5|", vec!["flex", "px-2.5"]),
("%w:flex px-2.5:", vec!["flex", "px-2.5"]),
("%w!flex px-2.5!", vec!["flex", "px-2.5"]),
(
"# test\n# test\n# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]\n%w[flex px-2.5]",
vec!["flex", "px-2.5"],
),
(r#""foo # bar""#, vec!["foo", "bar"]),
(r#"'foo # bar'"#, vec!["foo", "bar"]),
(r#"%w[foo ' bar]"#, vec!["foo", "bar"]),
] { ] {
Ruby::test_extract_contains(input, expected); Ruby::test_extract_contains(input, expected);
} }
@ -262,4 +450,79 @@ mod tests {
"#; "#;
Ruby::test_extract_contains(input, vec!["z-1", "z-2", "z-3"]); Ruby::test_extract_contains(input, vec!["z-1", "z-2", "z-3"]);
} }
// https://github.com/tailwindlabs/tailwindcss/issues/19728
#[test]
fn test_interpolated_expressions() {
let input = r#"
def width_class(width = nil)
<<~STYLE_CLASS
#{width || 'w-100'}
STYLE_CLASS
end
"#;
Ruby::test_extract_contains(input, vec!["w-100"]);
}
// https://github.com/tailwindlabs/tailwindcss/issues/19239
#[test]
fn test_skip_comments() {
let input = r#"
# From activerecord-8.1.1/lib/active_record/errors.rb:147
# Rails uses RDoc cross-reference syntax in inline documentation:
# {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!]
"#;
// Nothing should be extracted from comments, so expect an empty array.
Ruby::test_extract_exact(input, vec![]);
}
// https://github.com/tailwindlabs/tailwindcss/issues/19481
#[test]
fn test_strict_locals() {
// Strict locals are defined in a `<%# locals: … %>`, but the `#` looks like a comment
// which we should not ignore in this case.
let input = r#"
<%# locals: (css: "text-amber-600") %>
<% more_css = "text-sky-500" %>
<p class="text-green-500">
In a partial
</p>
<p class="<%= css %>">
In a partial using explicit local variables
</p>
<p class="<%= more_css %>">
In a partial using explicit local variables
</p>
"#;
Ruby::test_extract_contains(
input,
vec!["text-amber-600", "text-sky-500", "text-green-500"],
);
}
#[test]
fn test_invalid_utf8_does_not_panic() {
// Invalid UTF-8 sequence: 0x80 is a continuation byte without a leading byte
let invalid_utf8: &[u8] = &[0x80, 0x81, 0x82];
let processor = Ruby::default();
// Should not panic, just return the input unchanged
let result = processor.process(invalid_utf8);
assert_eq!(result, invalid_utf8);
}
#[test]
fn test_valid_utf8_with_multibyte_chars() {
// Test that valid UTF-8 with multi-byte characters (like em-dashes) works
let input = "# Comment with em—dash\n%w[flex px-2.5]";
Ruby::test_extract_contains(input, vec!["flex", "px-2.5"]);
}
} }

View file

@ -0,0 +1,238 @@
use crate::extractor::bracket_stack;
use crate::extractor::cursor;
use crate::extractor::machine::Machine;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
use crate::extractor::variant_machine::VariantMachine;
use crate::extractor::MachineState;
use bstr::ByteSlice;
#[derive(Debug, Default)]
pub struct Rust;
impl PreProcessor for Rust {
fn process(&self, content: &[u8]) -> Vec<u8> {
// Leptos support: https://github.com/tailwindlabs/tailwindcss/pull/18093
let replaced_content = content
.replace(" class:", " class ")
.replace("\tclass:", " class ")
.replace("\nclass:", " class ");
if replaced_content.contains_str(b"html!") {
self.process_maud_templates(&replaced_content)
} else {
replaced_content
}
}
}
impl Rust {
fn process_maud_templates(&self, replaced_content: &[u8]) -> Vec<u8> {
let len = replaced_content.len();
let mut result = replaced_content.to_vec();
let mut cursor = cursor::Cursor::new(replaced_content);
let mut bracket_stack = bracket_stack::BracketStack::default();
while cursor.pos < len {
match cursor.curr() {
// Escaped character, skip ahead to the next character
b'\\' => {
cursor.advance_twice();
continue;
}
// Consume strings as-is
b'"' => {
result[cursor.pos] = b' ';
cursor.advance();
while cursor.pos < len {
match cursor.curr() {
// Escaped character, skip ahead to the next character
b'\\' => cursor.advance_twice(),
// End of the string
b'"' => {
result[cursor.pos] = b' ';
break;
}
// Everything else is valid
_ => cursor.advance(),
};
}
}
// Only replace `.` with a space if it's not surrounded by numbers. E.g.:
//
// ```diff
// - .flex.items-center
// + flex items-center
// ```
//
// But with numbers, it's allowed:
//
// ```diff
// - px-2.5
// + px-2.5
// ```
b'.' => {
// Don't replace dots with spaces when inside of any type of brackets, because
// this could be part of arbitrary values. E.g.: `bg-[url(https://example.com)]`
// ^
if !bracket_stack.is_empty() {
cursor.advance();
continue;
}
// If the dot is surrounded by digits, we want to keep it. E.g.: `px-2.5`
// EXCEPT if it's followed by a valid variant that happens to start with a
// digit.
// E.g.: `bg-red-500.2xl:flex`
// ^^^
if cursor.prev().is_ascii_digit() && cursor.next().is_ascii_digit() {
let mut next_cursor = cursor.clone();
next_cursor.advance();
let mut variant_machine = VariantMachine::default();
if let MachineState::Done(_) = variant_machine.next(&mut next_cursor) {
result[cursor.pos] = b' ';
}
} else {
result[cursor.pos] = b' ';
}
}
b'[' => {
bracket_stack.push(cursor.curr());
// Handle `p.flex[condition]`. If there is a `-` before it, it will likely be an
// arbitrary value e.g. `text-[red]`
if !matches!(cursor.prev(), b'-') {
result[cursor.pos] = b' ';
}
}
b']' if !bracket_stack.is_empty() => {
bracket_stack.pop(cursor.curr());
}
// Consume everything else
_ => {}
};
cursor.advance();
}
result
}
}
#[cfg(test)]
mod tests {
use super::Rust;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[test]
fn test_leptos_extraction() {
for (input, expected) in [
// Spaces
(
"<div class:flex class:px-2.5={condition()}>",
"<div class flex class px-2.5={condition()}>",
),
// Tabs
(
"<div\tclass:flex class:px-2.5={condition()}>",
"<div class flex class px-2.5={condition()}>",
),
// Newlines
(
"<div\nclass:flex class:px-2.5={condition()}>",
"<div class flex class px-2.5={condition()}>",
),
] {
Rust::test(input, expected);
}
}
// https://github.com/tailwindlabs/tailwindcss/issues/18984
#[test]
fn test_maud_template_extraction() {
let input = r#"
use maud::{html, Markup};
pub fn main() -> Markup {
html! {
header.px-8.py-4.text-black {
"Hello, world!"
}
}
}
"#;
Rust::test_extract_contains(input, vec!["px-8", "py-4", "text-black"]);
// https://maud.lambda.xyz/elements-attributes.html#classes-and-ids-foo-bar
let input = r#"
html! {
input #cannon .big.scary.bright-red type="button" value="Launch Party Cannon";
}
"#;
Rust::test_extract_contains(input, vec!["big", "scary", "bright-red"]);
let input = r#"
html! {
div."bg-[#0088cc]" { "Quotes for backticks" }
}
"#;
Rust::test_extract_contains(input, vec!["bg-[#0088cc]"]);
let input = r#"
html! {
#main {
"Main content!"
.tip { "Storing food in a refrigerator can make it 20% cooler." }
}
}
"#;
Rust::test_extract_contains(input, vec!["tip"]);
let input = r#"
html! {
div."bg-[url(https://example.com)]" { "Arbitrary values" }
}
"#;
Rust::test_extract_contains(input, vec!["bg-[url(https://example.com)]"]);
let input = r#"
html! {
div.px-4.text-black {
"Some text, with unbalanced brackets ]["
}
div.px-8.text-white {
"Some more text, with unbalanced brackets ]["
}
}
"#;
Rust::test_extract_contains(input, vec!["px-4", "text-black", "px-8", "text-white"]);
let input = r#"html! { \x.px-4.text-black { } }"#;
Rust::test(input, r#"html! { \x px-4 text-black { } }"#);
}
// https://github.com/tailwindlabs/tailwindcss/issues/20233
#[test]
fn test_maud_template_extraction_with_conditional_classes() {
let input = r#"
use maud::{html, Markup};
pub fn main() -> Markup {
html! {
p.text-black[cuteness > 50] { "Squee!" }
}
}
"#;
Rust::test_extract_contains(input, vec!["text-black"]);
}
}

View file

@ -15,7 +15,7 @@ impl PreProcessor for Slim {
let mut bracket_stack = BracketStack::default(); let mut bracket_stack = BracketStack::default();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr { match cursor.curr() {
// Only replace `.` with a space if it's not surrounded by numbers. E.g.: // Only replace `.` with a space if it's not surrounded by numbers. E.g.:
// //
// ```diff // ```diff
@ -43,7 +43,7 @@ impl PreProcessor for Slim {
// digit. // digit.
// E.g.: `bg-red-500.2xl:flex` // E.g.: `bg-red-500.2xl:flex`
// ^^^ // ^^^
if cursor.prev.is_ascii_digit() && cursor.next.is_ascii_digit() { if cursor.prev().is_ascii_digit() && cursor.next().is_ascii_digit() {
let mut next_cursor = cursor.clone(); let mut next_cursor = cursor.clone();
next_cursor.advance(); next_cursor.advance();
@ -65,17 +65,74 @@ impl PreProcessor for Slim {
// class=%w[bg-blue-500 w-10 h-10] // class=%w[bg-blue-500 w-10 h-10]
// ] // ]
// ``` // ```
b'%' if matches!(cursor.next, b'w' | b'W') //
&& matches!(cursor.input.get(cursor.pos + 2), Some(b'[' | b'(' | b'{')) => // A `%` that follows a value is not a percent literal. E.g.: the `50%w` in
// `hit rate 50%w.`
b'%' if matches!(cursor.next(), b'w' | b'W')
&& !cursor.prev().is_ascii_alphanumeric()
&& !matches!(cursor.prev(), b'_' | b')' | b']' | b'}') =>
{ {
// Boundary characters
let (open, close) = match cursor.input.get(cursor.pos + 2) {
Some(b'[') => (b'[', b']'),
Some(b'(') => (b'(', b')'),
Some(b'{') => (b'{', b'}'),
Some(b'<') => (b'<', b'>'),
// Any other ASCII punctuation can be used as a custom delimiter
Some(&c) if c.is_ascii_punctuation() => (c, c),
// Everything else is not a valid delimiter
_ => {
cursor.advance();
continue;
}
};
result[cursor.pos] = b' '; // Replace `%` result[cursor.pos] = b' '; // Replace `%`
cursor.advance(); cursor.advance();
result[cursor.pos] = b' '; // Replace `w` result[cursor.pos] = b' '; // Replace `w`
cursor.advance(); cursor.advance();
result[cursor.pos] = b' '; // Replace `[` or `(` or `{` result[cursor.pos] = b' '; // Replace the opening delimiter
bracket_stack.push(cursor.curr); cursor.advance();
cursor.advance(); // Move past the bracket
continue; // Paired delimiters can be nested as long as they are balanced. E.g.:
// `%w[foo[bar]baz]` produces a flat array.
let mut depth = 1_usize;
while cursor.pos < len {
match cursor.curr() {
// Skip escaped characters, unless the backslash is the delimiter
// itself
b'\\' if close != b'\\' => {
// Use backslash to embed spaces in the strings.
if cursor.next() == b' ' {
result[cursor.pos] = b' ';
}
cursor.advance();
}
// Start of a nested delimiter pair
c if c == open && open != close => depth += 1,
// Closing delimiter
c if c == close => {
depth -= 1;
// End of the literal, replace the closing delimiter with a space
if depth == 0 {
result[cursor.pos] = b' ';
break;
}
}
// Everything else is valid content
_ => {}
}
cursor.advance();
}
} }
// Any `[` preceded by an alphanumeric value will not be part of a candidate. // Any `[` preceded by an alphanumeric value will not be part of a candidate.
@ -96,10 +153,10 @@ impl PreProcessor for Slim {
// Instead of listing all boundary characters, let's list the characters we know // Instead of listing all boundary characters, let's list the characters we know
// will be invalid instead. // will be invalid instead.
b'[' if bracket_stack.is_empty() b'[' if bracket_stack.is_empty()
&& matches!(cursor.prev, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') => && matches!(cursor.prev(), b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9') =>
{ {
result[cursor.pos] = b' '; result[cursor.pos] = b' ';
bracket_stack.push(cursor.curr); bracket_stack.push(cursor.curr());
} }
// In Slim the class name shorthand can be followed by a parenthesis. E.g.: // In Slim the class name shorthand can be followed by a parenthesis. E.g.:
@ -114,17 +171,17 @@ impl PreProcessor for Slim {
// //
// However, we also need to make sure that we keep the parens that are part of the // However, we also need to make sure that we keep the parens that are part of the
// utility class. E.g.: `bg-(--my-color)`. // utility class. E.g.: `bg-(--my-color)`.
b'(' if bracket_stack.is_empty() && !matches!(cursor.prev, b'-' | b'/') => { b'(' if bracket_stack.is_empty() && !matches!(cursor.prev(), b'-' | b'/') => {
result[cursor.pos] = b' '; result[cursor.pos] = b' ';
bracket_stack.push(cursor.curr); bracket_stack.push(cursor.curr());
} }
b'(' | b'[' | b'{' => { b'(' | b'[' | b'{' => {
bracket_stack.push(cursor.curr); bracket_stack.push(cursor.curr());
} }
b')' | b']' | b'}' if !bracket_stack.is_empty() => { b')' | b']' | b'}' if !bracket_stack.is_empty() => {
bracket_stack.pop(cursor.curr); bracket_stack.pop(cursor.curr());
} }
// Consume everything else // Consume everything else
@ -316,16 +373,77 @@ mod tests {
] ]
"#; "#;
let expected = r#" let expected = "
div div \n class= bg-blue-500 w-10 h-10 \n ]
class= bg-blue-500 w-10 h-10] div \n class= w-10 bg-green-500 h-10 \n ]
] ";
div
class= w-10 bg-green-500 h-10]
]
"#;
Slim::test(input, expected); Slim::test(input, expected);
Slim::test_extract_contains(input, vec!["bg-blue-500", "bg-green-500", "w-10", "h-10"]); Slim::test_extract_contains(input, vec!["bg-blue-500", "bg-green-500", "w-10", "h-10"]);
} }
// https://github.com/tailwindlabs/tailwindcss/issues/20386
#[test]
fn test_embedded_ruby_percent_w_delimiters() {
for (input, expected) in [
// %w<…>, Slim only counts `[({` nesting in attribute values, so the code must be
// wrapped in parentheses to contain spaces
(
"div class=(%w<bg-blue-500 w-10 h-10>)",
"div class= bg-blue-500 w-10 h-10 )",
),
// Nested `<…>` does not end the literal
(
"div class=(%w<flex <nested> px-2.5>)",
"div class= flex <nested> px-2.5 )",
),
// Custom delimiters
("div class=(%w|flex px-2.5|)", "div class= flex px-2.5 )"),
("div class=(%W!flex px-2.5!)", "div class= flex px-2.5 )"),
(
"div class=(%w#text-sm leading-6#)",
"div class= text-sm leading-6 )",
),
(
"div class=(%w=italic tracking-wide=)",
"div class= italic tracking-wide )",
),
// Nested paired delimiters stay balanced inside the literal
(
"div class=(%w[content-['[hello]'] p-4])",
"div class= content-['[hello]'] p-4 )",
),
// Escaped spaces embed a space in a single array element
(
r#"div class=(%w[foo\ bar baz-1])"#,
r#"div class= foo bar baz-1 )"#,
),
// Ruby control line, which is plain Ruby code
("- classes = %w<mt-4 flex>", "- classes = mt-4 flex "),
// A `%` that follows a value is not a percent literal
("| hit rate 50%w.", "| hit rate 50%w "),
] {
Slim::test(input, expected);
}
let input = r#"
div[
class=(%w<bg-blue-500 w-10 h-10>)
]
- classes = %w|w-10 bg-green-500 h-10|
= tag.div class: %W!px-2.5 flex!
"#;
Slim::test_extract_contains(
input,
vec![
"bg-blue-500",
"bg-green-500",
"w-10",
"h-10",
"px-2.5",
"flex",
],
);
}
} }

View file

@ -0,0 +1,52 @@
use crate::cursor;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[derive(Debug, Default)]
pub struct TemplateToolkit;
impl PreProcessor for TemplateToolkit {
fn process(&self, content: &[u8]) -> Vec<u8> {
let len = content.len();
let mut result = content.to_vec();
let mut cursor = cursor::Cursor::new(content);
while cursor.pos < len {
match (cursor.curr(), cursor.next()) {
(b'[', b'%') => result[cursor.pos] = b' ',
(b'%', b']') => result[cursor.pos + 1] = b' ',
_ => {}
}
cursor.advance();
}
result
}
}
#[cfg(test)]
mod tests {
use super::TemplateToolkit;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[test]
fn test_template_toolkit_pre_processor() {
for (input, expected) in [
(
"[% IF $is_open %]bg-white/40[% ELSE %]bg-white/10[% END %]",
" % IF $is_open % bg-white/40 % ELSE % bg-white/10 % END % ",
),
("[% WRAPPER %]flex[% END %]", " % WRAPPER % flex % END % "),
] {
TemplateToolkit::test(input, expected);
}
}
#[test]
fn test_extraction_between_template_tags_works() {
TemplateToolkit::test_extract_contains(
r#"<div class="[% IF $is_open %]bg-white/40[% ELSE %]bg-white/10[% END %]"></div>"#,
vec!["bg-white/40", "bg-white/10"],
);
}
}

View file

@ -1,155 +0,0 @@
/ https://github com/tailwindlabs/tailwindcss/pull/17051 issuecomment-2711181352
- star_styles "size- 800px mask mask-star bg-gradient-to-r from-secondary via-cyan-400 to-lime-400"
- crazy_text_styles "italic font-black bg-gradient-to-r via-60% to-90% from-orange-500 via-secondary to-primary text-transparent bg-clip-text inline-block py-2"
relative
- Blurred background star
absolute left-0 z-0 class: "-top-[400px] -right-[400px]"
flex justify-end blur-3xl
%div class: star_styles
relative z-10
%h1 mt-8 text-center text-5xl font-black tracking-wide drop-shadow-lg
%div
Components, Guides, and Paradigms
md:inline-flex md:items-center justify-center
%span -mr-2
for
%span relative -rotate-3 hover:-rotate-6 hover:scale-125 transition-transform
%span class: crazy_text_styles + " absolute blur-xl"
&nbsp;CRAZY&nbsp;
%span class: crazy_text_styles + " drop-shadow-[0_0_1px_#fff]"
&nbsp;CRAZY&nbsp;
%span -ml-3 md:-ml-1
\-fast Development
%div
in Ruby on Rails!
mt-16 text-center
daisy_button "😃 Get Started", css: "btn-primary text-xl",
right_icon: "arrow-right", target: "_blank",
href: "https://github com/profoundry-us/loco_motion locomotion-components"
mt-32 lg:flex lg:space-x-8
%div class: "md:basis-2/5"
%h3 text-4xl font-bold
Easy, Flexible Components
%p mt-2 text-xl
Powered by the fabulous
succeed ',' do
daisy_link "ViewComponent", "https://viewcomponent.org", target: "_blank"
succeed ', and' do
daisy_link "DaisyUI", "https://daisyui com/", target: "_blank"
daisy_link "TailwindCSS", "https://tailwindcss com/", target: "_blank"
libraries, our components are designed to be fast, flexible, and easy to
use <i>directy</i> in Ruby on Rails!
doc_example css: "mt-8 lg:mt-0 lg:basis-3/5 h-44" do
flex flex-col sm:flex-row items-center gap-4
daisy_button "Accent Button", css: "btn-accent"
daisy_tip "Click to Swap" do
daisy_swap off: "🌚", on: "🌞", css: "swap-rotate text-4xl"
daisy_badge "Large Badge", css: "badge-secondary badge-lg"
mt-32 xl:flex xl:flex-row-reverse xl:items-center
text-xl xl:ml-8
%h3 text-4xl font-bold
Simple, Concise Views
%p mt-2
Utlize
daisy_link "HAML", "https://haml.info/", target: "_blank"
so your views are simple, concise, and easy to understand
%p mt-2
No more messy ERB files with all of their closing tags and Ruby wrappers
HAML feels more natural to write and reduces file sizes, making your
views easier to read and maintain
%p mt-2
:markdown
**PLUS!** You can utlize filters like _Markdown_, CoffeeScript,
Textile, and many more!
mt-8 xl:mt-0
flex flex-col xl:flex xl:flex-row w-full
doc_code css: "grow", code_css: "xl:pl-2 xl:pr-0 xl:rounded-r-none", language: "erb" do
:escaped
<% Ruby %>
<% 5 times do |i| %>
<% if i even? %>
<p class "odd">Number <% i %></p>
<% else %>
<p class "even">Numero <% i %></p>
<% end %>
<% end %>
doc_code css: "grow mt-8 xl:mt-0", pre_css: "xl:h-full", code_css: "xl:pl-0 xl:pr-2 xl:h-full xl:rounded-l-none" do
:escaped
- HAML
- 5 times do |i|
- if i even?
%p odd Number \ i
- else
%p even Numero \ i
%h3 mt-32 text-4xl font-bold
Build Your <i>OWN</i> Components
%p mt-2 text-xl
Can't find exactly what you need? No problem! Build your own components
with ease using our simple, flexible, and powerful DSL
mt-8 xl:flex xl:space-x-8
%div
doc_code language: "ruby" do
:escaped
app/components/application_component rb
class ApplicationComponent < LocoMotion::BaseComponent
Add your custom / shared component logic here!
end
doc_code language: "haml", css: "mt-8" do
:escaped
- app/components/character_component html haml
part :component do
part :head
part :body do
content
part :legs
%div mt-8 xl:mt-0
doc_code language: "ruby" do
:escaped
app/components/character_component rb
class CharacterComponent < ApplicationComponent
define_parts :head, :body, :legs
def before_render
set_tag_name :head, :h1
add_css :head, "text-3xl font-bold"
set_tag_name :body, :p
add_stimulus_controller :body, "character-body"
add_css :body, "text-lg"
set_tag_name :legs, :footer
add_css :legs, "text-sm"
end
end
%h3 mt-24 text-center text-4xl font-bold italic
More Coming Soon!
%p mt-2 text-xl text-center
Keen an eye out as we'll be adding more components, guides, and
%br max-sm:hidden
suggested gems for you to build amazing Rails apps!
mt-4 text-center
daisy_button "😉 Get Started", css: "btn-primary text-xl",
right_icon: "arrow-right", target: "_blank",
class: "px-2.5"
href: "https://github com/profoundry-us/loco_motion locomotion-components"

View file

@ -0,0 +1,262 @@
/ https://github.com/tailwindlabs/tailwindcss/pull/17051#issuecomment-2711181352
- star_styles = "size-[800px] mask mask-star bg-gradient-to-r from-secondary via-cyan-400 to-lime-400"
^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^
- crazy_text_styles = "italic font-black bg-gradient-to-r via-60% to-90% from-orange-500 via-secondary to-primary text-transparent bg-clip-text inline-block py-2"
^^^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^
.relative
^^^^^^^^
- # Blurred background star
.absolute.left-0.z-0{ class: "-top-[400px] -right-[400px]" }
^^^^^^^^ ^^^^^^ ^^^ ^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^^^^^
.flex.justify-end.blur-3xl
^^^^ ^^^^^^^^^^^ ^^^^^^^^
%div{ class: star_styles }
^^^^^ ^^^^^^^^^^^
.relative.z-10
^^^^^^^^ ^^^^
%h1.mt-8.text-center.text-5xl.font-black.tracking-wide.drop-shadow-lg
^^^^ ^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
%div
Components, Guides, and Paradigms
^^^
.md:inline-flex.md:items-center.justify-center
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
%span.-mr-2
^^^^^
for
^^^
%span.relative.-rotate-3.hover:-rotate-6.hover:scale-125.transition-transform
^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
%span{ class: crazy_text_styles + " absolute blur-xl" }
^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^
&nbsp;CRAZY&nbsp;
%span{ class: crazy_text_styles + " drop-shadow-[0_0_1px_#fff]" }
^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
&nbsp;CRAZY&nbsp;
%span.-ml-3.md:-ml-1
^^^^^ ^^^^^^^^
\-fast Development
%div
in Ruby on Rails!
^^ ^^
.mt-16.text-center
^^^^^ ^^^^^^^^^^^
= daisy_button "😃 Get Started", css: "btn-primary text-xl",
^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^ ^^^^^^^
right_icon: "arrow-right", target: "_blank",
^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^
href: "https://github.com/profoundry-us/loco_motion#locomotion-components"
^^^^ ^^^^^^^^^^^^^^^^^^^^^
.mt-32.lg:flex.lg:space-x-8
^^^^^ ^^^^^^^ ^^^^^^^^^^^^
%div{ class: "md:basis-2/5" }
^^^^^ ^^^^^^^^^^^^
%h3.text-4xl.font-bold
^^^^^^^^ ^^^^^^^^^
Easy, Flexible Components
%p.mt-2.text-xl
^^^^ ^^^^^^^
Powered by the fabulous
^^ ^^^ ^^^^^^^^
= succeed ',' do
^^^^^^^ ^^
= daisy_link("ViewComponent", "https://viewcomponent.org", target: "_blank")
^^^ ^^^^^^
= succeed ', and' do
^^^^^^^ ^^^ ^^
= daisy_link "DaisyUI", "https://daisyui.com/", target: "_blank"
^^^^^^^^^^ ^^^^^^
= daisy_link "TailwindCSS", "https://tailwindcss.com/", target: "_blank"
^^^^^^^^^^ ^^^^^^
libraries, our components are designed to be fast, flexible, and easy to
^^^ ^^^^^^^^^^ ^^^ ^^^^^^^^ ^^ ^^ ^^^ ^^^^ ^^
use <i>directly</i> in Ruby on Rails!
^^^ ^^^^^^^^ ^^ ^^
= doc_example(css: "mt-8 lg:mt-0 lg:basis-3/5 h-44") do
^^^ ^^^^ ^^^^^^^ ^^^^^^^^^^^^ ^^^^ ^^
.flex.flex-col.sm:flex-row.items-center.gap-4
^^^^ ^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^
= daisy_button "Accent Button", css: "btn-accent"
^^^^^^^^^^^^ ^^^ ^^^^^^^^^^
= daisy_tip("Click to Swap") do
^^ ^^
= daisy_swap off: "🌚", on: "🌞", css: "swap-rotate text-4xl"
^^^^^^^^^^ ^^^ ^^ ^^^ ^^^^^^^^^^^ ^^^^^^^^
= daisy_badge "Large Badge", css: "badge-secondary badge-lg"
^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^
.mt-32.xl:flex.xl:flex-row-reverse.xl:items-center
^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
.text-xl.xl:ml-8
^^^^^^^ ^^^^^^^
%h3.text-4xl.font-bold
^^^^^^^^ ^^^^^^^^^
Simple, Concise Views
%p.mt-2
^^^^
Utilize
= daisy_link("HAML", "https://haml.info/", target: "_blank")
^^^^^^
so your views are simple, concise, and easy to understand.
^^ ^^^^ ^^^^^ ^^^ ^^^ ^^^^ ^^ ^^^^^^^^^^
%p.mt-2
^^^^
No more messy ERB files with all of their closing tags and Ruby wrappers.
^^^^ ^^^^^ ^^^^^ ^^^^ ^^^ ^^ ^^^^^ ^^^^^^^ ^^^^ ^^^ ^^^^^^^^
HAML feels more natural to write and reduces file sizes, making your
^^^^^ ^^^^ ^^^^^^^ ^^ ^^^^^ ^^^ ^^^^^^^ ^^^^ ^^^^^^ ^^^^
views easier to read and maintain.
^^^^^ ^^^^^^ ^^ ^^^^ ^^^ ^^^^^^^^
%p.mt-2
^^^^
:markdown
**PLUS!** You can utilize filters like _Markdown_, CoffeeScript,
^^^ ^^^^^^^ ^^^^^^^ ^^^^
Textile, and many more!
^^^ ^^^^ ^^^^^
.mt-8.xl:mt-0
^^^^ ^^^^^^^
.flex.flex-col.xl:flex.xl:flex-row.w-full
^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^ ^^^^^^
= doc_code(css: "grow", code_css: "xl:pl-2 xl:pr-0 xl:rounded-r-none", language: "erb") do
^^^ ^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^ ^^^ ^^
:escaped
<% # Ruby %>
<% 5.times do |i| %>
^^^^^ ^^
<% if i.even? %>
^^ ^
<p class="odd">Number <%= i %></p>
^^^^^ ^^^ ^
<% else %>
^^^^
<p class="even">Numero <%= i %></p>
^^^^^ ^^^^ ^
<% end %>
^^^
<% end %>
^^^
= doc_code(css: "grow mt-8 xl:mt-0", pre_css: "xl:h-full", code_css: "xl:pl-0 xl:pr-2 xl:h-full xl:rounded-l-none") do
^^^ ^^^^ ^^^^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^ ^^^^^^^^ ^^^^^^^ ^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^
:escaped
- # HAML
- 5.times do |i|
^^^^^ ^^
- if i.even?
^^
%p.odd Number \#{i}
^^^ ^
- else
^^^^
%p.even Numero \#{i}
^^^^ ^
%h3.mt-32.text-4xl.font-bold
^^^^^ ^^^^^^^^ ^^^^^^^^^
Build Your <i>OWN</i> Components
%p.mt-2.text-xl
^^^^ ^^^^^^^
Can't find exactly what you need? No problem! Build your own components
^ ^^^^ ^^^^^^^ ^^^^ ^^^ ^^^^^^^^ ^^^^ ^^^ ^^^^^^^^^^
with ease using our simple, flexible, and powerful DSL.
^^^^ ^^^^ ^^^^^ ^^^ ^^^ ^^^^^^^^
.mt-8.xl:flex.xl:space-x-8
^^^^ ^^^^^^^ ^^^^^^^^^^^^
%div
= doc_code(language: "ruby") do
^^^^^^^^ ^^^^ ^^
:escaped
# app/components/application_component.rb
^^
class ApplicationComponent < LocoMotion::BaseComponent
^^^^^
# Add your custom / shared component logic here!
^^^^ ^^^^^^ ^^^^^^ ^^^^^^^^^ ^^^^^ ^^^^^
end
^^^
= doc_code(language: "haml", css: "mt-8") do
^^^^^^^^ ^^^^ ^^^ ^^^^ ^^
:escaped
- # app/components/character_component.html.haml
= part(:component) do
^^
= part(:head)
= part(:body) do
^^
= content
^^^^^^^
= part(:legs)
%div.mt-8.xl:mt-0
^^^^ ^^^^^^^
= doc_code(language: "ruby") do
^^^^^^^^ ^^^^ ^^
:escaped
# app/components/character_component.rb
^^
class CharacterComponent < ApplicationComponent
^^^^^
define_parts :head, :body, :legs
^^^^^^^^^^^^
def before_render
^^^ ^^^^^^^^^^^^^
set_tag_name(:head, :h1)
^^^^^^^^^^^^
add_css(:head, "text-3xl font-bold")
^^^^^^^ ^^^^^^^^ ^^^^^^^^^
set_tag_name(:body, :p)
^^^^^^^^^^^^
add_stimulus_controller(:body, "character-body")
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
add_css(:body, "text-lg")
^^^^^^^ ^^^^^^^
set_tag_name(:legs, :footer)
^^^^^^^^^^^^
add_css(:legs, "text-sm")
^^^^^^^ ^^^^^^^
end
^^^
end
^^^
%h3.mt-24.text-center.text-4xl.font-bold.italic
^^^^^ ^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^^ ^^^^^^
More Coming Soon!
%p.mt-2.text-xl.text-center
^^^^ ^^^^^^^ ^^^^^^^^^^^
Keen an eye out as we'll be adding more components, guides, and
^^ ^^^ ^^^ ^^ ^^ ^^ ^^ ^^^^^^ ^^^^ ^^^
%br.max-sm:hidden
^^^^^^^^^^^^^
suggested gems for you to build amazing Rails apps!
^^^^^^^^^ ^^^^ ^^^ ^^^ ^^ ^^^^^ ^^^^^^^ ^^^^^
.mt-4.text-center
^^^^ ^^^^^^^^^^^
= daisy_button "😉 Get Started", css: "btn-primary text-xl",
^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^ ^^^^^^^
right_icon: "arrow-right", target: "_blank",
^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^
class: "px-2.5"
^^^^^ ^^^^^^
href: "https://github.com/profoundry-us/loco_motion#locomotion-components"
^^^^ ^^^^^^^^^^^^^^^^^^^^^

View file

@ -0,0 +1,30 @@
:ruby
- devices_classes = 'size-5 mr-2px'
^^^^^^^^^^^^^^^ ^^^^^^ ^^^^^^
- icon_classes = 'w-[12px] h-[12px]'
^^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^
!!!
%html{ lang: 'en' }
^^^^ ^^
%head
%title Tailwind v4.1.4 + HAML bug
^^^^^^ ^^^
-# This is a comment
^^ ^ ^^^^^^^
A multi-line comment
^^^^^^^^^^ ^^^^^^^
With more indentation
^^^^ ^^^^^^^^^^^
Which can dedent again just fine
^^^ ^^^^^^ ^^^^^ ^^^^ ^^^^
%body
- icon_classes = 'self-center w-[16px] h-[16px]'
^^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^ ^^^^^^^^
.flex{ class: icon_classes }
^^^^ ^^^^^ ^^^^^^^^^^^^
.flex{ class: devices_classes }
^^^^ ^^^^^ ^^^^^^^^^^^^^^^

View file

@ -42,7 +42,7 @@
= daisy_link "DaisyUI", "https://daisyui.com/", target: "_blank" = daisy_link "DaisyUI", "https://daisyui.com/", target: "_blank"
= daisy_link "TailwindCSS", "https://tailwindcss.com/", target: "_blank" = daisy_link "TailwindCSS", "https://tailwindcss.com/", target: "_blank"
libraries, our components are designed to be fast, flexible, and easy to libraries, our components are designed to be fast, flexible, and easy to
use <i>directy</i> in Ruby on Rails! use <i>directly</i> in Ruby on Rails!
= doc_example(css: "mt-8 lg:mt-0 lg:basis-3/5 h-44") do = doc_example(css: "mt-8 lg:mt-0 lg:basis-3/5 h-44") do
.flex.flex-col.sm:flex-row.items-center.gap-4 .flex.flex-col.sm:flex-row.items-center.gap-4
@ -58,7 +58,7 @@
Simple, Concise Views Simple, Concise Views
%p.mt-2 %p.mt-2
Utlize Utilize
= daisy_link("HAML", "https://haml.info/", target: "_blank") = daisy_link("HAML", "https://haml.info/", target: "_blank")
so your views are simple, concise, and easy to understand. so your views are simple, concise, and easy to understand.
@ -69,7 +69,7 @@
%p.mt-2 %p.mt-2
:markdown :markdown
**PLUS!** You can utlize filters like _Markdown_, CoffeeScript, **PLUS!** You can utilize filters like _Markdown_, CoffeeScript,
Textile, and many more! Textile, and many more!
.mt-8.xl:mt-0 .mt-8.xl:mt-0

View file

@ -0,0 +1,19 @@
:ruby
- devices_classes = 'size-5 mr-2px'
- icon_classes = 'w-[12px] h-[12px]'
!!!
%html{ lang: 'en' }
%head
%title Tailwind v4.1.4 + HAML bug
-# This is a comment
A multi-line comment
With more indentation
Which can dedent again just fine
%body
- icon_classes = 'self-center w-[16px] h-[16px]'
.flex{ class: icon_classes }
.flex{ class: devices_classes }

View file

@ -0,0 +1,179 @@
use crate::cursor;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[derive(Debug, Default)]
pub struct Twig;
impl PreProcessor for Twig {
fn process(&self, content: &[u8]) -> Vec<u8> {
let len = content.len();
let mut result = content.to_vec();
let mut cursor = cursor::Cursor::new(content);
let mut bracket_stack = vec![];
const ADD_CLASS: &[u8] = b"addClass";
const REMOVE_CLASS: &[u8] = b"removeClass";
while cursor.pos < len {
match (!bracket_stack.is_empty(), cursor.curr()) {
// addClass(
// ^
(false, b'(')
if cursor.pos >= ADD_CLASS.len()
&& matches!(
&content[cursor.pos - ADD_CLASS.len()..cursor.pos],
ADD_CLASS
) =>
{
bracket_stack.push(cursor.curr());
result[cursor.pos] = b' ';
}
// removeClass(
// ^
(false, b'(')
if cursor.pos >= REMOVE_CLASS.len()
&& matches!(
&content[cursor.pos - REMOVE_CLASS.len()..cursor.pos],
REMOVE_CLASS
) =>
{
bracket_stack.push(cursor.curr());
result[cursor.pos] = b' ';
}
(true, b'(') => {
bracket_stack.push(cursor.curr());
}
(true, b')') => {
bracket_stack.pop();
if bracket_stack.is_empty() {
result[cursor.pos] = b' ';
}
}
_ => {}
}
cursor.advance();
}
result
}
}
#[cfg(test)]
mod tests {
use super::Twig;
use crate::extractor::pre_processors::pre_processor::PreProcessor;
#[test]
fn test_twig_pre_processor() {
for (input, expected) in [
(
// Ensure we don't crash when we encounter an `(`
"(p-(--value))",
"(p-(--value))",
),
(
// addClass with single argument
"addClass(p-(--value))",
"addClass p-(--value) ",
),
(
// addClass with single argument
"addClass(m-4 p-8 w-full)",
"addClass m-4 p-8 w-full ",
),
(
// removeClass with single argument
"removeClass(p-(--value))",
"removeClass p-(--value) ",
),
(
// removeClass with single argument
"removeClass(m-4 p-8 w-full)",
"removeClass m-4 p-8 w-full ",
),
(
// Combined with single arguments
"addClass(m-(--value))|removeClass(p-(--value))",
"addClass m-(--value) |removeClass p-(--value) ",
),
] {
Twig::test(input, expected);
}
}
// https://github.com/tailwindlabs/tailwindcss/issues/19458
#[test]
fn test_extraction_in_add_class_and_remove_class_works() {
// Inside normal HTML
let input = r#"
<div data-loading="addClass(opacity-50)">
<!-- -->
</div>
"#;
let expected = r#"
<div data-loading="addClass opacity-50 ">
<!-- -->
</div>
"#;
Twig::test(input, expected);
Twig::test_extract_contains(input, vec!["opacity-50"]);
// Inside a component
let input = r#"
<twig:d:Card
{{ ...attributes.defaults({
as: "a",
class: "cursor-pointer bg-base-200 card-sm md:card-md lg:card-lg",
role: "button",
tabindex: "0",
"data-action": "live#$render",
"data-loading": "addAttribute(disabled)",
"data-poll": "delay(60000)|$render",
"data-loading": "addClass(border border-red-500 opacity-75)",
})
}}
>
"#;
let expected = r#"
<twig:d:Card
{{ ...attributes.defaults({
as: "a",
class: "cursor-pointer bg-base-200 card-sm md:card-md lg:card-lg",
role: "button",
tabindex: "0",
"data-action": "live#$render",
"data-loading": "addAttribute(disabled)",
"data-poll": "delay(60000)|$render",
"data-loading": "addClass border border-red-500 opacity-75 ",
})
}}
>
"#;
Twig::test(input, expected);
Twig::test_extract_contains(
input,
vec![
// class
"cursor-pointer",
"bg-base-200",
"card-sm",
"md:card-md",
"lg:card-lg",
// data-loading
"border",
"border-red-500",
"opacity-75",
],
);
}
}

View file

@ -15,13 +15,15 @@ impl PreProcessor for Vue {
fn process(&self, content: &[u8]) -> Vec<u8> { fn process(&self, content: &[u8]) -> Vec<u8> {
let mut result = content.to_vec(); let mut result = content.to_vec();
let content_as_str = std::str::from_utf8(content).unwrap(); // Only process template tags if content is valid UTF-8
for (_, [lang, body]) in TEMPLATE_REGEX if let Ok(content_as_str) = std::str::from_utf8(content) {
.captures_iter(content_as_str) for (_, [lang, body]) in TEMPLATE_REGEX
.map(|c| c.extract()) .captures_iter(content_as_str)
{ .map(|c| c.extract())
let replaced = pre_process_input(body.as_bytes(), lang); {
result = result.replace(body, replaced); let replaced = pre_process_input(body.as_bytes().to_vec(), lang);
result = result.replace(body, replaced);
}
} }
result result
@ -43,4 +45,16 @@ mod tests {
Vue::test_extract_contains(input, vec!["bg-neutral-900", "text-red-500"]); Vue::test_extract_contains(input, vec!["bg-neutral-900", "text-red-500"]);
} }
#[test]
fn test_invalid_utf8_does_not_panic() {
// Invalid UTF-8 sequence: 0x80 is a continuation byte without a leading byte
let invalid_utf8: &[u8] = &[0x80, 0x81, 0x82];
let processor = Vue::default();
// Should not panic, just return the input unchanged
let result = processor.process(invalid_utf8);
assert_eq!(result, invalid_utf8);
}
} }

View file

@ -30,20 +30,20 @@ impl Machine for StringMachine {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
if Class::Quote != cursor.curr.into() { if Class::Quote != cursor.curr().into() {
return MachineState::Idle; return MachineState::Idle;
} }
// Start of a string // Start of a string
let len = cursor.input.len(); let len = cursor.input.len();
let start_pos = cursor.pos; let start_pos = cursor.pos;
let end_char = cursor.curr; let end_char = cursor.curr();
cursor.advance(); cursor.advance();
while cursor.pos < len { while cursor.pos < len {
match cursor.curr.into() { match cursor.curr().into() {
Class::Escape => match cursor.next.into() { Class::Escape => match cursor.next().into() {
// An escaped whitespace character is not allowed // An escaped whitespace character is not allowed
Class::Whitespace => return MachineState::Idle, Class::Whitespace => return MachineState::Idle,
@ -52,7 +52,7 @@ impl Machine for StringMachine {
}, },
// End of the string // End of the string
Class::Quote if cursor.curr == end_char => return self.done(start_pos, cursor), Class::Quote if cursor.curr() == end_char => return self.done(start_pos, cursor),
// Any kind of whitespace is not allowed // Any kind of whitespace is not allowed
Class::Whitespace => return MachineState::Idle, Class::Whitespace => return MachineState::Idle,

View file

@ -27,12 +27,12 @@ impl Machine for UtilityMachine {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.curr.into() { match cursor.curr().into() {
// LEGACY: Important marker // LEGACY: Important marker
Class::Exclamation => { Class::Exclamation => {
self.legacy_important = true; self.legacy_important = true;
match cursor.next.into() { match cursor.next().into() {
// Start of an arbitrary property // Start of an arbitrary property
// //
// E.g.: `![color:red]` // E.g.: `![color:red]`
@ -78,7 +78,7 @@ impl UtilityMachine {
fn parse_arbitrary_property(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn parse_arbitrary_property(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match self.arbitrary_property_machine.next(cursor) { match self.arbitrary_property_machine.next(cursor) {
MachineState::Idle => self.restart(), MachineState::Idle => self.restart(),
MachineState::Done(_) => match cursor.next.into() { MachineState::Done(_) => match cursor.next().into() {
// End of arbitrary property, but there is a potential modifier. // End of arbitrary property, but there is a potential modifier.
// //
// E.g.: `[color:#0088cc]/` // E.g.: `[color:#0088cc]/`
@ -109,7 +109,7 @@ impl UtilityMachine {
fn parse_named_utility(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn parse_named_utility(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match self.named_utility_machine.next(cursor) { match self.named_utility_machine.next(cursor) {
MachineState::Idle => self.restart(), MachineState::Idle => self.restart(),
MachineState::Done(_) => match cursor.next.into() { MachineState::Done(_) => match cursor.next().into() {
// End of a named utility, but there is a potential modifier. // End of a named utility, but there is a potential modifier.
// //
// E.g.: `bg-red-500/` // E.g.: `bg-red-500/`
@ -140,7 +140,7 @@ impl UtilityMachine {
fn parse_modifier(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn parse_modifier(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match self.modifier_machine.next(cursor) { match self.modifier_machine.next(cursor) {
MachineState::Idle => self.restart(), MachineState::Idle => self.restart(),
MachineState::Done(_) => match cursor.next.into() { MachineState::Done(_) => match cursor.next().into() {
// A modifier followed by a modifier is invalid // A modifier followed by a modifier is invalid
Class::Slash => self.restart(), Class::Slash => self.restart(),

View file

@ -16,7 +16,7 @@ impl Machine for VariantMachine {
#[inline] #[inline]
fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState { fn next(&mut self, cursor: &mut cursor::Cursor<'_>) -> MachineState {
match cursor.curr.into() { match cursor.curr().into() {
// Start of an arbitrary variant // Start of an arbitrary variant
// //
// E.g.: `[&:hover]:` // E.g.: `[&:hover]:`
@ -48,7 +48,7 @@ impl VariantMachine {
start_pos: usize, start_pos: usize,
cursor: &mut cursor::Cursor<'_>, cursor: &mut cursor::Cursor<'_>,
) -> MachineState { ) -> MachineState {
match cursor.next.into() { match cursor.next().into() {
// End of an arbitrary value, must be followed by a `:` // End of an arbitrary value, must be followed by a `:`
// //
// E.g.: `[&:hover]:` // E.g.: `[&:hover]:`

View file

@ -10,7 +10,7 @@ pub fn fast_skip(cursor: &Cursor) -> Option<usize> {
return None; return None;
} }
if !cursor.curr.is_ascii_whitespace() { if !cursor.curr().is_ascii_whitespace() {
return None; return None;
} }

View file

@ -10,15 +10,21 @@ use std::sync;
/// - Ignoring common binary file extensions like `.png` and `.jpg` /// - Ignoring common binary file extensions like `.png` and `.jpg`
/// - Ignoring common files like `yarn.lock` and `package-lock.json` /// - Ignoring common files like `yarn.lock` and `package-lock.json`
/// ///
pub static RULES: sync::LazyLock<Gitignore> = sync::LazyLock::new(|| { pub static RULES: sync::LazyLock<Vec<Gitignore>> = sync::LazyLock::new(|| {
let mut builder = GitignoreBuilder::new(""); let mut builder = GitignoreBuilder::new("");
builder.add_line(None, &IGNORED_CONTENT_DIRS_GLOB).unwrap(); builder.add_line(None, &IGNORED_CONTENT_DIRS_GLOB).unwrap();
builder.add_line(None, &IGNORED_EXTENSIONS_GLOB).unwrap(); builder.add_line(None, &IGNORED_EXTENSIONS_GLOB).unwrap();
builder.add_line(None, &BINARY_EXTENSIONS_GLOB).unwrap();
builder.add_line(None, &IGNORED_FILES_GLOB).unwrap(); builder.add_line(None, &IGNORED_FILES_GLOB).unwrap();
builder.build().unwrap() // Ensure these rules do not match on folder names
let mut file_only_builder = GitignoreBuilder::new("");
file_only_builder
.only_on_files(true)
.add_line(None, &BINARY_EXTENSIONS_GLOB)
.unwrap();
vec![builder.build().unwrap(), file_only_builder.build().unwrap()]
}); });
pub static IGNORED_CONTENT_DIRS: sync::LazyLock<Vec<&'static str>> = sync::LazyLock::new(|| { pub static IGNORED_CONTENT_DIRS: sync::LazyLock<Vec<&'static str>> = sync::LazyLock::new(|| {

View file

@ -30,11 +30,9 @@ fn sort_by_dir_and_name(a: &DirEntry, z: &DirEntry) -> Ordering {
pub fn resolve_globs( pub fn resolve_globs(
base: PathBuf, base: PathBuf,
dirs: &[PathBuf], dirs: &FxHashSet<PathBuf>,
extensions: &FxHashSet<String>, extensions: &FxHashSet<String>,
) -> Vec<GlobEntry> { ) -> Vec<GlobEntry> {
let allowed_paths: FxHashSet<PathBuf> = FxHashSet::from_iter(dirs.iter().cloned());
// A list of known extensions + a list of extensions we found in the project. // A list of known extensions + a list of extensions we found in the project.
let mut found_extensions: FxHashSet<String> = let mut found_extensions: FxHashSet<String> =
FxHashSet::from_iter(KNOWN_EXTENSIONS.iter().map(|x| x.to_string())); FxHashSet::from_iter(KNOWN_EXTENSIONS.iter().map(|x| x.to_string()));
@ -72,7 +70,7 @@ pub fn resolve_globs(
continue; continue;
} }
if !allowed_paths.contains(path) { if !dirs.contains(path) {
let mut path = path; let mut path = path;
while let Some(parent) = path.parent() { while let Some(parent) = path.parent() {
if parent == base { if parent == base {
@ -113,7 +111,8 @@ pub fn resolve_globs(
continue; continue;
} }
if !allowed_paths.contains(path) { if !dirs.contains(path) {
it.skip_current_dir();
continue; continue;
} }

View file

@ -37,6 +37,7 @@ cpio
cr2 cr2
cur cur
dat dat
db
dcm dcm
deb deb
dex dex
@ -65,6 +66,7 @@ eol
eot eot
epub epub
exe exe
exr
f4v f4v
fbs fbs
fh fh
@ -77,6 +79,7 @@ fpx
fst fst
fvt fvt
g3 g3
geojson
gh gh
gif gif
graffle graffle
@ -85,6 +88,7 @@ gzip
h261 h261
h263 h263
h264 h264
hdr
icns icns
ico ico
ief ief
@ -199,8 +203,6 @@ snap
snk snk
so so
sqlite sqlite
sqlite
sqlite3
sqlite3 sqlite3
stl stl
storedata storedata

View file

@ -1,2 +1,15 @@
.git .git
.hg
.jj
.next
.parcel-cache
.pnpm-store
.svelte-kit
.svn
.turbo
.venv
.vercel
.yarn
__pycache__
node_modules node_modules
venv

View file

@ -3,3 +3,4 @@ lock
sass sass
scss scss
styl styl
log

View file

@ -2,3 +2,5 @@ package-lock.json
pnpm-lock.yaml pnpm-lock.yaml
bun.lockb bun.lockb
.gitignore .gitignore
.env
.env.*

View file

@ -0,0 +1,102 @@
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::path::Path;
use std::sync::{self, Arc, Mutex};
use tracing_subscriber::fmt::writer::BoxMakeWriter;
pub static SHOULD_TRACE: sync::LazyLock<bool> = sync::LazyLock::new(
|| matches!(std::env::var("DEBUG"), Ok(value) if value.eq("*") || (value.contains("tailwindcss:oxide") && !value.contains("-tailwindcss:oxide"))),
);
fn dim(input: &str) -> String {
format!("\u{001b}[2m{input}\u{001b}[22m")
}
fn blue(input: &str) -> String {
format!("\u{001b}[34m{input}\u{001b}[39m")
}
fn highlight(input: &str) -> String {
format!("{}{}{}", dim(&blue("`")), blue(input), dim(&blue("`")))
}
struct MutexWriter(Arc<Mutex<std::fs::File>>);
impl Write for MutexWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.lock().unwrap().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.0.lock().unwrap().flush()
}
}
pub fn init_tracing() {
if !*SHOULD_TRACE {
return;
}
let root = Path::new(".tailwindcss");
let logs_dir = root.join("logs");
if let Err(err) = std::fs::create_dir_all(&logs_dir) {
eprintln!(
"{} Failed to create {}, skipping debug logs ({err})",
dim("[DEBUG]"),
highlight(&logs_dir.display().to_string())
);
return;
}
// Ensure everything inside `.tailwindcss/` is ignored by git. The file is only created if it
// doesn't exist yet, an existing `.gitignore` is left untouched.
if let Ok(mut file) = std::fs::File::create_new(root.join(".gitignore")) {
_ = file.write_all(b"*\n");
}
let file_path = logs_dir.join(format!(
"scanner-{}-{}.log",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis())
.unwrap_or(0),
std::process::id()
));
let file = match OpenOptions::new()
.create(true)
.append(true)
.open(&file_path)
{
Ok(file) => file,
Err(err) => {
eprintln!(
"{} Failed to create {}, skipping debug logs ({err})",
dim("[DEBUG]"),
highlight(&file_path.display().to_string())
);
return;
}
};
let absolute_file_path = dunce::canonicalize(&file_path).unwrap_or_else(|_| file_path.clone());
eprintln!(
"{} Writing debug info to: {}\n",
dim("[DEBUG]"),
highlight(&absolute_file_path.display().to_string())
);
let file = Arc::new(Mutex::new(file));
let writer: BoxMakeWriter = BoxMakeWriter::new({
let file = file.clone();
move || Box::new(MutexWriter(file.clone())) as Box<dyn Write + Send>
});
_ = tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE)
.with_writer(writer)
.with_ansi(false)
.compact()
.try_init();
}

View file

@ -1,5 +1,6 @@
pub mod auto_source_detection; pub mod auto_source_detection;
pub mod detect_sources; pub mod detect_sources;
pub mod init_tracing;
pub mod sources; pub mod sources;
use crate::extractor::{Extracted, Extractor}; use crate::extractor::{Extracted, Extractor};
@ -14,10 +15,10 @@ use bstr::ByteSlice;
use fast_glob::glob_match; use fast_glob::glob_match;
use fxhash::{FxHashMap, FxHashSet}; use fxhash::{FxHashMap, FxHashSet};
use ignore::{gitignore::GitignoreBuilder, WalkBuilder}; use ignore::{gitignore::GitignoreBuilder, WalkBuilder};
use init_tracing::{init_tracing, SHOULD_TRACE};
use rayon::prelude::*; use rayon::prelude::*;
use std::collections::{BTreeMap, BTreeSet};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::{self, Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::SystemTime; use std::time::SystemTime;
use tracing::event; use tracing::event;
@ -31,21 +32,6 @@ use tracing::event;
// //
// @source "do-include-me.bin"; // `.bin` is typically ignored, but now it's explicit so should be included // @source "do-include-me.bin"; // `.bin` is typically ignored, but now it's explicit so should be included
// @source "git-ignored.html"; // A git ignored file that is listed explicitly, should be scanned // @source "git-ignored.html"; // A git ignored file that is listed explicitly, should be scanned
static SHOULD_TRACE: sync::LazyLock<bool> = sync::LazyLock::new(
|| matches!(std::env::var("DEBUG"), Ok(value) if value.eq("*") || (value.contains("tailwindcss:oxide") && !value.contains("-tailwindcss:oxide"))),
);
fn init_tracing() {
if !*SHOULD_TRACE {
return;
}
_ = tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE)
.compact()
.try_init();
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ChangedContent { pub enum ChangedContent {
@ -77,56 +63,75 @@ pub struct Scanner {
/// The walker to detect all files that we have to scan /// The walker to detect all files that we have to scan
walker: Option<WalkBuilder>, walker: Option<WalkBuilder>,
/// All changed content that we have to parse
changed_content: Vec<ChangedContent>,
/// All found extensions /// All found extensions
extensions: FxHashSet<String>, extensions: FxHashSet<String>,
/// All CSS files we want to scan for CSS variable usage
css_files: Vec<PathBuf>,
/// All files that we have to scan /// All files that we have to scan
files: Vec<PathBuf>, files: FxHashSet<PathBuf>,
/// All directories, sub-directories, etc… we saw during source detection /// All directories, sub-directories, etc… we saw during source detection
dirs: Vec<PathBuf>, dirs: FxHashSet<PathBuf>,
/// All generated globs, used for setting up watchers /// All generated globs, used for setting up watchers
globs: Option<Vec<GlobEntry>>, globs: Option<Vec<GlobEntry>>,
/// Track unique set of candidates /// Track unique set of candidates
candidates: FxHashSet<String>, candidates: FxHashSet<String>,
/// Track mtimes for files so incremental scans can skip unchanged files.
mtimes: FxHashMap<PathBuf, SystemTime>,
/// Files that were scanned during the last `scan()` call.
scanned_files: Vec<String>,
/// Whether we've completed at least one full scan. When false, we skip
/// mtime tracking entirely so the initial build stays fast.
has_scanned_once: bool,
/// Whether sources have been scanned since the last `scan()` call
sources_scanned: bool,
} }
impl Scanner { impl Scanner {
pub fn new(sources: Vec<PublicSourceEntry>) -> Self { pub fn new(sources: Vec<PublicSourceEntry>) -> Self {
init_tracing();
if *SHOULD_TRACE {
event!(tracing::Level::INFO, "Provided sources:");
for source in &sources {
event!(tracing::Level::INFO, "Source: {:?}", source);
}
}
let sources = Sources::new(public_source_entries_to_private_source_entries(sources)); let sources = Sources::new(public_source_entries_to_private_source_entries(sources));
if *SHOULD_TRACE {
event!(tracing::Level::INFO, "Optimized sources:");
for source in sources.iter() {
event!(tracing::Level::INFO, "Source: {:?}", source);
}
}
let walker = create_walker(&sources);
Self { Self {
sources: sources.clone(), sources,
walker: create_walker(sources), walker,
..Default::default() ..Default::default()
} }
} }
pub fn scan(&mut self) -> Vec<String> { pub fn scan(&mut self) -> Vec<String> {
init_tracing(); self.sources_scanned = false;
self.scan_sources();
// TODO: performance improvement, bail early if we don't have any changed content let (scanned_blobs, css_files, files) = self.discover_sources();
// if self.changed_content.is_empty() {
// return vec![];
// }
let _new_candidates = self.extract_candidates(); self.extract_candidates(scanned_blobs, css_files);
self.scanned_files = files;
// Make sure we have a sorted list of candidates // Return all candidates sorted
let mut candidates = self.candidates.iter().cloned().collect::<Vec<_>>(); let mut result = self.candidates.iter().cloned().collect::<Vec<_>>();
candidates.par_sort_unstable(); result.par_sort_unstable();
result
// Return all candidates instead of only the new ones
candidates
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
@ -141,7 +146,7 @@ impl Scanner {
// Raw content can be parsed directly, no need to verify if the file exists and is allowed // Raw content can be parsed directly, no need to verify if the file exists and is allowed
// to be scanned. // to be scanned.
self.changed_content.extend(changed_contents); let mut content_to_scan: Vec<ChangedContent> = changed_contents;
// Fully resolve all files // Fully resolve all files
let changed_files = changed_files let changed_files = changed_files
@ -165,7 +170,7 @@ impl Scanner {
}); });
// All known files are allowed to be scanned // All known files are allowed to be scanned
self.changed_content.extend(known_files); content_to_scan.extend(known_files);
// Figure out if the new unknown files are allowed to be scanned // Figure out if the new unknown files are allowed to be scanned
if !new_unknown_files.is_empty() { if !new_unknown_files.is_empty() {
@ -176,6 +181,11 @@ impl Scanner {
continue; continue;
} }
// The walked path can contain symlinks, while the changed files have already
// been canonicalized. Lazily canonicalize the walked path so we can compare
// the real paths as well.
let mut canonical_path: Option<PathBuf> = None;
let mut drop_file_indexes = vec![]; let mut drop_file_indexes = vec![];
for (idx, changed_file) in new_unknown_files.iter().enumerate().rev() { for (idx, changed_file) in new_unknown_files.iter().enumerate().rev() {
let ChangedContent::File(file, _) = changed_file else { let ChangedContent::File(file, _) = changed_file else {
@ -184,9 +194,17 @@ impl Scanner {
// When the file is found on disk it means that all the rules pass. We can // When the file is found on disk it means that all the rules pass. We can
// extract the current file and remove it from the list of passed in files. // extract the current file and remove it from the list of passed in files.
if file == path { let matches = file == path
self.files.push(path.to_path_buf()); // Track for future use || (file.file_name() == path.file_name() && {
self.changed_content.push(changed_file.clone()); // Track for parsing if canonical_path.is_none() {
canonical_path = dunce::canonicalize(path).ok();
}
canonical_path.as_deref() == Some(file.as_path())
});
if matches {
self.files.insert(path.to_path_buf()); // Track for future use
content_to_scan.push(changed_file.clone()); // Track for parsing
drop_file_indexes.push(idx); drop_file_indexes.push(idx);
} }
} }
@ -207,18 +225,17 @@ impl Scanner {
} }
} }
self.extract_candidates() // Read all content into blobs for extraction
let blobs = read_all_files(content_to_scan);
self.extract_candidates(blobs, vec![])
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn extract_candidates(&mut self) -> Vec<String> { fn extract_candidates(&mut self, blobs: Vec<Vec<u8>>, css_files: Vec<PathBuf>) -> Vec<String> {
let changed_content = self.changed_content.drain(..).collect::<Vec<_>>(); // Extract all candidates from the pre-read blobs
let mut new_candidates = parse_all_blobs(blobs);
// Extract all candidates from the changed content
let mut new_candidates = parse_all_blobs(read_all_files(changed_content));
// Extract all CSS variables from the CSS files // Extract all CSS variables from the CSS files
let css_files = self.css_files.drain(..).collect::<Vec<_>>();
if !css_files.is_empty() { if !css_files.is_empty() {
let css_variables = extract_css_variables(read_all_files( let css_variables = extract_css_variables(read_all_files(
css_files css_files
@ -230,63 +247,23 @@ impl Scanner {
new_candidates.extend(css_variables); new_candidates.extend(css_variables);
} }
// Only compute the new candidates and ignore the ones we already have. This is for // Only keep candidates we haven't seen before
// subsequent calls to prevent serializing the entire set of candidates every time. for existing in self.candidates.iter() {
let mut new_candidates = new_candidates new_candidates.remove(existing);
.into_par_iter() }
.filter(|candidate| !self.candidates.contains(candidate))
.collect::<Vec<_>>();
new_candidates.par_sort_unstable();
// Track new candidates for subsequent calls // Track new candidates for subsequent calls
self.candidates.par_extend(new_candidates.clone()); self.candidates.extend(new_candidates.iter().cloned());
new_candidates let mut result: Vec<String> = new_candidates.into_iter().collect();
} result.par_sort_unstable();
#[tracing::instrument(skip_all)] result
fn scan_sources(&mut self) {
let Some(walker) = &mut self.walker else {
return;
};
for entry in walker.build().filter_map(Result::ok) {
let path = entry.into_path();
let Ok(metadata) = path.metadata() else {
continue;
};
if metadata.is_dir() {
self.dirs.push(path);
} else if metadata.is_file() {
let extension = path
.extension()
.and_then(|x| x.to_str())
.unwrap_or_default(); // In case the file has no extension
match extension {
// Special handing for CSS files, we don't want to extract candidates from
// these files, but we do want to extract used CSS variables.
"css" => {
self.css_files.push(path.clone());
}
_ => {
self.changed_content.push(ChangedContent::File(
path.to_path_buf(),
extension.to_owned(),
));
}
}
self.extensions.insert(extension.to_owned());
self.files.push(path);
}
}
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub fn get_files(&mut self) -> Vec<String> { pub fn get_files(&mut self) -> Vec<String> {
self.scan_sources(); let _ = self.discover_sources();
self.files self.files
.par_iter() .par_iter()
@ -294,13 +271,18 @@ impl Scanner {
.collect() .collect()
} }
#[tracing::instrument(skip_all)]
pub fn get_scanned_files(&self) -> Vec<String> {
self.scanned_files.clone()
}
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub fn get_globs(&mut self) -> Vec<GlobEntry> { pub fn get_globs(&mut self) -> Vec<GlobEntry> {
if let Some(globs) = &self.globs { if let Some(globs) = &self.globs {
return globs.clone(); return globs.clone();
} }
self.scan_sources(); let _ = self.discover_sources();
let mut globs = vec![]; let mut globs = vec![];
for source in self.sources.iter() { for source in self.sources.iter() {
@ -374,27 +356,176 @@ impl Scanner {
let i = s.as_ptr() as usize - offset; let i = s.as_ptr() as usize - offset;
let original = &original_content[i..i + s.len()]; let original = &original_content[i..i + s.len()];
if original.contains_str("-[]") { if original.contains_str("-[]") {
return Some(unsafe { return String::from_utf8(original.to_vec())
(String::from_utf8_unchecked(original.to_vec()), i) .ok()
}); .map(|candidate| (candidate, i));
} }
// SAFETY: When we parsed the candidates, we already guaranteed that the byte String::from_utf8(s.to_vec())
// slices are valid, therefore we don't have to re-check here when we want to .ok()
// convert it back to a string. .map(|candidate| (candidate, i))
Some(unsafe { (String::from_utf8_unchecked(s.to_vec()), i) })
} }
_ => None, _ => None,
}) })
.collect() .collect()
} }
#[tracing::instrument(skip_all)]
fn discover_sources(&mut self) -> (Vec<Vec<u8>>, Vec<PathBuf>, Vec<String>) {
if self.sources_scanned {
return (vec![], vec![], vec![]);
}
self.sources_scanned = true;
let Some(walker) = &mut self.walker else {
return (vec![], vec![], vec![]);
};
// Use synchronous walk for the initial build (lower overhead) and parallel
// walk for subsequent calls (watch mode) where the overhead is amortised.
let all_entries = if self.has_scanned_once {
walk_parallel(walker)
} else {
walk_synchronous(walker)
};
let mut css_files: Vec<PathBuf> = vec![];
let mut content_paths: Vec<(PathBuf, String)> = vec![];
let mut changed_files = vec![];
// Fresh state
self.files.clear();
self.dirs.clear();
self.extensions.clear();
self.globs = None;
// Cache canonicalized folders in case a file itself is not symlinked, but any of the parent
// folders are symlinked.
let mut cached_canonical_dirs: FxHashMap<PathBuf, PathBuf> = FxHashMap::default();
for entry in all_entries {
match entry {
WalkEntry::Dir(path) => {
self.dirs.insert(path);
}
WalkEntry::File {
path,
mtime,
is_symlink,
} => {
// Deduplicate: parallel walk can visit the same file from multiple threads
if !self.files.insert(path.clone()) {
continue;
}
// Track canonicalized paths in addition to potentially symlinked file paths
let canonical = if is_symlink {
dunce::canonicalize(&path).ok()
} else {
path.parent().and_then(|parent| {
// Perf: cache the canonicalized parent path such that sibling files don't
// have to canonicalize over and over again.
let canonical_parent = cached_canonical_dirs
.entry(parent.to_path_buf())
.or_insert_with(|| {
dunce::canonicalize(parent)
.unwrap_or_else(|_| parent.to_path_buf())
});
if canonical_parent.as_path() != parent {
path.file_name()
.map(|file_name| canonical_parent.join(file_name))
} else {
None
}
})
};
if let Some(canonical) = canonical {
if canonical != path {
self.files.insert(canonical);
}
}
let extension = path
.extension()
.and_then(|x| x.to_str())
.unwrap_or_default()
.to_owned();
self.extensions.insert(extension.to_owned());
// On incremental scans, check mtime to skip unchanged files.
// On the first scan, track mtimes while still scanning every file.
let changed = if self.has_scanned_once {
match mtime {
Some(mtime) => {
let prev = self.mtimes.insert(path.clone(), mtime);
prev.is_none_or(|prev| prev != mtime)
}
None => true,
}
} else {
if let Some(mtime) = mtime {
self.mtimes.insert(path.clone(), mtime);
}
true
};
if !changed {
continue;
}
if let Ok(file) = path.clone().into_os_string().into_string() {
changed_files.push(file);
}
match extension.as_str() {
// Special handing for CSS files, we don't want to extract candidates from
// these files, but we do want to extract used CSS variables.
"css" => css_files.push(path),
_ => content_paths.push((path, extension)),
}
}
}
}
// Ensure `mtimes` don't include stale files
self.mtimes.retain(|path, _| self.files.contains(path));
// Read + preprocess all discovered files in parallel
let scanned_blobs: Vec<Vec<u8>> = content_paths
.into_par_iter()
.filter_map(|(path, ext)| {
let content = std::fs::read(&path).ok()?;
event!(tracing::Level::INFO, "Reading {:?}", path);
let processed = pre_process_input(content, &ext);
if processed.is_empty() {
None
} else {
Some(processed)
}
})
.collect();
if !self.has_scanned_once {
self.has_scanned_once = true;
}
changed_files.par_sort_unstable();
(scanned_blobs, css_files, changed_files)
}
} }
fn read_changed_content(c: ChangedContent) -> Option<Vec<u8>> { fn read_changed_content(c: ChangedContent) -> Option<Vec<u8>> {
let (content, extension) = match c { let (content, extension) = match c {
ChangedContent::File(file, extension) => match std::fs::read(&file) { ChangedContent::File(file, extension) => match std::fs::read(&file) {
Ok(content) => (content, extension), Ok(content) => {
event!(tracing::Level::INFO, "Reading {:?}", file);
(content, extension)
}
Err(e) => { Err(e) => {
event!(tracing::Level::ERROR, "Failed to read file: {:?}", e); event!(tracing::Level::ERROR, "Failed to read file: {:?}", e);
return None; return None;
@ -404,23 +535,28 @@ fn read_changed_content(c: ChangedContent) -> Option<Vec<u8>> {
ChangedContent::Content(contents, extension) => (contents.into_bytes(), extension), ChangedContent::Content(contents, extension) => (contents.into_bytes(), extension),
}; };
Some(pre_process_input(&content, &extension)) Some(pre_process_input(content, &extension))
} }
pub fn pre_process_input(content: &[u8], extension: &str) -> Vec<u8> { pub fn pre_process_input(content: Vec<u8>, extension: &str) -> Vec<u8> {
use crate::extractor::pre_processors::*; use crate::extractor::pre_processors::*;
match extension { match extension {
"clj" | "cljs" | "cljc" => Clojure.process(content), "clj" | "cljs" | "cljc" => Clojure.process(&content),
"cshtml" | "razor" => Razor.process(content), "heex" | "eex" | "ex" | "exs" => Elixir.process(&content),
"haml" => Haml.process(content), "cshtml" | "razor" => Razor.process(&content),
"json" => Json.process(content), "haml" => Haml.process(&content),
"pug" => Pug.process(content), "json" | "jsonl" | "ndjson" => Json.process(&content),
"rb" | "erb" => Ruby.process(content), "md" | "mdx" => Markdown.process(&content),
"slim" => Slim.process(content), "pug" => Pug.process(&content),
"svelte" => Svelte.process(content), "rb" | "erb" => Ruby.process(&content),
"vue" => Vue.process(content), "slim" | "slang" => Slim.process(&content),
_ => content.to_vec(), "svelte" => Svelte.process(&content),
"rs" => Rust.process(&content),
"tt" | "tt2" | "tx" => TemplateToolkit.process(&content),
"twig" => Twig.process(&content),
"vue" => Vue.process(&content),
_ => content,
} }
} }
@ -439,23 +575,23 @@ fn read_all_files(changed_content: Vec<ChangedContent>) -> Vec<Vec<u8>> {
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn extract_css_variables(blobs: Vec<Vec<u8>>) -> Vec<String> { fn extract_css_variables(blobs: Vec<Vec<u8>>) -> FxHashSet<String> {
extract(blobs, |mut extractor| { extract(blobs, |mut extractor| {
extractor.extract_variables_from_css() extractor.extract_variables_from_css()
}) })
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn parse_all_blobs(blobs: Vec<Vec<u8>>) -> Vec<String> { fn parse_all_blobs(blobs: Vec<Vec<u8>>) -> FxHashSet<String> {
extract(blobs, |mut extractor| extractor.extract()) extract(blobs, |mut extractor| extractor.extract())
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn extract<H>(blobs: Vec<Vec<u8>>, handle: H) -> Vec<String> fn extract<H>(blobs: Vec<Vec<u8>>, handle: H) -> FxHashSet<String>
where where
H: Fn(Extractor) -> Vec<Extracted> + std::marker::Sync, H: Fn(Extractor) -> Vec<Extracted> + std::marker::Sync,
{ {
let mut result: Vec<_> = blobs blobs
.par_iter() .par_iter()
.flat_map(|blob| blob.par_split(|x| *x == b'\n')) .flat_map(|blob| blob.par_split(|x| *x == b'\n'))
.filter_map(|blob| { .filter_map(|blob| {
@ -480,26 +616,113 @@ where
a a
}) })
.into_iter() .into_iter()
.map(|s| unsafe { String::from_utf8_unchecked(s.to_vec()) }) .filter_map(|s| String::from_utf8(s.to_vec()).ok())
.collect(); .collect()
// SAFETY: Unstable sort is faster and in this scenario it's also safe because we are
// guaranteed to have unique candidates.
result.par_sort_unstable();
result
} }
/// Create a walker for the given sources to detect all the files that we have to scan. #[derive(Debug)]
enum WalkEntry {
Dir(PathBuf),
File {
path: PathBuf,
mtime: Option<SystemTime>,
/// Whether the path itself is a symlink
is_symlink: bool,
},
}
impl From<ignore::DirEntry> for WalkEntry {
fn from(entry: ignore::DirEntry) -> Self {
let is_dir = entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false);
let is_symlink = entry.path_is_symlink();
let path = entry.into_path();
if is_dir {
WalkEntry::Dir(path)
} else {
let mtime = path.metadata().ok().and_then(|m| m.modified().ok());
WalkEntry::File {
path,
mtime,
is_symlink,
}
}
}
}
/// Walk the file system synchronously. Used for the initial build where the overhead of spawning
/// parallel walker threads is not worth it.
#[tracing::instrument(skip_all)]
fn walk_synchronous(walker: &mut WalkBuilder) -> Vec<WalkEntry> {
walker
.build()
.filter_map(Result::ok)
.map(WalkEntry::from)
.collect()
}
/// Walk the file system in parallel. Used in watch mode where the parallel walker overhead is
/// amortised across many rebuilds and subsequent calls are much faster.
#[tracing::instrument(skip_all)]
fn walk_parallel(walker: &mut WalkBuilder) -> Vec<WalkEntry> {
struct FlushOnDrop {
local: Vec<WalkEntry>,
shared: Arc<Mutex<Vec<WalkEntry>>>,
}
impl Drop for FlushOnDrop {
fn drop(&mut self) {
if !self.local.is_empty() {
self.shared.lock().unwrap().append(&mut self.local);
}
}
}
let collected: Arc<Mutex<Vec<WalkEntry>>> = Arc::new(Mutex::new(vec![]));
walker.build_parallel().run(|| {
let mut buf = FlushOnDrop {
local: Vec::with_capacity(256),
shared: collected.clone(),
};
Box::new(move |entry| {
let Ok(entry) = entry else {
return ignore::WalkState::Continue;
};
buf.local.push(WalkEntry::from(entry));
if buf.local.len() >= 256 {
buf.shared.lock().unwrap().append(&mut buf.local);
}
ignore::WalkState::Continue
})
});
// All threads have finished and flushed their buffers via FlushOnDrop::drop
Arc::try_unwrap(collected).unwrap().into_inner().unwrap()
}
/// Sets up a WalkBuilder with all source roots, gitignore rules, and source pattern matching.
/// ///
/// The `mtimes` map is used to keep track of the last modified time of each file. This is used to /// This is the common setup shared between the full walker (with mtime tracking for re-scans)
/// determine if a file or folder has changed since the last scan and we can skip folders that /// and the parallel walker (without mtime tracking for the initial scan).
/// haven't changed. fn create_walker(sources: &Sources) -> Option<WalkBuilder> {
fn create_walker(sources: Sources) -> Option<WalkBuilder> {
let mtimes: Arc<Mutex<FxHashMap<PathBuf, SystemTime>>> = Default::default();
let mut other_roots: FxHashSet<&PathBuf> = FxHashSet::default(); let mut other_roots: FxHashSet<&PathBuf> = FxHashSet::default();
let mut first_root: Option<&PathBuf> = None; let mut first_root: Option<&PathBuf> = None;
let mut ignores: BTreeMap<&PathBuf, BTreeSet<String>> = Default::default();
let mut ignores: Vec<(&PathBuf, Vec<String>)> = Default::default();
let mut emit = |base, pattern| match ignores.last_mut() {
Some((prev_base, patterns)) if *prev_base == base => {
patterns.push(pattern);
}
_ => {
ignores.push((base, vec![pattern]));
}
};
for source in sources.iter() { for source in sources.iter() {
match source { match source {
@ -511,7 +734,7 @@ fn create_walker(sources: Sources) -> Option<WalkBuilder> {
} }
} }
SourceEntry::Pattern { base, pattern } => { SourceEntry::Pattern { base, pattern } => {
let mut pattern = pattern.to_string(); let pattern = pattern.to_owned();
if first_root.is_none() { if first_root.is_none() {
first_root = Some(base); first_root = Some(base);
@ -520,35 +743,19 @@ fn create_walker(sources: Sources) -> Option<WalkBuilder> {
} }
if !pattern.contains("**") { if !pattern.contains("**") {
// Ensure that the pattern is pinned to the base path.
if !pattern.starts_with("/") {
pattern = format!("/{pattern}");
}
// Specific patterns should take precedence even over git-ignored files: // Specific patterns should take precedence even over git-ignored files:
ignores emit(base, format!("!{}", pattern));
.entry(base)
.or_default()
.insert(format!("!{}", pattern));
} else { } else {
// Assumption: the pattern we receive will already be brace expanded. So // Assumption: the pattern we receive will already be brace expanded. So
// `*.{html,jsx}` will result in two separate patterns: `*.html` and `*.jsx`. // `*.{html,jsx}` will result in two separate patterns: `*.html` and `*.jsx`.
if let Some(extension) = Path::new(&pattern).extension() { if let Some(extension) = Path::new(&pattern).extension() {
// Extend auto source detection to include the extension // Extend auto source detection to include the extension
ignores emit(base, format!("!*.{}", extension.to_string_lossy()));
.entry(base)
.or_default()
.insert(format!("!*.{}", extension.to_string_lossy()));
} }
} }
} }
SourceEntry::Ignored { base, pattern } => { SourceEntry::Ignored { base, pattern } => {
let mut pattern = pattern.to_string(); emit(base, pattern.to_owned());
// Ensure that the pattern is pinned to the base path.
if !pattern.starts_with("/") {
pattern = format!("/{pattern}");
}
ignores.entry(base).or_default().insert(pattern);
} }
SourceEntry::External { base } => { SourceEntry::External { base } => {
if first_root.is_none() { if first_root.is_none() {
@ -558,16 +765,10 @@ fn create_walker(sources: Sources) -> Option<WalkBuilder> {
} }
// External sources should take precedence even over git-ignored files: // External sources should take precedence even over git-ignored files:
ignores emit(base, "!/**/*".to_owned());
.entry(base)
.or_default()
.insert(format!("!{}", "/**/*"));
// External sources should still disallow binary extensions: // External sources should still disallow binary extensions:
ignores emit(base, BINARY_EXTENSIONS_GLOB.clone());
.entry(base)
.or_default()
.insert(BINARY_EXTENSIONS_GLOB.clone());
} }
} }
} }
@ -630,7 +831,9 @@ fn create_walker(sources: Sources) -> Option<WalkBuilder> {
} }
// Setup auto source detection rules // Setup auto source detection rules
builder.add_gitignore(auto_source_detection::RULES.clone()); for ignore in auto_source_detection::RULES.iter() {
builder.add_gitignore(ignore.clone());
}
// Setup ignores based on `@source` definitions // Setup ignores based on `@source` definitions
for (base, patterns) in ignores { for (base, patterns) in ignores {
@ -642,71 +845,61 @@ fn create_walker(sources: Sources) -> Option<WalkBuilder> {
builder.add_gitignore(ignore); builder.add_gitignore(ignore);
} }
builder.filter_entry({ // Pre-compute source matching data to avoid allocations in the hot filter_entry path
move |entry| { let auto_bases: Vec<PathBuf> = sources
let path = entry.path(); .iter()
.filter_map(|source| match source {
SourceEntry::Auto { base } | SourceEntry::External { base } => Some(base.clone()),
_ => None,
})
.collect();
// Ensure the entries are matching any of the provided source patterns (this is let pattern_sources: Vec<(PathBuf, String)> = sources
// necessary for manual-patterns that can filter the file extension) .iter()
if path.is_file() { .filter_map(|source| match source {
let mut matches = false; SourceEntry::Pattern { base, pattern } => Some((base.into(), pattern.into())),
for source in sources.iter() { _ => None,
match source { })
SourceEntry::Auto { base } | SourceEntry::External { base } => { .collect();
if path.starts_with(base) {
matches = true;
break;
}
}
SourceEntry::Pattern { base, pattern } => {
let mut pattern = pattern.to_string();
// Ensure that the pattern is pinned to the base path.
if !pattern.starts_with("/") {
pattern = format!("/{pattern}");
}
// Check if path starts with base, if so, remove the prefix and check the remainder against the pattern // Source pattern matching filter (lock-free, safe for parallel walking)
let remainder = path.strip_prefix(base); builder.filter_entry(move |entry| {
if remainder.is_ok_and(|remainder| { let path = entry.path();
let mut path_str = remainder.to_string_lossy().to_string();
if !path_str.starts_with("/") {
path_str = format!("/{path_str}");
}
glob_match(pattern, path_str.as_bytes())
}) {
matches = true;
break;
}
}
_ => {}
}
}
if !matches { // Ensure the entries are matching any of the provided source patterns (this is
return false; // necessary for manual-patterns that can filter the file extension)
if path.is_file() {
let mut matches = false;
for base in &auto_bases {
if path.starts_with(base) {
matches = true;
break;
} }
} }
let mut mtimes = mtimes.lock().unwrap(); if !matches {
let current_time = match entry.metadata() { for (base, pattern) in &pattern_sources {
Ok(metadata) if metadata.is_file() => { let remainder = path.strip_prefix(base);
if let Ok(time) = metadata.modified() { if remainder.is_ok_and(|remainder| {
Some(time) let mut path_str = remainder.to_string_lossy().to_string();
} else { if !path_str.starts_with("/") {
None path_str = format!("/{path_str}");
}
glob_match(pattern, path_str.as_bytes())
}) {
matches = true;
break;
} }
} }
_ => None, }
};
let previous_time = if !matches {
current_time.and_then(|time| mtimes.insert(entry.clone().into_path(), time)); return false;
match (current_time, previous_time) {
(Some(current), Some(prev)) if prev == current => false,
_ => true,
} }
} }
true
}); });
Some(builder) Some(builder)

View file

@ -1,7 +1,8 @@
use crate::glob::split_pattern;
use crate::GlobEntry; use crate::GlobEntry;
use bexpand::Expression; use bexpand::Expression;
use std::path::PathBuf; use fxhash::{FxHashMap, FxHashSet};
use ignore::gitignore::Gitignore;
use std::path::{Component, Path, PathBuf};
use tracing::{event, Level}; use tracing::{event, Level};
use super::auto_source_detection::IGNORED_CONTENT_DIRS; use super::auto_source_detection::IGNORED_CONTENT_DIRS;
@ -49,13 +50,14 @@ pub enum SourceEntry {
/// ``` /// ```
Ignored { base: PathBuf, pattern: String }, Ignored { base: PathBuf, pattern: String },
/// External sources are sources outside of your git root which should not /// External sources are directories that are ignored (by us or .gitignore rules), but should be
/// follow gitignore rules. /// included bypassing the default ignore rules.
/// ///
/// Represented by: /// Represented by:
/// ///
/// ```css /// ```css
/// @source "../node_modules/my-lib";` /// @source "../node_modules/my-lib";`
/// @source "../node_modules/my-lib/**/*";`
/// ``` /// ```
External { base: PathBuf }, External { base: PathBuf },
} }
@ -75,6 +77,164 @@ impl Sources {
} }
} }
/// When dealing with a pattern, then it could be that we end up with:
///
/// ```json
/// { base: '/some/folder', pattern: 'foo.ts' }
/// ```
///
/// If we just emit `!foo.ts` for the `/some/folder` path, then _everything_ else in that folder
/// would still be walked (but the result will be ignored).
///
/// Instead, we have to ensure that we ignore everything in that folder _except_ for the `foo.ts`
/// pattern.
///
/// This should be equivalent to:
/// ```gitignore
/// *
/// !foo.ts
/// ```
///
/// However, we have to be careful that we don't start ignoring files/folders that already exist.
/// ```css
/// @source "./some/folder/foo.ts";
/// @source "./some/folder/bar.ts";
/// ```
/// Would result in:
/// ```json
/// { base: '/some/folder', pattern: 'foo.ts' }
/// { base: '/some/folder', pattern: 'bar.ts' }
/// ```
///
/// If we were to blindly emit `*` for each pattern, then the `.gitignore` equivalent would look like
/// this:
/// ```gitignore
/// *
/// !foo.ts
/// *
/// !bar.ts
/// ```
///
/// This would result in ignoring the `foo.ts` file as well. Therefore we only want to insert
/// this `*` pattern when nothing else exists yet.
///
/// There is another problem that we need to solve. Let's say you have a pattern that contains a `*`
/// in the pattern:
/// ```css
/// @source './src/ba*/*.html';
/// ```
///
/// This would result in
/// ```json
/// { base: '/src', pattern: '/ba*/*.html' }
/// ```
///
/// If we now inject the `*` pattern for the `/src` folder, then we wouldn't scan any `ba*` folders
/// (e.g. `bar` or `baz`). For this, we have to make sure that we add inverse patterns for these
/// folders. This would essentially result in:
/// ```gitignore
/// * ← ignore everything
/// !/ba*/ ← except for the `ba*/` pattern, so we scan these folders
/// !/ba*/*.html ← then ensure we scan the `*.html` files in it as well
/// ```
///
fn expand_restricted_patterns(sources: Vec<SourceEntry>) -> Vec<SourceEntry> {
let unrestricted_roots = sources
.iter()
.filter_map(|source| match source {
SourceEntry::Auto { base } | SourceEntry::External { base } => Some(base.clone()),
SourceEntry::Pattern { base, pattern } if pattern.contains("**") => Some(base.clone()),
_ => None,
})
.collect::<Vec<_>>();
// Bases of restricted patterns. Each of these becomes its own walk root with its own
// `*` + `!<pattern>` rules, so an ancestor base must not ignore them recursively.
let pattern_roots = sources
.iter()
.filter_map(|source| match source {
SourceEntry::Pattern { base, .. } => Some(base.clone()),
_ => None,
})
.collect::<Vec<_>>();
let mut restricted_roots: FxHashSet<PathBuf> = FxHashSet::default();
let mut expanded = vec![];
for source in sources {
let SourceEntry::Pattern { base, pattern } = &source else {
expanded.push(source);
continue;
};
// `base` is already included by another `@source` that we know should be walked. This
// includes the case where `base` is _nested_ inside such a root, because everything under
// an unrestricted root is walked already. Restricting it would incorrectly hide siblings
// that the broader source is supposed to pick up.
if unrestricted_roots.iter().any(|root| base.starts_with(root)) {
expanded.push(source);
continue;
}
// Ignore everything in the directory. We will later add the specific patterns we are
// interested in.
if restricted_roots.insert(base.clone()) {
// When another source root is nested inside this base — an unrestricted root, or the
// base of another restricted pattern (which is walked from its own root with its own
// rules) — only ignore direct children so the nested root can still be walked.
let has_nested_root = unrestricted_roots.iter().any(|root| root.starts_with(base))
|| pattern_roots
.iter()
.any(|root| root != base && root.starts_with(base));
let pattern = if has_nested_root { "/*" } else { "*" };
expanded.push(SourceEntry::Ignored {
base: base.clone(),
pattern: pattern.to_owned(),
});
}
// Ensure to _include_ parent paths, otherwise the `*` from above would block walking the
// folders that need to be walked.
//
// ```css
// @source './src/ba*/*.html';
// ```
//
// ```gitignore
// * ← added by the above rule
// !/ba*/ ← this is what we're focusing on in this block
// !/ba*/*.html ← this is added later
// ```
{
let mut dir = PathBuf::new();
let mut components = Path::new(pattern).components().peekable();
while let Some(component) = components.next() {
if components.peek().is_none() {
break;
}
match component {
Component::Prefix(_) | Component::RootDir | Component::CurDir => continue,
Component::ParentDir | Component::Normal(_) => dir.push(component),
}
expanded.push(SourceEntry::Ignored {
base: base.clone(),
pattern: format!("!/{}/", path_to_posix_string(&dir).trim_start_matches('/')),
});
}
}
// Track the original source
expanded.push(source);
}
expanded
}
impl PublicSourceEntry { impl PublicSourceEntry {
/// Optimize the PublicSourceEntry by trying to move all the static parts of the pattern to the /// Optimize the PublicSourceEntry by trying to move all the static parts of the pattern to the
/// base of the PublicSourceEntry. /// base of the PublicSourceEntry.
@ -102,72 +262,456 @@ impl PublicSourceEntry {
/// resolved path. /// resolved path.
pub fn optimize(&mut self) { pub fn optimize(&mut self) {
// Resolve base path immediately // Resolve base path immediately
let Ok(base) = dunce::canonicalize(&self.base) else { let Ok(mut base) = dunce::canonicalize(&self.base) else {
event!(Level::ERROR, "Failed to resolve base: {:?}", self.base); event!(Level::ERROR, "Failed to resolve base: {:?}", self.base);
return; return;
}; };
self.base = base.to_string_lossy().to_string();
// No dynamic part, figure out if we are dealing with a file or a directory. let mut new_pattern = PathBuf::new();
if !self.pattern.contains('*') { enum ComponentStage {
let combined_path = if self.pattern.starts_with("/") { Base,
PathBuf::from(&self.pattern) Pattern,
} else {
PathBuf::from(&self.base).join(&self.pattern)
};
match dunce::canonicalize(combined_path) {
Ok(resolved_path) if resolved_path.is_dir() => {
self.base = resolved_path.to_string_lossy().to_string();
self.pattern = "**/*".to_owned();
}
Ok(resolved_path) if resolved_path.is_file() => {
self.base = resolved_path
.parent()
.unwrap()
.to_string_lossy()
.to_string();
// Ensure leading slash, otherwise it will match against all files in all folders/
self.pattern =
format!("/{}", resolved_path.file_name().unwrap().to_string_lossy());
}
_ => {}
}
return;
} }
let mut stage = ComponentStage::Base;
// Contains dynamic part let mut components = Path::new(&self.pattern).components().peekable();
let (static_part, dynamic_part) = split_pattern(&self.pattern); while let Some(component) = components.next() {
match stage {
ComponentStage::Base => {
match component {
// Ignore the current dir, e.g. `.`
Component::CurDir => {}
let base: PathBuf = self.base.clone().into(); // Go up a directory, e.g. `..`
let base = match static_part { Component::ParentDir => {
Some(static_part) => { base.pop();
// TODO: If the base does not exist on disk, try removing the last slash and try }
// again.
match dunce::canonicalize(base.join(static_part)) { // Once we hit a component that contains a wildcard character, then we
Ok(base) => base, // can't change the base anymore and we must move to the pattern part.
Err(err) => { Component::Normal(part) if part.to_string_lossy().contains("*") => {
event!(tracing::Level::ERROR, "Failed to resolve glob: {:?}", err); new_pattern.push(component);
return; stage = ComponentStage::Pattern;
}
// File or folder, but not the last component
Component::Normal(part) if components.peek().is_some() => {
base.push(part);
}
// Last file or folder. If it's a folder, we move it to the base,
// otherwise we move it to the pattern.
Component::Normal(part) => {
let full_path = base.join(part);
if full_path.is_dir() {
base.push(part);
} else {
new_pattern.push(part);
}
}
// When we're dealing with an absolute path, then we have to bypass the
// `base` entirely.
Component::Prefix(_) => {
base.clear();
base.push(component);
}
Component::RootDir => {
#[cfg(not(windows))]
base.clear();
base.push(component);
}
} }
} }
} ComponentStage::Pattern => {
None => base, new_pattern.push(component);
};
let pattern = match dynamic_part {
Some(dynamic_part) => dynamic_part,
None => {
if base.is_dir() {
"**/*".to_owned()
} else {
"".to_owned()
} }
} }
}; }
self.base = base.to_string_lossy().to_string(); self.base = base.to_string_lossy().to_string();
self.pattern = pattern; self.pattern = path_to_posix_string(&new_pattern);
// Ensure we have `**/*` when the base is a folder and we don't have a pattern at all
if self.pattern == "" {
self.pattern = "/**/*".to_owned();
}
// Ensure that the pattern is pinned to the base path.
else if !self.pattern.starts_with("/") {
self.pattern = format!("/{}", self.pattern);
}
}
}
fn path_to_posix_string(path: &Path) -> String {
let mut parts = Vec::new();
let mut is_rooted = false;
for component in path.components() {
match component {
Component::Prefix(prefix) => {
parts.push(prefix.as_os_str().to_string_lossy().to_string());
}
Component::RootDir => {
is_rooted = true;
if parts.is_empty() {
parts.push(String::new());
}
}
Component::CurDir => {
parts.push(".".to_string());
}
Component::ParentDir => {
parts.push("..".to_string());
}
Component::Normal(part) => {
parts.push(part.to_string_lossy().to_string());
}
}
}
let result = parts.join("/");
if result.is_empty() && is_rooted {
"/".to_string()
} else {
result
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use std::fs;
use tempfile::tempdir;
#[test]
fn path_to_posix_string_serializes_relative_paths() {
let path = PathBuf::from("src").join("**").join("*.html");
assert_eq!(path_to_posix_string(&path), "src/**/*.html");
}
#[test]
fn path_to_posix_string_serializes_rooted_paths() {
let path = PathBuf::from(std::path::MAIN_SEPARATOR.to_string())
.join("src")
.join("**")
.join("*.html");
assert_eq!(path_to_posix_string(&path), "/src/**/*.html");
}
#[test]
fn path_to_posix_string_serializes_empty_paths() {
assert_eq!(path_to_posix_string(&PathBuf::new()), "");
}
#[test]
fn optimize_hoists_static_directories_and_keeps_files_in_the_pattern() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("src").join("examples")).unwrap();
let mut source = PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/examples/index.html".to_string(),
negated: false,
};
source.optimize();
assert_eq!(
source.base,
dunce::canonicalize(dir.path().join("src").join("examples"))
.unwrap()
.to_string_lossy()
);
assert_eq!(source.pattern, "/index.html");
}
#[test]
fn optimize_hoists_folder_patterns() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("src").join("examples")).unwrap();
let mut source = PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/examples".to_string(),
negated: false,
};
source.optimize();
assert_eq!(
source.base,
dunce::canonicalize(dir.path().join("src").join("examples"))
.unwrap()
.to_string_lossy()
);
assert_eq!(source.pattern, "/**/*");
}
#[test]
fn optimize_keeps_wildcards_in_the_pattern() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("src")).unwrap();
let mut source = PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/**/*.html".to_string(),
negated: false,
};
source.optimize();
assert_eq!(
source.base,
dunce::canonicalize(dir.path().join("src"))
.unwrap()
.to_string_lossy()
);
assert_eq!(source.pattern, "/**/*.html");
}
#[test]
fn concrete_patterns_are_expanded_to_restrict_their_base() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("src")).unwrap();
let base = dunce::canonicalize(dir.path().join("src")).unwrap();
let sources = public_source_entries_to_private_source_entries(vec![PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/foo.html".to_string(),
negated: false,
}]);
assert_eq!(
sources,
vec![
SourceEntry::Ignored {
base: base.clone(),
pattern: "*".to_string(),
},
SourceEntry::Pattern {
base,
pattern: "/foo.html".to_string(),
},
]
);
}
#[test]
fn restricted_patterns_include_parent_directory_allow_rules() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("src")).unwrap();
let base = dunce::canonicalize(dir.path().join("src")).unwrap();
let sources = public_source_entries_to_private_source_entries(vec![PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/ef*/*.html".to_string(),
negated: false,
}]);
assert_eq!(
sources,
vec![
SourceEntry::Ignored {
base: base.clone(),
pattern: "*".to_string(),
},
SourceEntry::Ignored {
base: base.clone(),
pattern: "!/ef*/".to_string(),
},
SourceEntry::Pattern {
base,
pattern: "/ef*/*.html".to_string(),
},
]
);
}
#[test]
fn unrestricted_sources_do_not_expand_patterns_for_the_same_base() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("src")).unwrap();
let base = dunce::canonicalize(dir.path().join("src")).unwrap();
let sources = public_source_entries_to_private_source_entries(vec![
PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src".to_string(),
negated: false,
},
PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/foo.html".to_string(),
negated: false,
},
]);
assert_eq!(
sources,
vec![
SourceEntry::Auto { base: base.clone() },
SourceEntry::Pattern {
base,
pattern: "/foo.html".to_string(),
},
]
);
}
#[test]
fn restricted_parent_bases_do_not_open_unrelated_siblings() {
let dir = tempdir().unwrap();
let project = dir.path().join("Users").join("robin").join("docus-test");
fs::create_dir_all(&project).unwrap();
let users = dunce::canonicalize(dir.path().join("Users")).unwrap();
let project = dunce::canonicalize(project).unwrap();
let sources = public_source_entries_to_private_source_entries(vec![
PublicSourceEntry {
base: project.to_string_lossy().to_string(),
pattern: "**/*".to_string(),
negated: false,
},
PublicSourceEntry {
base: project.to_string_lossy().to_string(),
pattern: "../../app.config.ts".to_string(),
negated: false,
},
]);
assert_eq!(
sources,
vec![
SourceEntry::Auto {
base: project.clone(),
},
SourceEntry::Ignored {
base: users.clone(),
pattern: "/*".to_string(),
},
SourceEntry::Pattern {
base: users,
pattern: "/app.config.ts".to_string(),
},
]
);
}
#[test]
fn restricted_patterns_preserve_source_order() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join("src")).unwrap();
let base = dunce::canonicalize(dir.path().join("src")).unwrap();
let sources = public_source_entries_to_private_source_entries(vec![
PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/foo.html".to_string(),
negated: false,
},
PublicSourceEntry {
base: dir.path().to_string_lossy().to_string(),
pattern: "src/foo.html".to_string(),
negated: true,
},
]);
assert_eq!(
sources,
vec![
SourceEntry::Ignored {
base: base.clone(),
pattern: "*".to_string(),
},
SourceEntry::Pattern {
base: base.clone(),
pattern: "/foo.html".to_string(),
},
SourceEntry::Ignored {
base,
pattern: "/foo.html".to_string(),
},
]
);
}
/// Run the public-to-private conversion for an auto-detected source pointing at `base` and
/// return the resulting entry.
fn auto_source_entry(base: &Path) -> SourceEntry {
public_source_entries_to_private_source_entries(vec![PublicSourceEntry {
base: base.to_string_lossy().to_string(),
pattern: "**/*".to_string(),
negated: false,
}])
.into_iter()
.next()
.unwrap()
}
#[test]
fn auto_detected_folders_become_auto_sources() {
let dir = tempdir().unwrap();
let base = dir.path().join("src");
fs::create_dir_all(&base).unwrap();
let base = dunce::canonicalize(&base).unwrap();
assert_eq!(auto_source_entry(&base), SourceEntry::Auto { base });
}
#[test]
fn folders_ignored_by_default_become_external_sources() {
let dir = tempdir().unwrap();
let base = dir.path().join("node_modules").join("my-lib");
fs::create_dir_all(&base).unwrap();
let base = dunce::canonicalize(&base).unwrap();
assert_eq!(auto_source_entry(&base), SourceEntry::External { base });
}
#[test]
fn folders_ignored_by_gitignore_become_external_sources() {
let dir = tempdir().unwrap();
// Pretend this is a git repository so the `.gitignore` search is bounded to it.
fs::create_dir_all(dir.path().join(".git")).unwrap();
fs::write(dir.path().join(".gitignore"), "dist/\n").unwrap();
let base = dir.path().join("dist");
fs::create_dir_all(&base).unwrap();
let base = dunce::canonicalize(&base).unwrap();
assert_eq!(auto_source_entry(&base), SourceEntry::External { base });
}
#[test]
fn folders_ignored_by_a_parent_gitignore_become_external_sources() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join(".git")).unwrap();
// A `.gitignore` higher up in the tree should still apply to nested directories.
fs::write(dir.path().join(".gitignore"), "generated/\n").unwrap();
let base = dir.path().join("packages").join("app").join("generated");
fs::create_dir_all(&base).unwrap();
let base = dunce::canonicalize(&base).unwrap();
assert_eq!(auto_source_entry(&base), SourceEntry::External { base });
}
#[test]
fn folders_not_ignored_by_gitignore_stay_auto_sources() {
let dir = tempdir().unwrap();
fs::create_dir_all(dir.path().join(".git")).unwrap();
fs::write(dir.path().join(".gitignore"), "dist/\n").unwrap();
let base = dir.path().join("src");
fs::create_dir_all(&base).unwrap();
let base = dunce::canonicalize(&base).unwrap();
assert_eq!(auto_source_entry(&base), SourceEntry::Auto { base });
} }
} }
@ -218,18 +762,111 @@ pub fn public_source_entries_to_private_source_entries(
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Compiled `.gitignore` matchers are cached per directory so we read and parse each
// `.gitignore` file at most once, even though entries commonly share ancestor directories (e.g.
// the repository root). A cached `None` means the directory has no `.gitignore` file.
let mut gitignores: FxHashMap<PathBuf, Option<Gitignore>> = FxHashMap::default();
// Boundary for the `.gitignore` walk when a source is not inside a git repository (see below).
let cwd = std::env::current_dir()
.map(|cwd| dunce::canonicalize(&cwd).unwrap_or(cwd))
.ok();
// Convert from public SourceEntry to private SourceEntry // Convert from public SourceEntry to private SourceEntry
expanded_globs let sources = expanded_globs
.into_iter() .into_iter()
.map(Into::into) .map(|public_source| {
.collect::<Vec<_>>() let mut source: SourceEntry = public_source.into();
// Promote auto-sources to external sources if they were gitignored
if let SourceEntry::Auto { ref base } = source {
let inside_git_repo = base.ancestors().any(|dir| dir.join(".git").exists());
// Walk up from the folder, applying each `.gitignore` relative to the directory
// that contains it (matching git), and stop at the git repository root so
// `.gitignore` files outside of the repo are not considered.
for dir in base.ancestors() {
let gitignore = gitignores.entry(dir.to_path_buf()).or_insert_with(|| {
let path = dir.join(".gitignore");
// `Gitignore::new` roots the matcher at the directory containing the file,
// so patterns match relative to it.
path.is_file().then(|| Gitignore::new(&path).0)
});
// Only `.gitignore` files in ancestors of `base` can ignore `base` itself.
// Patterns in `base`'s own `.gitignore` only match paths _inside_ `base`, never
// `base` itself (the file walker still applies them to `base`'s contents).
//
// Skipping `base`'s own `.gitignore` also prevents a false positive for
// whitelist style `.gitignore` files, because relativizing `base` against
// itself yields the empty path, which incorrectly matches `/*`.
//
// E.g.:
//
// ```gitignore
// /*
// !/.gitignore
// !/app
// !/public
// ```
//
// Everything inside `base` except `.gitignore`, `app` and `public` is ignored,
// but `base` itself is not.
if dir != base {
if let Some(gitignore) = gitignore {
if gitignore
.matched_path_or_any_parents(&base, true)
.is_ignore()
{
source = SourceEntry::External { base: base.into() };
break;
}
}
}
// Stop at the git repository root.
if dir.join(".git").exists() {
break;
}
// Without a git repository there is no repository root to stop at. Stop once
// the directory contains the current working directory instead, so `.gitignore`
// files outside of the project (e.g. in the user's home directory) can never
// promote a source to an external source. Note that the file walker still
// applies those `.gitignore` files when deciding which files to scan.
if !inside_git_repo && cwd.as_ref().is_some_and(|cwd| cwd.starts_with(dir)) {
break;
}
}
}
source
})
.collect::<Vec<SourceEntry>>();
expand_restricted_patterns(sources)
} }
/// Convert a public source entry to a source entry /// Convert a public source entry to a source entry
impl From<PublicSourceEntry> for SourceEntry { impl From<PublicSourceEntry> for SourceEntry {
fn from(value: PublicSourceEntry) -> Self { fn from(value: PublicSourceEntry) -> Self {
let auto = value.pattern.ends_with("**/*") if value.negated {
|| PathBuf::from(&value.base).join(&value.pattern).is_dir(); return SourceEntry::Ignored {
base: value.base.into(),
pattern: value.pattern,
};
}
let auto =
value.pattern == "/**/*" || PathBuf::from(&value.base).join(&value.pattern).is_dir();
if !auto {
return SourceEntry::Pattern {
base: value.base.into(),
pattern: value.pattern,
};
}
let inside_ignored_content_dir = IGNORED_CONTENT_DIRS.iter().any(|dir| { let inside_ignored_content_dir = IGNORED_CONTENT_DIRS.iter().any(|dir| {
value.base.contains(&format!( value.base.contains(&format!(
@ -239,24 +876,16 @@ impl From<PublicSourceEntry> for SourceEntry {
std::path::MAIN_SEPARATOR std::path::MAIN_SEPARATOR
)) || value )) || value
.base .base
.ends_with(&format!("{}{}", std::path::MAIN_SEPARATOR, dir,)) .ends_with(&format!("{}{}", std::path::MAIN_SEPARATOR, dir))
}); });
match (value.negated, auto, inside_ignored_content_dir) { match inside_ignored_content_dir {
(false, true, false) => SourceEntry::Auto { false => SourceEntry::Auto {
base: value.base.into(), base: value.base.into(),
}, },
(false, true, true) => SourceEntry::External { true => SourceEntry::External {
base: value.base.into(), base: value.base.into(),
}, },
(false, false, _) => SourceEntry::Pattern {
base: value.base.into(),
pattern: value.pattern,
},
(true, _, _) => SourceEntry::Ignored {
base: value.base.into(),
pattern: value.pattern,
},
} }
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -126,6 +126,72 @@ test(
}, },
) )
// https://github.com/tailwindlabs/tailwindcss/issues/19706
test(
'Config files (TS, valid types)',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"tailwindcss": "workspace:^",
"@tailwindcss/cli": "workspace:^"
},
"devDependencies": {
"typescript": "^5.9.3"
}
}
`,
'tsconfig.json': json`
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"composite": true,
"outDir": "./.build",
"skipLibCheck": true
},
"include": ["tailwind.config.ts"]
}
`,
'index.html': html`
<div class="text-primary"></div>
`,
'tailwind.config.ts': ts`
import type { Config } from 'tailwindcss'
function defineConfig(config: Config): Config {
return config
}
export default defineConfig({
theme: {
extend: {
colors: {
primary: 'blue',
},
},
},
})
`,
'src/index.css': css`
@import 'tailwindcss';
@config '../tailwind.config.ts';
`,
},
},
async ({ fs, exec }) => {
// We expect that these commands don't crash:
await exec('pnpm tsc -b')
await exec('pnpm tailwindcss --input src/index.css --output dist/out.css')
await fs.expectFileToContain('dist/out.css', [candidate`text-primary`])
},
)
test( test(
'Config files (CJS, watch mode)', 'Config files (CJS, watch mode)',
{ {

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
import { candidate, css, html, json, test } from '../utils' import { candidate, css, html, json, test, ts } from '../utils'
test( test(
'builds the `@tailwindcss/typography` plugin utilities', 'builds the `@tailwindcss/typography` plugin utilities',
@ -42,12 +42,86 @@ test(
await fs.expectFileToContain('dist/out.css', [ await fs.expectFileToContain('dist/out.css', [
candidate`prose`, candidate`prose`,
':where(h1):not(:where([class~="not-prose"],[class~="not-prose"] *))', ':where(h1):not(:where([class~="not-prose"], [class~="not-prose"] *))',
':where(tbody td, tfoot td):not(:where([class~="not-prose"],[class~="not-prose"] *))', ':where(tbody td, tfoot td):not(:where([class~="not-prose"], [class~="not-prose"] *))',
]) ])
}, },
) )
test(
'builds the `@tailwindcss/typography` plugin utilities with `@variant` usages',
{
fs: {
'package.json': json`
{
"dependencies": {
"@tailwindcss/typography": "^0.5.14",
"tailwindcss": "workspace:^",
"@tailwindcss/cli": "workspace:^"
}
}
`,
'index.html': html`
<div className="prose prose-custom">
<h1>Headline</h1>
<p>
Until now, trying to style an article, document, or blog post with Tailwind has been a
tedious task that required a keen eye for typography and a lot of complex custom CSS.
</p>
</div>
`,
'src/index.css': css`
@import 'tailwindcss/utilities';
@theme {
--breakpoint-sm: 640px;
}
@plugin '@tailwindcss/typography';
@config '../tailwind.config.js';
@custom-variant custom (&.custom);
`,
'tailwind.config.js': ts`
module.exports = {
theme: {
typography: ({ theme }) => ({
custom: {
css: {
hr: {
'--x': '1',
'@variant sm:custom': {
'--x': '2',
},
},
},
},
}),
},
}
`,
},
},
async ({ fs, exec, expect }) => {
await exec('pnpm tailwindcss --input src/index.css --output dist/out.css')
// We don't want to see `@variant` in the output
let contents = await fs.read('dist/out.css')
expect(contents).not.toContain('@variant')
expect(await fs.dumpFiles('dist/out.css')).toMatchInlineSnapshot(`
"
--- dist/out.css ---
.prose-custom :where(hr):not(:where([class~="not-prose"], [class~="not-prose"] *)) {
--x: 1;
@media (width >= 640px) {
&.custom {
--x: 2;
}
}
}
"
`)
},
)
test( test(
'builds the `@tailwindcss/forms` plugin utilities', 'builds the `@tailwindcss/forms` plugin utilities',
{ {
@ -117,7 +191,7 @@ test(
await fs.expectFileToContain('dist/out.css', [ await fs.expectFileToContain('dist/out.css', [
// //
`::-webkit-date-and-time-value`, `::-webkit-date-and-time-value`,
`[type='checkbox']:indeterminate`, `input:where([type='checkbox']):indeterminate`,
]) ])
// No classes are included even though they are used in the HTML // No classes are included even though they are used in the HTML
@ -207,3 +281,88 @@ test(
]) ])
}, },
) )
// https://github.com/tailwindlabs/tailwindcss/issues/15844
test(
'builds CSS with a custom plugin compiled from TypeScript',
{
fs: {
'package.json': json`
{
"type": "module",
"dependencies": {
"tailwindcss": "workspace:^",
"@tailwindcss/cli": "workspace:^"
},
"devDependencies": {
"typescript": "^5.7.2"
}
}
`,
'tsconfig.json': json`
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"composite": true,
"rootDir": "./src",
"outDir": "./.build",
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
`,
'index.html': html`
<div class="test-red"></div>
`,
'src/index.css': css`
@import 'tailwindcss';
@plugin '../.build/plugin.js';
`,
'src/plugin.ts': ts`
import plugin from 'tailwindcss/plugin'
export const typedPlugin = plugin(() => {
return ({ matchComponents }) => {
matchComponents(
{
test: (content: string) => ({
color: content,
}),
},
{
values: {
red: 'red',
},
},
)
}
})
export default plugin(({ matchComponents }) => {
matchComponents(
{
test: (content: string) => ({
color: content,
}),
},
{
values: {
red: 'red',
},
},
)
})
`,
},
},
async ({ fs, exec }) => {
// We expect that these commands don't crash:
await exec('pnpm tsc -b')
await exec('pnpm tailwindcss --input src/index.css --output dist/out.css')
await fs.expectFileToContain('dist/out.css', [candidate`test-red`])
},
)

View file

@ -1,6 +1,7 @@
import nodeFs from 'node:fs/promises'
import os from 'node:os' import os from 'node:os'
import path from 'node:path' import path from 'node:path'
import { candidate, css, html, js, json, test } from '../utils' import { candidate, css, html, IS_WINDOWS, js, json, test, ts } from '../utils'
const STANDALONE_BINARY = (() => { const STANDALONE_BINARY = (() => {
switch (os.platform()) { switch (os.platform()) {
@ -15,6 +16,41 @@ const STANDALONE_BINARY = (() => {
} }
})() })()
test(
'does not scan itself for candidates',
{
fs: {
'package.json': json`
{
"dependencies": {}
}
`,
'src/index.css': css` @import 'tailwindcss'; `,
},
},
async ({ root, fs, exec }) => {
let sourceBinary = path.resolve(
__dirname,
`../../packages/@tailwindcss-standalone/dist/${STANDALONE_BINARY}`,
)
let binary = IS_WINDOWS ? 'tailwindcss.exe' : 'tailwindcss'
let localBinary = path.join(root, binary)
await nodeFs.copyFile(sourceBinary, localBinary)
if (!IS_WINDOWS) {
await nodeFs.chmod(localBinary, 0o755)
}
await exec(`${IS_WINDOWS ? binary : `./${binary}`} --input src/index.css --output dist/out.css`)
await fs.expectFileNotToContain('dist/out.css', [
candidate`flex`,
candidate`grid`,
candidate`underline`,
])
},
)
test( test(
'includes first-party plugins', 'includes first-party plugins',
{ {
@ -122,7 +158,7 @@ test(
}) })
}) })
`, `,
'src/plugin.ts': js` 'src/plugin.ts': ts`
import plugin from 'tailwindcss/plugin' import plugin from 'tailwindcss/plugin'
// Make sure all available JS APIs can be imported and used // Make sure all available JS APIs can be imported and used
@ -132,8 +168,7 @@ test(
import defaultTheme from 'tailwindcss/defaultTheme' import defaultTheme from 'tailwindcss/defaultTheme'
import * as pkg from 'tailwindcss/package.json' import * as pkg from 'tailwindcss/package.json'
export interface PluginOptions { export interface PluginOptions {}
}
export default plugin(function ({ addUtilities }) { export default plugin(function ({ addUtilities }) {
addUtilities({ addUtilities({

View file

@ -0,0 +1,243 @@
import { css, js, json, test, yaml } from '../utils'
// This test runs the wasm build using the `node:wasi` runtime.
//
// There are currently a known problems that the Node WASI preview implementation does not properly
// handle FS reads on macOS and it does not implement all APIs on Windows. Because of that, this
// test is only run on Linux for now.
//
// https://github.com/nodejs/node/issues/47193
// https://github.com/nodejs/uvwasi/issues/11
let testFn = process.platform === 'linux' ? test : test.skip
testFn(
'@tailwindcss/oxide-wasm32-wasi can be loaded into a Node.js process',
{
fs: {
'package.json': json`
{
"dependencies": {
"@tailwindcss/oxide-wasm32-wasi": "workspace:^"
}
}
`,
'src/index.css': css`@import 'tailwindcss/utilities';`,
'src/index.js': js`
const className = "content-['src/index.js']"
module.exports = { className }
`,
'index.mjs': js`
import { Scanner } from '@tailwindcss/oxide-wasm32-wasi'
import { join, resolve } from 'node:path'
let scanner = new Scanner({
sources: [
{
base: join(process.cwd(), 'src'),
pattern: '**/*',
negated: false,
},
],
})
console.log(JSON.stringify(scanner.scan()))
process.exit()
`,
},
},
async ({ expect, exec }) => {
let output = await exec(`node index.mjs`)
expect(JSON.parse(output)).toMatchInlineSnapshot(`
[
"className",
"const",
"content-['src/index.js']",
"exports",
]
`)
},
)
testFn(
'`@tailwindcss/oxide` falls back to the wasm build when no native binding is available',
{
fs: {
'package.json': json`
{
"dependencies": {
"@tailwindcss/oxide": "workspace:^"
}
}
`,
'pnpm-workspace.yaml': yaml`
# Trick pnpm in only supporting an architecture that @tailwindcss/oxide
# doesn't support, and therefore should fallback to the wasm version.
supportedArchitectures:
os:
- openbsd
cpu:
- x64
`,
'src/index.js': js`
const className = "content-['src/index.js']"
module.exports = { className }
`,
'index.mjs': js`
import { createRequire } from 'node:module'
import { join } from 'node:path'
let require = createRequire(import.meta.url)
let { Scanner } = require('@tailwindcss/oxide')
let loaded = Object.keys(require.cache)
let scanner = new Scanner({
sources: [
{
base: join(process.cwd(), 'src'),
pattern: '**/*',
negated: false,
},
],
})
console.log(
JSON.stringify({
native: loaded.filter((file) => file.endsWith('.node')),
wasi: loaded.some((file) => file.endsWith('tailwindcss-oxide.wasi.cjs')),
candidates: scanner.scan(),
}),
)
process.exit()
`,
},
},
async ({ expect, exec }) => {
// Since vitest runs under `pnpm run`, pnpm's bin shims export a NODE_PATH
// that includes the repository's hidden hoist directory
// (`node_modules/.pnpm/node_modules`), which links every workspace package,
// including all native `@tailwindcss/oxide-*` bindings.
//
// Node uses `NODE_PATH` exactly when the local `node_modules` lookup fails,
// which would defeat the simulated unsupported platform, so clear it.
let output = await exec(`node index.mjs`, { env: { NODE_PATH: '' } })
let { native, wasi, candidates } = JSON.parse(output)
// No native binding was installed or loaded, ...
expect(native).toEqual([])
// ... the wasm32-wasi binding is what actually loaded, ...
expect(wasi).toBe(true)
// ... and scanning real files on disk works through it.
expect(candidates).toMatchInlineSnapshot(`
[
"className",
"const",
"content-['src/index.js']",
"exports",
]
`)
},
)
testFn(
'the wasm build loads even when preopening the filesystem root is denied',
{
fs: {
'package.json': json`
{
"dependencies": {
"@tailwindcss/oxide": "workspace:^"
}
}
`,
'pnpm-workspace.yaml': yaml`
# Trick pnpm in only supporting an architecture that @tailwindcss/oxide
# doesn't support, and therefore should fallback to the wasm version.
supportedArchitectures:
os:
- openbsd
cpu:
- x64
`,
// The wasm bindings generated by `@napi-rs/cli` preopen the filesystem
// root, which sandboxed platforms (e.g. OpenHarmony, Android) deny with
// `UVWASI_EACCES`, making the wasm fallback fail to load on exactly the
// platforms that need it.
//
// We patch `@napi-rs/cli`'s templates to retry with narrower preopens
// (see `patches/@napi-rs__cli@3.7.4.patch`). Simulate such a sandbox by
// denying `/` preopens.
'preload.cjs': js`
const wasi = require('node:wasi')
const RealWASI = wasi.WASI
wasi.WASI = class WASI extends RealWASI {
constructor(options) {
if (options?.preopens?.['/'] !== undefined) {
const error = new Error('UVWASI_EACCES, uvwasi_init')
error.code = 'UVWASI_EACCES'
error.syscall = 'uvwasi_init'
throw error
}
super(options)
}
}
`,
'src/index.js': js`
const className = "content-['src/index.js']"
module.exports = { className }
`,
'index.mjs': js`
import { createRequire } from 'node:module'
import { join } from 'node:path'
let require = createRequire(import.meta.url)
let { Scanner } = require('@tailwindcss/oxide')
let loaded = Object.keys(require.cache)
let scanner = new Scanner({
sources: [
{
base: join(process.cwd(), 'src'),
pattern: '**/*',
negated: false,
},
],
})
console.log(
JSON.stringify({
wasi: loaded.some((file) => file.endsWith('tailwindcss-oxide.wasi.cjs')),
candidates: scanner.scan(),
}),
)
process.exit()
`,
},
},
async ({ expect, exec }) => {
// See the note about NODE_PATH in the test above.
let output = await exec(`node --require ./preload.cjs index.mjs`, { env: { NODE_PATH: '' } })
// Only parse the first line, because Node prints an `ExperimentalWarning`
// about WASI to stderr, which `exec` appends to the output.
let { wasi, candidates } = JSON.parse(output.trim().split('\n')[0])
// The wasm32-wasi binding loaded despite `/` being denied, ...
expect(wasi).toBe(true)
// ... and scanning files under the current working directory still works
// through the narrower preopen.
expect(candidates).toMatchInlineSnapshot(`
[
"className",
"const",
"content-['src/index.js']",
"exports",
]
`)
},
)

View file

@ -3,7 +3,9 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"dedent": "1.5.3", "dedent": "catalog:",
"fast-glob": "^3.3.3" "fast-glob": "^3.3.3",
"source-map-js": "^1.2.1",
"yaml": "^2.9.0"
} }
} }

Some files were not shown because too many files have changed in this diff Show more