tailwindcss/packages/@tailwindcss-upgrade/src/index.ts

273 lines
7.8 KiB
TypeScript
Raw Normal View History

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
#!/usr/bin/env node
import { globby } from 'globby'
import fs from 'node:fs/promises'
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
import path from 'node:path'
import postcss from 'postcss'
import { formatNodes } from './codemods/format-nodes'
import { sortBuckets } from './codemods/sort-buckets'
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
import { help } from './commands/help'
import {
analyze as analyzeStylesheets,
linkConfigs as linkConfigsToStylesheets,
migrate as migrateStylesheet,
split as splitStylesheets,
} from './migrate'
Add simple JS config migration (#14639) This PR implements the first version of JS config file migration to CSS. It is based on the most simple config setups we are using in the Tailwind UI templates Commit, Primer, Radiant, and Studio. The example we use in the integration test is a config that looks like this: ```js import { type Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' module.exports = { darkMode: 'selector', content: ['./src/**/*.{html,js}'], theme: { boxShadow: { sm: '0 2px 6px rgb(15 23 42 / 0.08)', }, colors: { red: { 500: '#ef4444', }, }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], sm: ['0.875rem', { lineHeight: '1.5rem' }], base: ['1rem', { lineHeight: '2rem' }], }, extend: { colors: { red: { 600: '#dc2626', }, }, fontFamily: { sans: 'Inter, system-ui, sans-serif', display: ['Cabinet Grotesk', ...defaultTheme.fontFamily.sans], }, borderRadius: { '4xl': '2rem', }, }, }, plugins: [], } satisfies Config ``` As you can see, this file only has a `darkMode` selector, custom `content` globs, a `theme` (with some theme keys being overwriting the default theme and some others extending the defaults). Note that it does not support `plugins` and/or `presets` yet. In the case above, we will find the CSS file containing the existing `@tailwind` directives and are migrating it to the following: ```css @import 'tailwindcss'; @source './**/*.{html,js}'; @variant dark (&:where(.dark, .dark *)); @theme { --box-shadow-*: initial; --box-shadow-sm: 0 2px 6px rgb(15 23 42 / 0.08); --color-*: initial; --color-red-500: #ef4444; --font-size-*: initial; --font-size-xs: 0.75rem; --font-size-xs--line-height: 1rem; --font-size-sm: 0.875rem; --font-size-sm--line-height: 1.5rem; --font-size-base: 1rem; --font-size-base--line-height: 2rem; --color-red-600: #dc2626; --font-family-sans: Inter, system-ui, sans-serif; --font-family-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --border-radius-4xl: 2rem; } ``` This replicates all features of the JS config so we can even delete the existing JS config in this case. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-10-11 15:27:53 +02:00
import { migrateJsConfig } from './migrate-js-config'
import { migratePostCSSConfig } from './migrate-postcss'
import { migratePrettierPlugin } from './migrate-prettier'
import { Stylesheet } from './stylesheet'
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
import { migrate as migrateTemplate } from './template/migrate'
import { prepareConfig } from './template/prepare-config'
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
import { args, type Arg } from './utils/args'
import { isRepoDirty } from './utils/git'
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 13:22:25 +01:00
import { hoistStaticGlobParts } from './utils/hoist-static-glob-parts'
import { pkg } from './utils/packages'
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
import { eprintln, error, header, highlight, info, success } from './utils/renderer'
const options = {
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
'--config': { type: 'string', description: 'Path to the configuration file', alias: '-c' },
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
'--help': { type: 'boolean', description: 'Display usage information', alias: '-h' },
'--force': { type: 'boolean', description: 'Force the migration', alias: '-f' },
'--version': { type: 'boolean', description: 'Display the version number', alias: '-v' },
} satisfies Arg
const flags = args(options)
if (flags['--help']) {
help({
usage: ['npx @tailwindcss/upgrade'],
options,
})
process.exit(0)
}
async function run() {
Add simple JS config migration (#14639) This PR implements the first version of JS config file migration to CSS. It is based on the most simple config setups we are using in the Tailwind UI templates Commit, Primer, Radiant, and Studio. The example we use in the integration test is a config that looks like this: ```js import { type Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' module.exports = { darkMode: 'selector', content: ['./src/**/*.{html,js}'], theme: { boxShadow: { sm: '0 2px 6px rgb(15 23 42 / 0.08)', }, colors: { red: { 500: '#ef4444', }, }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], sm: ['0.875rem', { lineHeight: '1.5rem' }], base: ['1rem', { lineHeight: '2rem' }], }, extend: { colors: { red: { 600: '#dc2626', }, }, fontFamily: { sans: 'Inter, system-ui, sans-serif', display: ['Cabinet Grotesk', ...defaultTheme.fontFamily.sans], }, borderRadius: { '4xl': '2rem', }, }, }, plugins: [], } satisfies Config ``` As you can see, this file only has a `darkMode` selector, custom `content` globs, a `theme` (with some theme keys being overwriting the default theme and some others extending the defaults). Note that it does not support `plugins` and/or `presets` yet. In the case above, we will find the CSS file containing the existing `@tailwind` directives and are migrating it to the following: ```css @import 'tailwindcss'; @source './**/*.{html,js}'; @variant dark (&:where(.dark, .dark *)); @theme { --box-shadow-*: initial; --box-shadow-sm: 0 2px 6px rgb(15 23 42 / 0.08); --color-*: initial; --color-red-500: #ef4444; --font-size-*: initial; --font-size-xs: 0.75rem; --font-size-xs--line-height: 1rem; --font-size-sm: 0.875rem; --font-size-sm--line-height: 1.5rem; --font-size-base: 1rem; --font-size-base--line-height: 2rem; --color-red-600: #dc2626; --font-family-sans: Inter, system-ui, sans-serif; --font-family-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --border-radius-4xl: 2rem; } ``` This replicates all features of the JS config so we can even delete the existing JS config in this case. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-10-11 15:27:53 +02:00
let base = process.cwd()
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
eprintln(header())
eprintln()
let cleanup: (() => void)[] = []
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
if (!flags['--force']) {
if (isRepoDirty()) {
error('Git directory is not clean. Please stash or commit your changes before migrating.')
info(
`You may use the ${highlight('--force')} flag to silence this warning and perform the migration.`,
)
process.exit(1)
}
}
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
{
// Stylesheet migrations
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
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
// Use provided files
Add simple JS config migration (#14639) This PR implements the first version of JS config file migration to CSS. It is based on the most simple config setups we are using in the Tailwind UI templates Commit, Primer, Radiant, and Studio. The example we use in the integration test is a config that looks like this: ```js import { type Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' module.exports = { darkMode: 'selector', content: ['./src/**/*.{html,js}'], theme: { boxShadow: { sm: '0 2px 6px rgb(15 23 42 / 0.08)', }, colors: { red: { 500: '#ef4444', }, }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], sm: ['0.875rem', { lineHeight: '1.5rem' }], base: ['1rem', { lineHeight: '2rem' }], }, extend: { colors: { red: { 600: '#dc2626', }, }, fontFamily: { sans: 'Inter, system-ui, sans-serif', display: ['Cabinet Grotesk', ...defaultTheme.fontFamily.sans], }, borderRadius: { '4xl': '2rem', }, }, }, plugins: [], } satisfies Config ``` As you can see, this file only has a `darkMode` selector, custom `content` globs, a `theme` (with some theme keys being overwriting the default theme and some others extending the defaults). Note that it does not support `plugins` and/or `presets` yet. In the case above, we will find the CSS file containing the existing `@tailwind` directives and are migrating it to the following: ```css @import 'tailwindcss'; @source './**/*.{html,js}'; @variant dark (&:where(.dark, .dark *)); @theme { --box-shadow-*: initial; --box-shadow-sm: 0 2px 6px rgb(15 23 42 / 0.08); --color-*: initial; --color-red-500: #ef4444; --font-size-*: initial; --font-size-xs: 0.75rem; --font-size-xs--line-height: 1rem; --font-size-sm: 0.875rem; --font-size-sm--line-height: 1.5rem; --font-size-base: 1rem; --font-size-base--line-height: 2rem; --color-red-600: #dc2626; --font-family-sans: Inter, system-ui, sans-serif; --font-family-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --border-radius-4xl: 2rem; } ``` This replicates all features of the JS config so we can even delete the existing JS config in this case. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-10-11 15:27:53 +02:00
let files = flags._.map((file) => path.resolve(base, file))
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
// Discover CSS files in case no files were provided
if (files.length === 0) {
info(
'No input stylesheets provided. Searching for CSS files in the current directory and its subdirectories…',
)
files = await globby(['**/*.css'], {
absolute: true,
gitignore: true,
})
}
// Ensure we are only dealing with CSS files
files = files.filter((file) => file.endsWith('.css'))
// Analyze the stylesheets
let loadResults = await Promise.allSettled(files.map((filepath) => Stylesheet.load(filepath)))
// Load and parse all stylesheets
for (let result of loadResults) {
if (result.status === 'rejected') {
error(`${result.reason}`)
}
}
let stylesheets = loadResults
.filter((result) => result.status === 'fulfilled')
.map((result) => result.value)
// Analyze the stylesheets
try {
await analyzeStylesheets(stylesheets)
} catch (e: unknown) {
error(`${e}`)
}
// Ensure stylesheets are linked to configs
try {
await linkConfigsToStylesheets(stylesheets, {
configPath: flags['--config'],
base,
})
} catch (e: unknown) {
error(`${e}`)
}
// Migrate js config files, linked to stylesheets
info('Migrating JavaScript configuration files using the provided configuration file.')
let configBySheet = new Map<Stylesheet, Awaited<ReturnType<typeof prepareConfig>>>()
let jsConfigMigrationBySheet = new Map<
Stylesheet,
Awaited<ReturnType<typeof migrateJsConfig>>
>()
for (let sheet of stylesheets) {
if (!sheet.isTailwindRoot) continue
let config = await prepareConfig(sheet.linkedConfigPath, { base })
configBySheet.set(sheet, config)
let jsConfigMigration = await migrateJsConfig(
config.designSystem,
config.configFilePath,
base,
)
jsConfigMigrationBySheet.set(sheet, jsConfigMigration)
if (jsConfigMigration !== null) {
// Remove the JS config if it was fully migrated
cleanup.push(() => fs.rm(config.configFilePath))
}
}
// Migrate source files, linked to config files
{
// Template migrations
info('Migrating templates using the provided configuration file.')
for (let config of configBySheet.values()) {
let set = new Set<string>()
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 13:22:25 +01:00
for (let globEntry of config.globs.flatMap((entry) => hoistStaticGlobParts(entry))) {
let files = await globby([globEntry.pattern], {
absolute: true,
gitignore: true,
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 13:22:25 +01:00
cwd: globEntry.base,
})
for (let file of files) {
set.add(file)
}
}
let files = Array.from(set)
files.sort()
// Migrate each file
await Promise.allSettled(
files.map((file) => migrateTemplate(config.designSystem, config.userConfig, file)),
)
}
success('Template migration complete.')
}
// Migrate each CSS file
let migrateResults = await Promise.allSettled(
stylesheets.map((sheet) => {
let config = configBySheet.get(sheet)!
let jsConfigMigration = jsConfigMigrationBySheet.get(sheet)!
if (!config) {
for (let parent of sheet.ancestors()) {
if (parent.isTailwindRoot) {
config ??= configBySheet.get(parent)!
jsConfigMigration ??= jsConfigMigrationBySheet.get(parent)!
break
}
}
}
return migrateStylesheet(sheet, { ...config, jsConfigMigration })
}),
)
for (let result of migrateResults) {
if (result.status === 'rejected') {
error(`${result.reason}`)
}
}
// Split up stylesheets (as needed)
try {
await splitStylesheets(stylesheets)
} catch (e: unknown) {
error(`${e}`)
}
// Cleanup `@import "…" layer(utilities)`
for (let sheet of stylesheets) {
for (let importRule of sheet.importRules) {
if (!importRule.raws.tailwind_injected_layer) continue
let importedSheet = stylesheets.find(
(sheet) => sheet.id === importRule.raws.tailwind_destination_sheet_id,
)
if (!importedSheet) continue
// Only remove the `layer(…)` next to the import if any of the children
// contain `@utility`. Otherwise `@utility` will not be top-level.
if (
!importedSheet.containsRule((node) => node.type === 'atrule' && node.name === 'utility')
) {
continue
}
// Make sure to remove the `layer(…)` from the `@import` at-rule
importRule.params = importRule.params.replace(/ layer\([^)]+\)/, '').trim()
}
}
// Format nodes
for (let sheet of stylesheets) {
await postcss([sortBuckets(), formatNodes()]).process(sheet.root!, { from: sheet.file! })
}
// Write all files to disk
for (let sheet of stylesheets) {
if (!sheet.file) continue
await fs.writeFile(sheet.file, sheet.root.toString())
}
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
success('Stylesheet migration complete.')
}
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
{
// PostCSS config migration
Add simple JS config migration (#14639) This PR implements the first version of JS config file migration to CSS. It is based on the most simple config setups we are using in the Tailwind UI templates Commit, Primer, Radiant, and Studio. The example we use in the integration test is a config that looks like this: ```js import { type Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' module.exports = { darkMode: 'selector', content: ['./src/**/*.{html,js}'], theme: { boxShadow: { sm: '0 2px 6px rgb(15 23 42 / 0.08)', }, colors: { red: { 500: '#ef4444', }, }, fontSize: { xs: ['0.75rem', { lineHeight: '1rem' }], sm: ['0.875rem', { lineHeight: '1.5rem' }], base: ['1rem', { lineHeight: '2rem' }], }, extend: { colors: { red: { 600: '#dc2626', }, }, fontFamily: { sans: 'Inter, system-ui, sans-serif', display: ['Cabinet Grotesk', ...defaultTheme.fontFamily.sans], }, borderRadius: { '4xl': '2rem', }, }, }, plugins: [], } satisfies Config ``` As you can see, this file only has a `darkMode` selector, custom `content` globs, a `theme` (with some theme keys being overwriting the default theme and some others extending the defaults). Note that it does not support `plugins` and/or `presets` yet. In the case above, we will find the CSS file containing the existing `@tailwind` directives and are migrating it to the following: ```css @import 'tailwindcss'; @source './**/*.{html,js}'; @variant dark (&:where(.dark, .dark *)); @theme { --box-shadow-*: initial; --box-shadow-sm: 0 2px 6px rgb(15 23 42 / 0.08); --color-*: initial; --color-red-500: #ef4444; --font-size-*: initial; --font-size-xs: 0.75rem; --font-size-xs--line-height: 1rem; --font-size-sm: 0.875rem; --font-size-sm--line-height: 1.5rem; --font-size-base: 1rem; --font-size-base--line-height: 2rem; --color-red-600: #dc2626; --font-family-sans: Inter, system-ui, sans-serif; --font-family-display: Cabinet Grotesk, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --border-radius-4xl: 2rem; } ``` This replicates all features of the JS config so we can even delete the existing JS config in this case. --------- Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2024-10-11 15:27:53 +02:00
await migratePostCSSConfig(base)
}
{
// Migrate the prettier plugin to the latest version
await migratePrettierPlugin(base)
}
// Run all cleanup functions because we completed the migration
await Promise.allSettled(cleanup.map((fn) => fn()))
try {
// Upgrade Tailwind CSS
await pkg(base).add(['tailwindcss@next'])
} catch {}
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
// Figure out if we made any changes
if (isRepoDirty()) {
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
success('Verify the changes and commit them to your repository.')
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
} else {
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
success('No changes were made to your repository.')
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
}
}
run()
.then(() => process.exit(0))
.catch((err) => {
console.error(err)
process.exit(1)
})