41 Commits

Author SHA1 Message Date
Jordan Pittman
a8c54acaba
Prepare v4.0.0 release (#15693) 2025-01-21 20:58:59 +00:00
Robin Malfait
8a97a6a8d9
v4.0.0-beta.10 (#15691)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-01-21 16:19:28 +00:00
depfu[bot]
fcee63d997
Update @types/braces 3.0.4 → 3.0.5 (patch) (#15612) 2025-01-13 10:53:47 +01:00
depfu[bot]
0ed4d81925
Update tree-sitter 0.22.1 → 0.22.4 (minor) (#15585) 2025-01-10 10:26:48 +01:00
Robin Malfait
aac8c5a12a
Prepare v4.0.0-beta.9 release (#15583)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2025-01-09 17:04:34 +00:00
depfu[bot]
acdb406f4f
Update prettier 3.3.3 → 3.4.2 (minor) (#15564)
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 (3.3.3 → 3.4.2) ·
[Repo](https://github.com/prettier/prettier) ·
[Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)



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

<blockquote><p dir="auto">🔗 <a
href="https://bounce.depfu.com/github.com/prettier/prettier/blob/main/CHANGELOG.md#342">Changelog</a></p></blockquote>
<h4><a
href="https://github.com/prettier/prettier/releases/tag/3.4.1">3.4.1</a></h4>

<blockquote><p dir="auto">🔗 <a
href="https://bounce.depfu.com/github.com/prettier/prettier/blob/main/CHANGELOG.md#341">Changelog</a></p></blockquote>
<p><em>Does any of this look wrong? <a
href="https://depfu.com/packages/npm/prettier/feedback">Please let us
know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="52829385bc...cca946176c">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-01-09 17:27:12 +01:00
depfu[bot]
fd84f9d639
Update enhanced-resolve 5.17.1 → 5.18.0 (minor) (#15582)
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.17.1 → 5.18.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.18.0">5.18.0</a></h4>

<blockquote><h3 dir="auto">Features</h3>
<ul dir="auto">
<li>Added wildcards support for aliases</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="https://depfu.com/packages/npm/enhanced-resolve/feedback">Please
let us know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="247edebc90...27e457a905">See
the full diff on Github</a>. The new version differs by 9 commits:</p>
<ul>
<li><a
href="27e457a905"><code>chore(release):
5.18.0</code></a></li>
<li><a
href="88ceebe3cc"><code>feat:
add wildcards support for aliases</code></a></li>
<li><a
href="35b67ce834"><code>feat:
add wildcards</code></a></li>
<li><a
href="4fbcfa1c83"><code>chore(deps):
bump cross-spawn from 7.0.3 to 7.0.6</code></a></li>
<li><a
href="572a54f0c6"><code>chore(deps):
bump cross-spawn from 7.0.3 to 7.0.6</code></a></li>
<li><a
href="af4e2fb155"><code>ci:
add Node.js v23</code></a></li>
<li><a
href="bf443c04ac"><code>ci:
add Node.js v23</code></a></li>
<li><a
href="72999caf00"><code>chore(deps):
bump micromatch from 4.0.5 to 4.0.8</code></a></li>
<li><a
href="fbee162cc2"><code>chore(deps):
bump micromatch from 4.0.5 to 4.0.8</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-01-09 17:26:48 +01:00
Robin Malfait
c9dfe17cac
Prepare v4.0.0-beta.8 release (#15418) 2024-12-17 13:31:28 +01:00
Robin Malfait
0072f01376
Prepare v4.0.0-beta.7 release (#15392)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-12-13 14:18:21 +00:00
Jordan Pittman
3d0b86c7d2
Prepare v4.0.0-beta.6 release (#15325) 2024-12-06 14:32:21 -05:00
Philipp Spiess
85da88f851
Prepare v4.0.0-beta.5 (#15285)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-12-04 16:28:16 +01:00
Robin Malfait
973650624d
Prepare v4.0.0-beta.4 (#15245) 2024-11-29 17:18:42 +01:00
Philipp Spiess
6abd8086c3 Prepare v4.0.0-beta.3 (#15217)
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
2024-11-27 18:25:37 +01:00
depfu[bot]
068b1c2c76
Update tree-sitter 0.22.0 → 0.22.1 (minor) (#15130) 2024-11-26 14:06:25 +01:00
Jordan Pittman
bd43d63df2
Prepare v4.0.0-beta.2 release (#15104)
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-11-22 11:17:21 -05:00
Jordan Pittman
5e4f565fe4
Prepare v4.0.0-beta.1 release (#15070) 2024-11-21 13:20:30 -05:00
Robin Malfait
11dce5af48
v4.0.0-alpha.36 (#15062)
Prepare v4.0.0-alpha.36
2024-11-21 14:20:56 +01:00
Robin Malfait
8b098fc83d
Prepare v4.0.0-alpha.35 release (#15052) 2024-11-20 11:12:32 +00:00
depfu[bot]
22d36cc95b
Update tree-sitter-typescript 0.23.0 → 0.23.2 (minor) (#15026) 2024-11-18 10:23:22 +01:00
Philipp Spiess
5edf6c7dc0
Ensure clients pin the tailwindcss version (#15011)
We noticed that in the current alpha 34 release, the `package.json` file
of the `@tailwindcss/node` package only defines `tailwindcss` as a dev
dependency. This makes it very easy for version mismatches to happen
when a v3 version (or an earlier v4 alpha for that matter) was installed
in the same project:

```json
{
  "name": "@tailwindcss/node",
  "version": "4.0.0-alpha.34",
  "description": "A utility-first CSS framework for rapidly building custom user interfaces.",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/tailwindlabs/tailwindcss.git",
    "directory": "packages/@tailwindcss-node"
  },
  "bugs": "https://github.com/tailwindlabs/tailwindcss/issues",
  "homepage": "https://tailwindcss.com",
  "files": [
    "dist/"
  ],
  "publishConfig": {
    "provenance": true,
    "access": "public"
  },
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    },
    "./require-cache": {
      "types": "./dist/require-cache.d.ts",
      "default": "./dist/require-cache.js"
    },
    "./esm-cache-loader": {
      "types": "./dist/esm-cache.loader.d.mts",
      "default": "./dist/esm-cache.loader.mjs"
    }
  },
  "devDependencies": {
    "tailwindcss": "4.0.0-alpha.34"
  },
  "dependencies": {
    "enhanced-resolve": "^5.17.1",
    "jiti": "^2.0.0-beta.3"
  },
  "scripts": {
    "build": "tsup-node",
    "dev": "pnpm run build -- --watch"
  }
}
```

Furthermore, we were trying to fix issues where our integration test
setup could not install `tailwindcss@3` because of how we did pnpm
overrides.

This PR fixes this by:

- Ensuring every client that calls into `tailwindcss` core marks it as a
version-pinned dependency. You are still required to install
`tailwindcss` in your project along side a client (e.g.
`@tailwindcss/vite`) but we now only use your installed version for
importing the respective `.css` files. For the core logic, we are now
requiring each package to use `tailwindcss` at the same version. This
should help resolve issues like
https://github.com/tailwindlabs/tailwindcss/discussions/14652
- We tried to eliminate the dependency on `tailwindcss` from the
`@tailwindcss/upgrade` package. Unfortunately this is not possible to do
right now because we need to load the CSS files from v4 to create the
right environment. In a future version we could bundle the required CSS
files with `@tailwidncss/upgrade` but it doesn't seem necessary for now.
- We then changed our integration test overrides to only override the
`tailwindcss` package that are dependencies of the known list of
packages that we have `tailwindcss` dependencies on: `@tailwindcss/node`
and `@tailwindcss/upgrade`. This ensures that we can install v3 of
`tailwindcss` in the integration tests and it will work. Something we
want to do for some upgrade tests.

# Test plan

Integration work again. Furthermore we added a quick setup with the CLI
using the local tarballs and ensured it works:

```bash
pnpm init
pnpm install ../../tailwindcss/dist/tailwindcss-cli.tgz 
pnpm install ../../tailwindcss/dist/tailwindcss.tgz 
echo '@import "tailwindcss";' > index.css
echo '<div class="underline"></div>' > index.html
pnpm tailwindcss -i index.css -o out.css
cat out.css
```
2024-11-15 17:18:48 +01:00
Philipp Spiess
953ecd2d19
Prepare v4.0.0-alpha.34 (#15002) 2024-11-14 18:23:40 +01:00
Adam Wathan
437579d3f0
Prepare v4.0.0-alpha.33 release (#14967)
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-11-11 20:28:41 -05:00
Adam Wathan
7da9272d0f
Prepare v4.0.0-alpha.32 (#14954)
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-11-11 11:05:22 -05:00
Robin Malfait
75eeed85b6
Fix crash during upgrade when content globs escape root of project (#14896)
This PR fixes an issue where globs in you `content` configuration escape
the current "root" of the project.

This can happen if you have a folder, and you need to look up in the
tree (e.g.: when looking at another package in a monorepo, or in case of
a Laravel project where you want to look at mail templates).

This applies a similar strategy we already implement on the Rust side.

1. Expand braces in the globs
2. Move static parts of the `pattern` to the `base` of the glob entry
object

---

Given a project setup like this:
```
.
├── admin
│   ├── my-tailwind.config.ts
│   └── src
│       ├── abc.jpg
│       ├── index.html
│       ├── index.js
│       └── styles
│           └── input.css
├── dashboard
│   ├── src
│   │   ├── index.html
│   │   ├── index.js
│   │   ├── input.css
│   │   └── pickaday.css
│   └── tailwind.config.ts
├── package-lock.json
├── package.json
├── postcss.config.js
└── unrelated
    └── index.html

7 directories, 14 files
```


If you then have this config:
```ts
// admin/my-tailwind.config.ts
export default {
  content: {
    relative: true,
    files: ['./src/**/*.html', '../dashboard/src/**/*.html'],
                            //  ^^  this is the important part, which escapes
                            //      the current root of the project.
  },
  theme: {
    extend: {
      colors: {
        primary: 'red',
      },
    },
  },
}
```


Then before this change, running the command looks like this:
<img width="1760" alt="image"
src="https://github.com/user-attachments/assets/60e2dfc7-3751-4432-80e3-8b4b8f1083d4">


After this change, running the command looks like this:
<img width="1452" alt="image"
src="https://github.com/user-attachments/assets/5c47182c-119c-4732-a253-2dace7086049">

---------

Co-authored-by: Philipp Spiess <hello@philippspiess.com>
2024-11-07 12:22:25 +00:00
depfu[bot]
df6dfb012c
Update tree-sitter 0.21.1 → 0.22.0 (major) (#14858) 2024-11-04 15:14:46 +01:00
depfu[bot]
840c9e65b9
Update postcss-selector-parser 6.1.2 → 7.0.0 (major) (#14834)
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-selector-parser (6.1.2 → 7.0.0) ·
[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.0.0">7.0.0</a></h4>

<blockquote><h1 dir="auto">7.0.0</h1>
<ul dir="auto">
<li>Feat: make insertions during iteration safe (major)</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="https://depfu.com/packages/npm/postcss-selector-parser/feedback">Please
let us know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="1b1e9c3bc1...6158750aab">See
the full diff on Github</a>. The new version differs by 2 commits:</p>
<ul>
<li><a
href="6158750aab"><code>chore(release):
7.0.0</code></a></li>
<li><a
href="4fa6e860a1"><code>feat!:
make insertions during iteration safe (#295)</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>
2024-10-30 15:27:53 -04:00
Robin Malfait
94ea5e225b
Prepare v4.0.0-alpha.31 release (#14823)
Prepare v4.0.0-alpha.31 release
2024-10-30 07:53:51 -04:00
Jordan Pittman
10a8f1a725
Prepare v4.0.0-alpha.30 release (#14789) 2024-10-24 16:22:08 -04:00
depfu[bot]
2e0446c503
Update picocolors 1.0.1 → 1.1.1 (minor) (#14771) 2024-10-24 11:25:50 +02:00
Philipp Spiess
2327e68bc7
Prepare v4.0.0-alpha.29 release (#14761) 2024-10-23 15:30:26 +02:00
RobinMalfait
c6572ab929 Add codemod for border style compatibility (#14746)
This PR adds a codemod that ensures that the border styles from Tailwind CSS v3 work as expected once your project is migrated to Tailwind CSS v4.

In Tailwind CSS v3, the default border color is `colors.gray.200` and in Tailwind CSS v4 the default border color is `currentColor`.

Similarly in Tailwind CSS v3, DOM elements such as `input`, `select`, and `textarea` have a border width of `0px`, in Tailwind CSS v4, we don't change the border width of these elements and keep them as `1px`.

If your project happens to already use the same value for the default border color (`currentColor`) as we use in Tailwind CSS v4, then nothing happens. But this is very unlikely, so we will make sure that we honor your `borderColor.DEFAULT` value.

If you didn't change the default values in your `tailwind.config.js`, then we will inject compatibility CSS using the default Tailwind CSS v3 values to ensure the default color and width are applied correctly.
2024-10-22 17:41:50 +00:00
Adam Wathan
b701ed6916
Prepare v4.0.0-alpha.28 release (#14709)
Co-authored-by: Adam Wathan <4323180+adamwathan@users.noreply.github.com>
2024-10-17 17:03:28 -04:00
Jordan Pittman
92a43d6904
Speed up template migrations (#14679)
This PR does two things:
- Computes UTF-16 string positions in Rust rather than in JS —
eliminating a significant number of traversals of the input string
- Applies replacements to the content in ascending order so we only ever
move forward through the source string — this lets v8 optimize string
concatenation
2024-10-16 13:13:48 +02:00
Philipp Spiess
a75152d162
Release v4.0.0-alpha.27 (#14671) 2024-10-15 10:28:34 +00:00
Philipp Spiess
468cb5e99e
Detect and migrate static plugin usages (#14648)
This PR builds on top of the new [JS config to CSS
migration](https://github.com/tailwindlabs/tailwindcss/pull/14651) and
extends it to support migrating _static_ plugins.

What are _static_ plugins you might ask? Static plugins are plugins
where we can statically determine that these are coming from a different
file (so there is nothing inside the JS config that creates them). An
example for this is this config file:

```js
import typographyPlugin from '@tailwindcss/typography'
import { type Config } from 'tailwindcss'

export default {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  darkMode: 'selector',
  plugins: [typographyPlugin],
} satisfies Config
```

Here, the `plugins` array only has one element and it is a static import
from the `@tailwindcss/typography` module. In this PR we attempt to
parse the config file via Tree-sitter to extract the following
information from this file:

- What are the contents of the `plugins` array
- What are statically imported resources from the file

We then check if _all_ entries in the `plugins` array are either static
resources or _strings_ (something I saw working in some tests but I’m
not sure it still does). We migrate the JS config file to CSS if all
plugins are static and we can migrate them to CSS `@plugin` calls.

## Todo

This will need to be rebased after the updated tests in #14648
2024-10-14 17:45:36 +02:00
Robin Malfait
39e108d5f5
release v4.0.0-alpha.26 2024-10-03 16:39:59 +02:00
Philipp Spiess
65240c9240
Template migrations: Migrate v3 prefixes to v4 (#14557)
This PR adds a new migration that can migrate Tailwind CSS v3 style
prefixes into Tailwind CSS v4.

The migration is split into three separate pieces of work:

1. Firstly, we need to read the full JavaScript config to get the _old_
prefix option. This is necessary because in v4, we will not allow things
like custom-separators for the prefix. From this option we will then try
and compute a new prefix (in 90% of the cases this is going to just
remove the trailing `-` but it can also work in more complex cases).
2. Then we migrate all Candidates. The important thing here is that we
need to operate on the raw candidate string because by relying on
`parseCandidate` (which we do for all other migrations) would not work,
as the candidates are not valid in v4 syntax. More on that in a bit.
3. Lastly we also make sure to update the CSS config to include the new
prefix. This is done by prepending the prefix option like so:
    
    ```css
    @import "tailwindcss" prefix(tw);
    ```

### Migrating candidates

The main difference between v3 prefixes and v4 prefixes is that in v3,
the prefix was _part of the utility_ where as in v4 it is _always in
front of the CSS class.

So, for example, this candidate in v3: 

```
hover:-tw-mr-4
```

Would be converted to the following in v4:

```
tw:hover:-mr-4
```

Since the first example _won't parse as a valid Candidate in v4, as the
`tw-mr` utility does not exist, we have to operate on the raw candidate
string first. To do this I created a fork of the `parseCandidate`
function _without any validation of utilities or variants_. This is used
to identify part of the candidate that is the `base` and then ensuring
the `base` starts with the old prefix. We then remove this to create an
"unprefixed" candidate that we validate against a version of the
DesignSystem _with no prefixes configured_. If the variant is valid this
way, we can then print it again with the `DesignSystem` that has the new
prefix to get the migrated version.

Since we set up the `DesignSystem` to include the new prefix, we can
also be certain that migrations that happen afterwards would still
disqualify candidates that aren't valid according to the new prefix
policy. This does mean we need to have the prefix fixup be the first
step in our pipeline.

One interesting bit is that in v3, arbitrary properties did not require
prefixes where as in v4 they do. So the following candidate:

```
[color:red]
```

Will be converted to:

```
tw:[color:red]
```
2024-10-01 18:04:08 +02:00
Philipp Spiess
732147a761
Add setup for template migrations (#14502)
This PR adds the initial setup and a first codemod for the template
migrations. These are a new set of migrations that operate on files
defined in the Tailwind v3 config as part of the `content` option (so
your HTML, JavaScript, TSX files etc.).

The migration for this is integrated in the new `@tailwindcss/upgrade`
package and will require pointing the migration to an input JavaScript
config file, like this:

```
npx @tailwindcss/upgrade --config tailwind.config.js
```

The idea of template migrations is to apply breaking changes from the v3
to v4 migration within your template files.

## Migrating !important syntax

The first migration that I’m adding with this PR is to ensure we use the
v4 important syntax that has the exclamation mark at the end of the
utility.

For example, this:

```html
<div class="!flex sm:!block"></div>
```

Will now turn into:

```html
<div class="flex! sm:block!"></div>
```

## Architecture considerations

Implementation wise, we make use of Oxide to scan the content files fast
and efficiently. By relying on the same scanner als Tailwind v4, we
guarantee that all candidates that are part of the v4 output will have
gone through a migration.

Migrations itself operate on the abstract `Candidate` type, similar to
the type we use in the v4 codebase. It will parse the candidate into its
parts so they can easily be introspected/modified. Migrations are typed
as:

```ts
type TemplateMigration = (candidate: Candidate) => Candidate | null
``` 

`null` should be returned if the `Candidate` does not need a migration. 

We currently use the v4 `parseCandidate` function to get an abstract
definition of the candidate rule that we can operate on. _This will
likely need to change in the future as we need to fork `parseCandidate`
for v3 specific syntax_.

Additionally, we're inlining a `printCandidate` function that can
stringify the abstract `Candidate` type. It is not guaranteed that this
is an identity function since some information can be lost during the
parse step. This is not a problem though, because migrations will only
run selectively and if none of the selectors trigger, the candidates are
not updated. h/t to @RobinMalfait for providing the printer.

So the overall flow of a migration looks like this:

- Scan the config file for `content` files
- Use Oxide to extract a list of candidate and their positions from
these `content` files
- Run a few migrations that operate on the `Candidate` abstract type.
- Print the updated `Candidate` back into the original `content` file.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-09-25 16:20:14 +02:00
Robin Malfait
c094fadbbc
Release v4.0.0-alpha.25 (#14507) 2024-09-24 17:03:00 +00:00
Robin Malfait
d14249ddc2
Add CSS codemods for migrating @layer utilities (#14455)
This PR adds CSS codemods for migrating existing `@layer utilities` to
`@utility` directives.

This PR has the ability to migrate the following cases:

---

The most basic case is when you want to migrate a simple class to a
utility directive.

Input:
```css
@layer utilities {
  .foo {
    color: red;
  }

  .bar {
    color: blue;
  }
}
```

Output:
```css
@utility foo {
  color: red;
}

@utility bar {
  color: blue;
}
```

You'll notice that the class `foo` will be used as the utility name, the
declarations (and the rest of the body of the rule) will become the body
of the `@utility` definition.

---

In v3, every class in a selector will become a utility. To correctly
migrate this to `@utility` directives, we have to register each class in
the selector and generate `n` utilities.

We can use nesting syntax, and replace the current class with `&` to
ensure that the final result behaves the same.

Input:
```css
@layer utilities {
  .foo .bar .baz {
    color: red;
  }
}
```

Output:
```css
@utility foo {
  & .bar .baz {
    color: red;
  }
}

@utility bar {
  .foo & .baz {
    color: red;
  }
}

@utility .baz {
  .foo .bar & {
    color: red;
  }
}
```

In this case, it could be that you know that some of them will never be
used as a utility (e.g.: `hover:bar`), but then you can safely remove
them.

---

Even classes inside of `:has(…)` will become a utility. The only
exception to the rule is that we don't do it for `:not(…)`.

Input:
```css
@layer utilities {
  .foo .bar:not(.qux):has(.baz) {
    display: none;
  }
}
```

Output:
```css
@utility foo {
  & .bar:not(.qux):has(.baz) {
    display: none;
  }
}

@utility bar {
  .foo &:not(.qux):has(.baz) {
    display: none;
  }
}

@utility baz {
  .foo .bar:not(.qux):has(&) {
    display: none;
  }
}
```

Notice that there is no `@utility qux` because it was used inside of
`:not(…)`.

---

When classes are nested inside at-rules, then these classes will also
become utilities. However, the `@utility <name>` will be at the top and
the at-rules will live inside of it. If there are multiple classes
inside a shared at-rule, then the at-rule will be duplicated for each
class.

Let's look at an example to make it more clear:

Input:
```css
@layer utilities {
  @media (min-width: 640px) {
    .foo {
      color: red;
    }

    .bar {
      color: blue;
    }

    @media (min-width: 1024px) {
      .baz {
        color: green;
      }

      @media (min-width: 1280px) {
        .qux {
          color: yellow;
        }
      }
    }
  }
}
```

Output:
```css
@utility foo {
  @media (min-width: 640px) {
    color: red;
  }
}

@utility bar {
  @media (min-width: 640px) {
    color: blue;
  }
}

@utility baz {
  @media (min-width: 640px) {
    @media (min-width: 1024px) {
      color: green;
    }
  }
}

@utility qux {
  @media (min-width: 640px) {
    @media (min-width: 1024px) {
      @media (min-width: 1280px) {
        color: yellow;
      }
    }
  }
}
```

---

When classes result in multiple `@utility` directives with the same
name, then the definitions will be merged together.

Input:
```css
@layer utilities {
  .no-scrollbar::-webkit-scrollbar {
    display: none;
  }

  .no-scrollbar {
    -ms-overflow-style: none;
    scrollbar-width: none;
  }
}
```

Intermediate representation:
```css
@utility no-scrollbar {
  &::-webkit-scrollbar {
    display: none;
  }
}

@utility no-scrollbar {
  -ms-overflow-style: none;
  scrollbar-width: none;
}
```

Output:
```css
@utility no-scrollbar {
  &::-webkit-scrollbar {
    display: none;
  }
  -ms-overflow-style: none;
  scrollbar-width: none
}
```

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2024-09-24 18:17:09 +02:00
Robin Malfait
ee7e02b1f3
Add initial codemod tooling (#14434)
This PR adds some initial tooling for codemods. We are currently only
interested in migrating CSS files, so we will be using PostCSS under the
hood to do this. This PR also implements the "migrate `@apply`" codemod
from #14412.

The usage will look like this:

```sh
npx @tailwindcss/upgrade
```

You can pass in CSS files to transform as arguments:

```sh
npx @tailwindcss/upgrade src/**/*.css
```

But, if none are provided, it will search for CSS files in the current
directory and its subdirectories.

```
≈ tailwindcss v4.0.0-alpha.24

│ No files provided. Searching for CSS files in the current
│ directory and its subdirectories…

│ Migration complete. Verify the changes and commit them to
│ your repository.
```

The tooling also requires the Git repository to be in a clean state.
This is a common convention to ensure that everything is undo-able. If
we detect that the git repository is dirty, we will abort the migration.

```
≈ tailwindcss v4.0.0-alpha.24

│ Git directory is not clean. Please stash or commit your
│ changes before migrating.

│ You may use the `--force` flag to override this safety
│ check.
```


---

This PR alsoo adds CSS codemods for migrating existing `@apply`
directives to the new version.

This PR has the ability to migrate the following cases:

---

In v4, the convention is to put the important modifier `!` at the end of
the utility class instead of right before it. This makes it easier to
reason about, especially when you are variants.

Input:
```css
.foo {
  @apply !flex flex-col! hover:!items-start items-center;
}
```

Output:
```css
.foo {
  @apply flex! flex-col! hover:items-start! items-center;
}
```


---

In v4 we don't support `!important` as a marker at the end of `@apply`
directives. Instead, you can append the `!` to each utility class to
make it `!important`.

Input:
```css
.foo {
  @apply flex flex-col !important;
}
```

Output:
```css
.foo {
  @apply flex! flex-col!;
}
```
2024-09-18 16:45:43 +02:00