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

35 lines
1.3 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
import fs from 'node:fs/promises'
import path from 'node:path'
import postcss from 'postcss'
import type { Config } from 'tailwindcss'
import type { DesignSystem } from '../../tailwindcss/src/design-system'
import { formatNodes } from './codemods/format-nodes'
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 { migrateAtApply } from './codemods/migrate-at-apply'
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
import { migrateAtLayerUtilities } from './codemods/migrate-at-layer-utilities'
import { migrateMissingLayers } from './codemods/migrate-missing-layers'
Add CSS codemods for migrating `@tailwind` directives (#14411) This PR adds CSS codemods for migrating existing `@tailwind` directives to the new alternatives. This PR has the ability to migrate the following cases: --- Typical default usage of `@tailwind` directives in v3. Input: ```css @tailwind base; @tailwind components; @tailwind utilities; ``` Output: ```css @import 'tailwindcss'; ``` --- Similar as above, but always using `@import` instead of `@import` directly. Input: ```css @import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; ``` Output: ```css @import 'tailwindcss'; ``` --- When you are _only_ using `@tailwind base`: Input: ```css @tailwind base; ``` Output: ```css @import 'tailwindcss/theme' layer(theme); @import 'tailwindcss/preflight' layer(base); ``` --- When you are _only_ using `@tailwind utilities`: Input: ```css @tailwind utilities; ``` Output: ```css @import 'tailwindcss/utilities' layer(utilities); ``` --- If the default order changes (aka, `@tailwind utilities` was defined _before_ `@tailwind base`), then an additional `@layer` will be added to the top to re-define the default order. Input: ```css @tailwind utilities; @tailwind base; ``` Output: ```css @layer theme, components, utilities, base; @import 'tailwindcss'; ``` --- When you are _only_ using `@tailwind base; @tailwind utilities;`: Input: ```css @tailwind base; @tailwind utilities; ``` Output: ```css @import 'tailwindcss'; ``` We currently don't have a concept of `@tailwind components` in v4, so if you are not using `@tailwind components`, we can expand to the default `@import 'tailwindcss';` instead of the individual imports. --- `@tailwind screens` and `@tailwind variants` are not supported/necessary in v4, so we can safely remove them. Input: ```css @tailwind screens; @tailwind variants; ``` Output: ```css ```
2024-09-18 22:40:23 +02:00
import { migrateTailwindDirectives } from './codemods/migrate-tailwind-directives'
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
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
export interface MigrateOptions {
newPrefix: string | null
designSystem: DesignSystem
userConfig: Config
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
}
export async function migrateContents(contents: string, options: MigrateOptions, file?: string) {
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
return postcss()
.use(migrateAtApply(options))
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
.use(migrateAtLayerUtilities())
.use(migrateMissingLayers())
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
.use(migrateTailwindDirectives(options))
.use(formatNodes())
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
.process(contents, { from: file })
.then((result) => result.css)
}
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
export async function migrate(file: string, options: MigrateOptions) {
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
let fullPath = path.resolve(process.cwd(), file)
let contents = await fs.readFile(fullPath, 'utf-8')
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
await fs.writeFile(fullPath, await migrateContents(contents, options, fullPath))
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
}