Skip to content

Commit 1ec566f

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 6e9b2d7 commit 1ec566f

File tree

4 files changed

+467
-2
lines changed

4 files changed

+467
-2
lines changed

integrations/upgrade/index.test.ts

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,229 @@ test(
259259
`)
260260
},
261261
)
262+
263+
test(
264+
'migrate utilities in an imported file',
265+
{
266+
fs: {
267+
'package.json': json`
268+
{
269+
"dependencies": {
270+
"tailwindcss": "workspace:^",
271+
"@tailwindcss/upgrade": "workspace:^"
272+
}
273+
}
274+
`,
275+
'tailwind.config.js': js`module.exports = {}`,
276+
'src/index.css': css`
277+
@import 'tailwindcss';
278+
@import './utilities.css' layer(utilities);
279+
`,
280+
'src/utilities.css': css`
281+
.no-scrollbar::-webkit-scrollbar {
282+
display: none;
283+
}
284+
285+
.no-scrollbar {
286+
-ms-overflow-style: none;
287+
scrollbar-width: none;
288+
}
289+
`,
290+
},
291+
},
292+
async ({ fs, exec }) => {
293+
await exec('npx @tailwindcss/upgrade --force')
294+
295+
expect(await fs.dumpFiles('./src/**/*.css')).toMatchInlineSnapshot(`
296+
"
297+
--- ./src/index.css ---
298+
@import 'tailwindcss';
299+
@import './utilities.css';
300+
301+
--- ./src/utilities.css ---
302+
@utility no-scrollbar {
303+
&::-webkit-scrollbar {
304+
display: none;
305+
}
306+
-ms-overflow-style: none;
307+
scrollbar-width: none;
308+
}"
309+
`)
310+
},
311+
)
312+
313+
test(
314+
'migrate utilities in deep import trees',
315+
{
316+
fs: {
317+
'package.json': json`
318+
{
319+
"dependencies": {
320+
"tailwindcss": "workspace:^",
321+
"@tailwindcss/cli": "workspace:^",
322+
"@tailwindcss/upgrade": "workspace:^"
323+
}
324+
}
325+
`,
326+
'tailwind.config.js': js`module.exports = {}`,
327+
'src/index.html': html`
328+
<div class="hover:thing"></div>
329+
`,
330+
'src/index.css': css`
331+
@import 'tailwindcss/utilities';
332+
@import './a.1.css' layer(utilities);
333+
@import './b.1.css' layer(components);
334+
@import './c.1.css';
335+
@import './d.1.css';
336+
`,
337+
'src/a.1.css': css`
338+
@import './a.1.utilities.css';
339+
340+
.foo-from-a {
341+
color: red;
342+
}
343+
`,
344+
'src/a.1.utilities.css': css`
345+
#foo {
346+
--keep: me;
347+
}
348+
349+
.foo-from-import {
350+
color: blue;
351+
}
352+
`,
353+
'src/b.1.css': css`
354+
@import './b.1.components.css';
355+
356+
.bar-from-b {
357+
color: red;
358+
}
359+
`,
360+
'src/b.1.components.css': css`
361+
.bar-from-import {
362+
color: blue;
363+
}
364+
`,
365+
'src/c.1.css': css`
366+
@import './c.2.css' layer(utilities);
367+
.baz-from-c {
368+
color: green;
369+
}
370+
`,
371+
'src/c.2.css': css`
372+
@import './c.3.css';
373+
#baz {
374+
--keep: me;
375+
}
376+
.baz-from-import {
377+
color: yellow;
378+
}
379+
`,
380+
'src/c.3.css': css`
381+
#baz {
382+
--keep: me;
383+
}
384+
.baz-from-import {
385+
color: yellow;
386+
}
387+
`,
388+
389+
// This is a super deep import chain
390+
// And no `*.utilities.css` files should be created for these
391+
// because there are no rules that need to be separated
392+
'src/d.1.css': css`@import './d.2.css' layer(utilities);`,
393+
'src/d.2.css': css`@import './d.3.css';`,
394+
'src/d.3.css': css`@import './d.4.css';`,
395+
'src/d.4.css': css`
396+
.from-a-4 {
397+
color: blue;
398+
}
399+
`,
400+
},
401+
},
402+
async ({ fs, exec }) => {
403+
await exec('npx @tailwindcss/upgrade --force')
404+
405+
expect(await fs.dumpFiles('./src/**/*.css')).toMatchInlineSnapshot(`
406+
"
407+
--- ./src/index.css ---
408+
@import 'tailwindcss/utilities' layer(utilities);
409+
@import './a.1.css' layer(utilities);
410+
@import './a.1.utilities.css';
411+
@import './b.1.css';
412+
@import './c.1.css' layer(utilities);
413+
@import './c.1.utilities.css';
414+
@import './d.1.css';
415+
416+
--- ./src/a.1.css ---
417+
@import './a.1.utilities.css'
418+
419+
--- ./src/a.1.utilities.css ---
420+
@import './a.1.utilities.utilities.css';
421+
@utility foo-from-a {
422+
color: red;
423+
}
424+
425+
--- ./src/a.1.utilities.utilities.css ---
426+
@utility foo-from-import {
427+
color: blue;
428+
}
429+
430+
--- ./src/b.1.components.css ---
431+
@utility bar-from-import {
432+
color: blue;
433+
}
434+
435+
--- ./src/b.1.css ---
436+
@import './b.1.components.css';
437+
@utility bar-from-b {
438+
color: red;
439+
}
440+
441+
--- ./src/c.1.css ---
442+
@import './c.2.css' layer(utilities);
443+
.baz-from-c {
444+
color: green;
445+
}
446+
447+
--- ./src/c.1.utilities.css ---
448+
@import './c.2.utilities.css'
449+
450+
--- ./src/c.2.css ---
451+
@import './c.3.css';
452+
#baz {
453+
--keep: me;
454+
}
455+
456+
--- ./src/c.2.utilities.css ---
457+
@import './c.3.utilities.css';
458+
@utility baz-from-import {
459+
color: yellow;
460+
}
461+
462+
--- ./src/c.3.css ---
463+
#baz {
464+
--keep: me;
465+
}
466+
467+
--- ./src/c.3.utilities.css ---
468+
@utility baz-from-import {
469+
color: yellow;
470+
}
471+
472+
--- ./src/d.1.css ---
473+
@import './d.2.css'
474+
475+
--- ./src/d.2.css ---
476+
@import './d.3.css'
477+
478+
--- ./src/d.3.css ---
479+
@import './d.4.css'
480+
481+
--- ./src/d.4.css ---
482+
@utility from-a-4 {
483+
color: blue;
484+
}"
485+
`)
486+
},
487+
)

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)