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!; } ```
74 lines
2 KiB
JavaScript
74 lines
2 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { globby } from 'globby'
|
|
import path from 'node:path'
|
|
import { help } from './commands/help'
|
|
import { migrate } from './migrate'
|
|
import { args, type Arg } from './utils/args'
|
|
import { isRepoDirty } from './utils/git'
|
|
import { eprintln, error, header, highlight, info, success } from './utils/renderer'
|
|
|
|
const options = {
|
|
'--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() {
|
|
eprintln(header())
|
|
eprintln()
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
// Use provided files
|
|
let files = flags._.map((file) => path.resolve(process.cwd(), file))
|
|
|
|
// Discover CSS files in case no files were provided
|
|
if (files.length === 0) {
|
|
info(
|
|
'No files 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'))
|
|
|
|
// Migrate each file
|
|
await Promise.allSettled(files.map((file) => migrate(file)))
|
|
|
|
// Figure out if we made any changes
|
|
if (isRepoDirty()) {
|
|
success('Migration complete. Verify the changes and commit them to your repository.')
|
|
} else {
|
|
success('Migration complete. No changes were made to your repository.')
|
|
}
|
|
}
|
|
|
|
run()
|
|
.then(() => process.exit(0))
|
|
.catch((err) => {
|
|
console.error(err)
|
|
process.exit(1)
|
|
})
|