Commit graph

7 commits

Author SHA1 Message Date
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
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
57f87be440
Resolve imports from CSS file (#15010)
This PR adds an improvement to the upgrade tool to make sure that if you
pass a single CSS file, that the upgrade tool resolves all the imports
in that file and processes them as well.


Test plan:
---

Created a project where `index.css` imports `other.css`. Another
`leave-me-alone.css` is created to proof that this file is _not_
changed. Running the upgrade guide using `index.css` also migrates
`other.css` but not `leave-me-alone.css`.

Here is a video so you don't have to manually create it:



https://github.com/user-attachments/assets/20decf77-77d2-4a7c-8ff1-accb1c77f8c1
2024-11-15 22:06:52 +01:00
Robin Malfait
894bf9f5ef
Support migrating projects with multiple config files (#14863)
When migrating a project from Tailwind CSS v3 to Tailwind CSS v4, then
we started the migration process in the following order:

1. Migrate the JS/TS config file
2. Migrate the source files (found via the `content` option)
3. Migrate the CSS files

However, if you have a setup where you have multiple CSS root files
(e.g.: `frontend` and `admin` are separated), then that typically means
that you have an `@config` directive in your CSS files. These point to
the Tailwind CSS config file.

This PR changes the migration order to do the following:

1. Build a tree of all the CSS files
2. For each `@config` directive, migrate the JS/TS config file
3. For each JS/TS config file, migrate the source files

If a CSS file does not contain any `@config` directives, then we start
by filling in the `@config` directive with the default Tailwind CSS
config file (if found, or the one passed in). If no default config file
or passed in config file can be found, then we will error out (just like
we do now)

---------

Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-04 16:52:11 +00:00
Robin Malfait
430836f651
Ensure layer(…) on @import is only removed when @utility is present (#14783)
This PR fixes an issue where `layer(…)` next to imports were removed
where they shouldn't have been removed.

The issue exists if _any_ of the `@import` nodes in a file contains
`@utility`, if that's the case then we removed the `layer(…)` next to
_all_ `@import` nodes.

Before we were checking if the current sheet contained `@utility` or in
any of its children (sub-`@import` nodes).

This fixes that by looping over the `@import` nodes in the current
sheet, and looking for the `@utility` in the associated/imported file.
This way we update each node individually.

Test plan:
---

Added a dedicated integration test to make sure all codemods together
result in the correct result. Input:

96e8908378/integrations/upgrade/index.test.ts (L2076-L2108)

Output:

96e8908378/integrations/upgrade/index.test.ts (L2116-L2126)
2024-10-24 14:33:10 -04:00
Robin Malfait
d2865c3e58
Remove layer(utilities) if imports contain @utility (#14738)
We have a migration that adds the `layer(…)` next to the `@import`
depending on the order of original values. For example:
```css
@import "tailwindcss/utilities":
@import "./foo.css":
@import "tailwindcss/components":
```

Will be turned into:
```css
@import "tailwindcss":
@import "./foo.css" layer(utilities):
```

Because it used to exist between `utilities` and `components`. Without
this it would be _after_ `components`.

This results in an issue if an import has (deeply) nested `@utility`
at-rules after migrations. This is because if this is generated:
```css
/* ./src/index.css */
@import "tailwindcss";
@import "./foo.css" layer(utilities);

/* ./src/foo.css */
@utility foo {
  color: red;
}
```

Once we interpret this (and thus flatten it), the final CSS would look
like:
```css
@layer utilities {
  @utility foo {
    color: red;
  }
}
```

This means that `@utility` is not top-level and an error would occur.

This fixes that by removing the `layer(…)` from the import if the
imported file (or any of its children) contains an `@utility`. This is
to ensure that once everything is imported and flattened, that all
`@utility` at-rules are top-level.
2024-10-21 23:46:24 +02:00
Jordan Pittman
4d1becd2f9
Migrate utilities in CSS files imported into layers (#14617)
When a stylesheet is imported with `@import “…” layer(utilities)` that
means that all classes in that stylesheet and any of its imported
stylesheets become candidates for `@utility` conversion.

Doing this correctly requires us to place `@utility` rules into separate
stylesheets (usually) and replicate the import tree without layers as
`@utility` MUST be root-level. If a file consists of only utilities we
won't create a separate file for it and instead place the `@utility`
rules in the same stylesheet.

Been doing a LOT of pairing with @RobinMalfait on this one but I think
this is finally ready to be looked at

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-10-10 15:44:04 +02:00