Skip to content

Commit 9ac66c2

Browse files
committed
Place @utility rules into separate stylesheets as needed
Now that we convert layered, imported rules into `@utility` where needed we need to hoist these rules outside of their imported layer since `@utility` MUST be root-level.
1 parent 3c09255 commit 9ac66c2

File tree

4 files changed

+503
-2
lines changed

4 files changed

+503
-2
lines changed

integrations/upgrade/index.test.ts

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,3 +344,234 @@ test(
344344
`)
345345
},
346346
)
347+
348+
test(
349+
'migrate utilities in an imported file',
350+
{
351+
fs: {
352+
'package.json': json`
353+
{
354+
"dependencies": {
355+
"tailwindcss": "workspace:^",
356+
"@tailwindcss/upgrade": "workspace:^"
357+
}
358+
}
359+
`,
360+
'tailwind.config.js': js`module.exports = {}`,
361+
'src/index.css': css`
362+
@import 'tailwindcss';
363+
@import './utilities.css' layer(utilities);
364+
`,
365+
'src/utilities.css': css`
366+
.no-scrollbar::-webkit-scrollbar {
367+
display: none;
368+
}
369+
370+
.no-scrollbar {
371+
-ms-overflow-style: none;
372+
scrollbar-width: none;
373+
}
374+
`,
375+
},
376+
},
377+
async ({ fs, exec }) => {
378+
await exec('npx @tailwindcss/upgrade --force')
379+
380+
expect(await fs.dumpFiles('./src/**/*.css')).toMatchInlineSnapshot(`
381+
"
382+
--- ./src/index.css ---
383+
@import 'tailwindcss';
384+
@import './utilities.css';
385+
386+
--- ./src/utilities.css ---
387+
@utility no-scrollbar {
388+
&::-webkit-scrollbar {
389+
display: none;
390+
}
391+
-ms-overflow-style: none;
392+
scrollbar-width: none;
393+
}"
394+
`)
395+
},
396+
)
397+
398+
test(
399+
'migrate utilities in deep import trees',
400+
{
401+
fs: {
402+
'package.json': json`
403+
{
404+
"dependencies": {
405+
"tailwindcss": "workspace:^",
406+
"@tailwindcss/cli": "workspace:^",
407+
"@tailwindcss/upgrade": "workspace:^"
408+
}
409+
}
410+
`,
411+
'tailwind.config.js': js`module.exports = {}`,
412+
'src/index.html': html`
413+
<div class="hover:thing"></div>
414+
`,
415+
'src/index.css': css`
416+
@import 'tailwindcss/utilities';
417+
@import './a.1.css' layer(utilities);
418+
@import './b.1.css' layer(components);
419+
@import './c.1.css';
420+
@import './d.1.css';
421+
`,
422+
'src/a.1.css': css`
423+
@import './a.1.utilities.css';
424+
425+
.foo-from-a {
426+
color: red;
427+
}
428+
`,
429+
'src/a.1.utilities.css': css`
430+
#foo {
431+
--keep: me;
432+
}
433+
434+
.foo-from-import {
435+
color: blue;
436+
}
437+
`,
438+
'src/b.1.css': css`
439+
@import './b.1.components.css';
440+
441+
.bar-from-b {
442+
color: red;
443+
}
444+
`,
445+
'src/b.1.components.css': css`
446+
.bar-from-import {
447+
color: blue;
448+
}
449+
`,
450+
'src/c.1.css': css`
451+
@import './c.2.css' layer(utilities);
452+
.baz-from-c {
453+
color: green;
454+
}
455+
`,
456+
'src/c.2.css': css`
457+
@import './c.3.css';
458+
#baz {
459+
--keep: me;
460+
}
461+
.baz-from-import {
462+
color: yellow;
463+
}
464+
`,
465+
'src/c.3.css': css`
466+
#baz {
467+
--keep: me;
468+
}
469+
.baz-from-import {
470+
color: yellow;
471+
}
472+
`,
473+
474+
// This is a super deep import chain
475+
// And no `*.utilities.css` files should be created for these
476+
// because there are no rules that need to be separated
477+
'src/d.1.css': css`@import './d.2.css' layer(utilities);`,
478+
'src/d.2.css': css`@import './d.3.css';`,
479+
'src/d.3.css': css`@import './d.4.css';`,
480+
'src/d.4.css': css`
481+
.from-a-4 {
482+
color: blue;
483+
}
484+
`,
485+
},
486+
},
487+
async ({ fs, exec }) => {
488+
await exec('npx @tailwindcss/upgrade --force')
489+
490+
expect(await fs.dumpFiles('./src/**/*.css')).toMatchInlineSnapshot(`
491+
"
492+
--- ./src/index.css ---
493+
@import 'tailwindcss/utilities' layer(utilities);
494+
@import './a.1.css' layer(utilities);
495+
@import './a.1.utilities.1.css';
496+
@import './b.1.css';
497+
@import './c.1.css' layer(utilities);
498+
@import './c.1.utilities.css';
499+
@import './d.1.css';
500+
501+
--- ./src/a.1.css ---
502+
@import './a.1.utilities.css'
503+
504+
--- ./src/a.1.utilities.1.css ---
505+
@import './a.1.utilities.utilities.css';
506+
@utility foo-from-a {
507+
color: red;
508+
}
509+
510+
--- ./src/a.1.utilities.css ---
511+
#foo {
512+
--keep: me;
513+
}
514+
515+
--- ./src/a.1.utilities.utilities.css ---
516+
@utility foo-from-import {
517+
color: blue;
518+
}
519+
520+
--- ./src/b.1.components.css ---
521+
@utility bar-from-import {
522+
color: blue;
523+
}
524+
525+
--- ./src/b.1.css ---
526+
@import './b.1.components.css';
527+
@utility bar-from-b {
528+
color: red;
529+
}
530+
531+
--- ./src/c.1.css ---
532+
@import './c.2.css' layer(utilities);
533+
.baz-from-c {
534+
color: green;
535+
}
536+
537+
--- ./src/c.1.utilities.css ---
538+
@import './c.2.utilities.css'
539+
540+
--- ./src/c.2.css ---
541+
@import './c.3.css';
542+
#baz {
543+
--keep: me;
544+
}
545+
546+
--- ./src/c.2.utilities.css ---
547+
@import './c.3.utilities.css';
548+
@utility baz-from-import {
549+
color: yellow;
550+
}
551+
552+
--- ./src/c.3.css ---
553+
#baz {
554+
--keep: me;
555+
}
556+
557+
--- ./src/c.3.utilities.css ---
558+
@utility baz-from-import {
559+
color: yellow;
560+
}
561+
562+
--- ./src/d.1.css ---
563+
@import './d.2.css'
564+
565+
--- ./src/d.2.css ---
566+
@import './d.3.css'
567+
568+
--- ./src/d.3.css ---
569+
@import './d.4.css'
570+
571+
--- ./src/d.4.css ---
572+
@utility from-a-4 {
573+
color: blue;
574+
}"
575+
`)
576+
},
577+
)

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ import path from 'node:path'
66
import postcss from 'postcss'
77
import { formatNodes } from './codemods/format-nodes'
88
import { help } from './commands/help'
9-
import { analyze as analyzeStylesheets, migrate as migrateStylesheet } from './migrate'
9+
import {
10+
analyze as analyzeStylesheets,
11+
migrate as migrateStylesheet,
12+
split as splitStylesheets,
13+
} from './migrate'
1014
import { Stylesheet } from './stylesheet'
1115
import { migrate as migrateTemplate } from './template/migrate'
1216
import { prepareConfig } from './template/prepare-config'
@@ -128,6 +132,13 @@ async function run() {
128132
}
129133
}
130134

135+
// Split up stylesheets (as needed)
136+
try {
137+
await splitStylesheets(stylesheets)
138+
} catch (e: unknown) {
139+
error(`${e}`)
140+
}
141+
131142
// Format nodes
132143
for (let sheet of stylesheets) {
133144
await postcss([formatNodes()]).process(sheet.root!, { from: sheet.file! })

0 commit comments

Comments
 (0)