-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CSS codemod for missing
@layer
(#14504)
This PR adds a codemod that ensures that some parts of your stylesheet are wrapped in an `@layer`. This is a follow-up PR of #14411, in that PR we migrate `@tailwind` directives to imports. As a quick summary, that will turn this: ```css @tailwind base; @tailwind components; @tailwind utilities; ``` Into: ```css @import 'tailwindcss'; ``` But there are a few issues with that _if_ we have additional CSS on the page. For example let's imagine we had this: ```css @tailwind base; body { background-color: red; } @tailwind components; .btn {} @tailwind utilities; ``` This will now be turned into: ```css @import 'tailwindcss'; body { background-color: red; } .btn {} ``` But in v4 we use real layers, in v3 we used to replace the directive with the result of that layer. This means that now the `body` and `.btn` styles are in the incorrect spot. To solve this, we have to wrap them in a layer. The `body` should go in an `@layer base`, and the `.btn` should be in an `@layer components` to make sure it's in the same spot as it was before. That's what this PR does, the original input will now be turned into: ```css @import 'tailwindcss'; @layer base { body { background-color: red; } } @layer components { .btn { } } ``` There are a few internal refactors going on as well, but those are less important.
- Loading branch information
1 parent
d14249d
commit d869442
Showing
14 changed files
with
423 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
packages/@tailwindcss-upgrade/src/codemods/format-nodes.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import postcss, { type Plugin } from 'postcss' | ||
import { expect, it } from 'vitest' | ||
import { formatNodes } from './format-nodes' | ||
|
||
function markPretty(): Plugin { | ||
return { | ||
postcssPlugin: '@tailwindcss/upgrade/mark-pretty', | ||
OnceExit(root) { | ||
root.walkAtRules('utility', (atRule) => { | ||
atRule.raws.tailwind_pretty = true | ||
}) | ||
}, | ||
} | ||
} | ||
|
||
function migrate(input: string) { | ||
return postcss() | ||
.use(markPretty()) | ||
.use(formatNodes()) | ||
.process(input, { from: expect.getState().testPath }) | ||
.then((result) => result.css) | ||
} | ||
|
||
it('should format PostCSS nodes that are marked with tailwind_pretty', async () => { | ||
expect( | ||
await migrate(` | ||
@utility .foo { .foo { color: red; } }`), | ||
).toMatchInlineSnapshot(` | ||
"@utility .foo { | ||
.foo { | ||
color: red; | ||
} | ||
}" | ||
`) | ||
}) |
30 changes: 30 additions & 0 deletions
30
packages/@tailwindcss-upgrade/src/codemods/format-nodes.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { parse, type ChildNode, type Plugin, type Root } from 'postcss' | ||
import { format } from 'prettier' | ||
import { walk, WalkAction } from '../utils/walk' | ||
|
||
// Prettier is used to generate cleaner output, but it's only used on the nodes | ||
// that were marked as `pretty` during the migration. | ||
export function formatNodes(): Plugin { | ||
async function migrate(root: Root) { | ||
// Find the nodes to format | ||
let nodesToFormat: ChildNode[] = [] | ||
walk(root, (child) => { | ||
if (child.raws.tailwind_pretty) { | ||
nodesToFormat.push(child) | ||
return WalkAction.Skip | ||
} | ||
}) | ||
|
||
// Format the nodes | ||
await Promise.all( | ||
nodesToFormat.map(async (node) => { | ||
node.replaceWith(parse(await format(node.toString(), { parser: 'css', semi: true }))) | ||
}), | ||
) | ||
} | ||
|
||
return { | ||
postcssPlugin: '@tailwindcss/upgrade/format-nodes', | ||
OnceExit: migrate, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
packages/@tailwindcss-upgrade/src/codemods/migrate-at-layer-utilities.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
packages/@tailwindcss-upgrade/src/codemods/migrate-missing-layers.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import dedent from 'dedent' | ||
import postcss from 'postcss' | ||
import { expect, it } from 'vitest' | ||
import { formatNodes } from './format-nodes' | ||
import { migrateMissingLayers } from './migrate-missing-layers' | ||
|
||
const css = dedent | ||
|
||
function migrate(input: string) { | ||
return postcss() | ||
.use(migrateMissingLayers()) | ||
.use(formatNodes()) | ||
.process(input, { from: expect.getState().testPath }) | ||
.then((result) => result.css) | ||
} | ||
|
||
it('should migrate rules between tailwind directives', async () => { | ||
expect( | ||
await migrate(css` | ||
@tailwind base; | ||
.base { | ||
} | ||
@tailwind components; | ||
.component-a { | ||
} | ||
.component-b { | ||
} | ||
@tailwind utilities; | ||
.utility-a { | ||
} | ||
.utility-b { | ||
} | ||
`), | ||
).toMatchInlineSnapshot(` | ||
"@tailwind base; | ||
@layer base { | ||
.base { | ||
} | ||
} | ||
@tailwind components; | ||
@layer components { | ||
.component-a { | ||
} | ||
.component-b { | ||
} | ||
} | ||
@tailwind utilities; | ||
@layer utilities { | ||
.utility-a { | ||
} | ||
.utility-b { | ||
} | ||
}" | ||
`) | ||
}) |
Oops, something went wrong.