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>
This PR improves the PostCSS migrations to make sure that we install
`@tailwindcss/postcss` in the same bucket as `tailwindcss`.
If `tailwindcss` exists in the `dependencies` bucket, we install
`@tailwindcss/postcss` in the same bucket. If `tailwindcss` exists in
the `devDependencies` bucket, we install `@tailwindcss/postcss` in the
same bucket.
This also contains an internal refactor that normalizes the package
manager to make sure we can install a package to the correct bucket
depending on the package manager.
---------
Co-authored-by: Adam Wathan <adam.wathan@gmail.com>
This PR updates the `extractStaticPlugins` function to also emit options as long as these are objects containing of only `string` and `number` values.
While doing this I also cleaned up the `require('custom-plugin')` detector to use a Tree-Sitter query instead of operating on the AST.
Here are the two cases we considered:
```js
import plugin1 from 'plugin1';
export default {
plugins: [
plugin1({
foo: 'bar',
num: 19,
}),
require('./plugin2')({
foo: 'bar',
num: 19,
}),
]
}
```
The test plan also contains a number of scenarios that we do not want to migrate to CSS (because we do not have a CSS API we can use for e.g. nested objects). We do support all types that we also support in the CSS API.
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
This PR attempts to detect simple postcss setups: These are setups that
do not load dynamic modules and are based off the examples we are
[recommending in our
docs](https://tailwindcss.com/docs/installation/using-postcss). We
detect wether a config is appropriate by having it use the object plugin
config and by not requiring any other modules:
```js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
```
When we find such a config file, we will go over it line-by-line and
attempt to:
- Upgrade `tailwindcss:` to `'@tailwindcss/postcss':`
- Remove `autoprefixer` if used
We then attempt to install and remove the respective npm packages based
on the package manger we detect.
And since we now have logic to upgrade packages, this also makes sure to
install `tailwindcss@next` at the end of a sucessful migration.
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This PR adds a codemod that ensures that some parts of your stylesheet
are wrapped in an `@layer`.
This is a follow-up PR of #14411, in that PR we migrate `@tailwind`
directives to imports.
As a quick summary, that will turn this:
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
Into:
```css
@import 'tailwindcss';
```
But there are a few issues with that _if_ we have additional CSS on the
page. For example let's imagine we had this:
```css
@tailwind base;
body {
background-color: red;
}
@tailwind components;
.btn {}
@tailwind utilities;
```
This will now be turned into:
```css
@import 'tailwindcss';
body {
background-color: red;
}
.btn {}
```
But in v4 we use real layers, in v3 we used to replace the directive
with the result of that layer. This means that now the `body` and `.btn`
styles are in the incorrect spot.
To solve this, we have to wrap them in a layer. The `body` should go in
an `@layer base`, and the `.btn` should be in an `@layer components` to
make sure it's in the same spot as it was before.
That's what this PR does, the original input will now be turned into:
```css
@import 'tailwindcss';
@layer base {
body {
background-color: red;
}
}
@layer components {
.btn {
}
}
```
There are a few internal refactors going on as well, but those are less
important.
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!;
}
```