Skip to content

Commit

Permalink
ensure layer(…) on @import is correct
Browse files Browse the repository at this point in the history
  • Loading branch information
RobinMalfait committed Oct 24, 2024
1 parent 5a1c2e7 commit 5ce4bd5
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 37 deletions.
86 changes: 85 additions & 1 deletion integrations/upgrade/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ test(
@import './a.1.css' layer(utilities);
@import './a.1.utilities.1.css';
@import './b.1.css';
@import './c.1.css';
@import './c.1.css' layer(utilities);
@import './c.1.utilities.css';
@import './d.1.css';
Expand Down Expand Up @@ -1545,3 +1545,87 @@ test(
`)
},
)

test(
'that it attaches the correct layers to the imported files',
{
fs: {
'package.json': json`
{
"dependencies": {
"tailwindcss": "workspace:^",
"@tailwindcss/upgrade": "workspace:^"
}
}
`,
'tailwind.config.js': js`module.exports = {}`,
'src/index.css': css`
@import 'tailwindcss/utilities';
/* No layer expected */
@import './my-components.css';
/* No layer expected */
@import './my-utilities.css';
/* Expecting a layer */
@import './my-other.css';
@import 'tailwindcss/components';
`,
'src/my-components.css': css`
@layer components {
.foo {
color: red;
}
}
`,
'src/my-utilities.css': css`
@layer utilities {
.css {
color: red;
}
}
`,
'src/my-other.css': css`
/* All my fonts! */
@font-face {
}
`,
},
},
async ({ fs, exec }) => {
await exec('npx @tailwindcss/upgrade --force')

expect(await fs.dumpFiles('./src/**/*.css')).toMatchInlineSnapshot(`
"
--- ./src/index.css ---
@import 'tailwindcss/utilities' layer(utilities);
/* No layer expected */
@import './my-components.css';
/* No layer expected */
@import './my-utilities.css';
/* Expecting a layer */
@import './my-other.css' layer(utilities);
--- ./src/my-components.css ---
@utility foo {
color: red;
}
--- ./src/my-other.css ---
/* All my fonts! */
@font-face {
}
--- ./src/my-utilities.css ---
@utility css {
color: red;
}
"
`)
},
)
49 changes: 15 additions & 34 deletions packages/@tailwindcss-upgrade/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,42 +151,23 @@ async function run() {

// Cleanup `@import "…" layer(utilities)`
for (let sheet of stylesheets) {
// If the `@import` contains an injected `layer(…)` we need to remove it
if (!Array.from(sheet.importRules).some((node) => node.raws.tailwind_injected_layer)) {
continue
}

let hasAtUtility = false

// Only remove the `layer(…)` next to the import, if any of the children
// contains an `@utility`. Otherwise the `@utility` will not be top-level.
{
sheet.root.walkAtRules('utility', () => {
hasAtUtility = true
return false
})

if (!hasAtUtility) {
for (let child of sheet.descendants()) {
child.root.walkAtRules('utility', () => {
hasAtUtility = true
return false
})

if (hasAtUtility) {
break
}
}
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
// contains an `@utility`. Otherwise the `@utility` will not be top-level.
if (
!importedSheet.containsRule((node) => node.type === 'atrule' && node.name === 'utility')
) {
continue
}
}

// No `@utility` found, we can keep the `layer(…)` next to the import
if (!hasAtUtility) continue

for (let importNode of sheet.importRules) {
if (importNode.raws.tailwind_injected_layer) {
importNode.params = importNode.params.replace(/ layer\([^)]+\)/, '').trim()
}
// Make sure to remove the `layer(…)` from the `@import` at-rule
importRule.params = importRule.params.replace(/ layer\([^)]+\)/, '').trim()
}
}

Expand Down
27 changes: 25 additions & 2 deletions packages/@tailwindcss-upgrade/src/stylesheet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,36 @@ export class Stylesheet {
return { convertiblePaths, nonConvertiblePaths }
}

containsRule(cb: (rule: postcss.AnyNode) => boolean) {
let contains = false

this.root.walk((rule) => {
if (cb(rule)) {
contains = true
return false
}
})

if (contains) {
return true
}

for (let child of this.children) {
if (child.item.containsRule(cb)) {
return true
}
}

return false
}

[util.inspect.custom]() {
return {
...this,
root: this.root.toString(),
layers: Array.from(this.layers()),
parents: Array.from(this.parents, (s) => s.item.id),
children: Array.from(this.children, (s) => s.item.id),
parents: Array.from(this.parents, (s) => s.item),
children: Array.from(this.children, (s) => s.item),
parentsMeta: Array.from(this.parents, (s) => s.meta),
childrenMeta: Array.from(this.children, (s) => s.meta),
}
Expand Down

0 comments on commit 5ce4bd5

Please sign in to comment.