Skip to content

Commit 088e23f

Browse files
RobinMalfaitthecrypticace
authored andcommitted
Ensure @config "…" is added when tailwind.config.js is detected
1 parent 15bea44 commit 088e23f

File tree

4 files changed

+147
-3
lines changed

4 files changed

+147
-3
lines changed

integrations/upgrade/index.test.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ test(
3939
<div class="flex! sm:block! bg-linear-to-t bg-[var(--my-red)]"></div>
4040
4141
--- ./src/input.css ---
42-
@import 'tailwindcss';"
42+
@import 'tailwindcss';
43+
@config "../tailwind.config.js";"
4344
`)
4445
},
4546
)
@@ -89,6 +90,8 @@ test(
8990
--- ./src/input.css ---
9091
@import 'tailwindcss' prefix(tw);
9192
93+
@config "../tailwind.config.js";
94+
9295
.btn {
9396
@apply tw:rounded-md! tw:px-2 tw:py-1 tw:bg-blue-500 tw:text-white;
9497
}"
@@ -134,6 +137,8 @@ test(
134137
--- ./src/index.css ---
135138
@import 'tailwindcss';
136139
140+
@config "../tailwind.config.js";
141+
137142
.a {
138143
@apply flex;
139144
}
@@ -187,6 +192,8 @@ test(
187192
--- ./src/index.css ---
188193
@import 'tailwindcss';
189194
195+
@config "../tailwind.config.js";
196+
190197
@layer base {
191198
html {
192199
color: #333;
@@ -245,6 +252,8 @@ test(
245252
--- ./src/index.css ---
246253
@import 'tailwindcss';
247254
255+
@config "../tailwind.config.js";
256+
248257
@utility btn {
249258
@apply rounded-md px-2 py-1 bg-blue-500 text-white;
250259
}
@@ -382,6 +391,7 @@ test(
382391
--- ./src/index.css ---
383392
@import 'tailwindcss';
384393
@import './utilities.css';
394+
@config "../tailwind.config.js";
385395
386396
--- ./src/utilities.css ---
387397
@utility no-scrollbar {
@@ -497,6 +507,7 @@ test(
497507
@import './c.1.css' layer(utilities);
498508
@import './c.1.utilities.css';
499509
@import './d.1.css';
510+
@config "../tailwind.config.js";
500511
501512
--- ./src/a.1.css ---
502513
@import './a.1.utilities.css'
@@ -629,14 +640,64 @@ test(
629640
--- ./src/root.1.css ---
630641
@import 'tailwindcss/utilities' layer(utilities);
631642
@import './a.1.css' layer(utilities);
643+
@config "../tailwind.config.js";
632644
633645
--- ./src/root.2.css ---
634646
@import 'tailwindcss/utilities' layer(utilities);
635647
@import './a.1.css' layer(components);
648+
@config "../tailwind.config.js";
636649
637650
--- ./src/root.3.css ---
638651
@import 'tailwindcss/utilities' layer(utilities);
639-
@import './a.1.css' layer(utilities);"
652+
@import './a.1.css' layer(utilities);
653+
@config "../tailwind.config.js";"
654+
`)
655+
},
656+
)
657+
658+
test(
659+
'injecting `@config` when a tailwind.config.{js,ts,…} is detected',
660+
{
661+
fs: {
662+
'package.json': json`
663+
{
664+
"dependencies": {
665+
"@tailwindcss/upgrade": "workspace:^"
666+
}
667+
}
668+
`,
669+
'tailwind.config.ts': js`
670+
export default {
671+
content: ['./src/**/*.{html,js}'],
672+
}
673+
`,
674+
'src/index.html': html`
675+
<h1>🤠👋</h1>
676+
<div class="!flex sm:!block bg-gradient-to-t bg-[--my-red]"></div>
677+
`,
678+
'src/input.css': css`
679+
@tailwind base;
680+
@tailwind components;
681+
@tailwind utilities;
682+
`,
683+
},
684+
},
685+
async ({ exec, fs }) => {
686+
await exec('npx @tailwindcss/upgrade --force')
687+
688+
await fs.expectFileToContain(
689+
'src/index.html',
690+
html`
691+
<h1>🤠👋</h1>
692+
<div class="flex! sm:block! bg-linear-to-t bg-[var(--my-red)]"></div>
693+
`,
694+
)
695+
696+
expect(await fs.dumpFiles('./src/**/*.css')).toMatchInlineSnapshot(`
697+
"
698+
--- ./src/input.css ---
699+
@import 'tailwindcss';
700+
@config "../tailwind.config.ts";"
640701
`)
641702
},
642703
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import path from 'node:path'
2+
import { AtRule, type Plugin, type Root } from 'postcss'
3+
import type { Stylesheet } from '../stylesheet'
4+
import { walk, WalkAction } from '../utils/walk'
5+
6+
export function migrateAtConfig(
7+
sheet: Stylesheet,
8+
{ configFilePath }: { configFilePath: string },
9+
): Plugin {
10+
function migrate(root: Root) {
11+
let hasConfig = false
12+
root.walkAtRules('config', () => {
13+
hasConfig = true
14+
return false
15+
})
16+
17+
// We already have a `@config`
18+
if (hasConfig) return
19+
20+
// We don't have a sheet with a file path
21+
// TODO: Why? Tests?
22+
if (!sheet.file) return
23+
24+
// Should this sheet have an `@config`?
25+
// 1. It should be a root CSS file
26+
if (sheet.parents.size > 0) return
27+
28+
// 2. It should include a `@import "tailwindcss"`
29+
let hasTailwindImport = false
30+
root.walkAtRules('import', (node) => {
31+
if (node.params.includes('tailwindcss')) {
32+
hasTailwindImport = true
33+
return false
34+
}
35+
})
36+
if (!hasTailwindImport) return
37+
38+
// Figure out the path to the config file
39+
let sheetPath = sheet.file
40+
let configPath = configFilePath
41+
42+
let relativePath = path.relative(path.dirname(sheetPath), configPath)
43+
if (relativePath[0] !== '.') {
44+
relativePath = `./${relativePath}`
45+
}
46+
47+
// Inject the `@config` in a sensible place
48+
let locationNode = null as AtRule | null
49+
50+
walk(root, (node) => {
51+
if (node.type === 'atrule' && (node.name === 'import' || node.name === 'theme')) {
52+
locationNode = node
53+
}
54+
55+
return WalkAction.Skip
56+
})
57+
58+
let configNode = new AtRule({ name: 'config', params: `"${relativePath}"` })
59+
60+
if (!locationNode) {
61+
root.prepend(configNode)
62+
} else if (locationNode.name === 'import') {
63+
locationNode.after(configNode)
64+
} else if (locationNode.name === 'theme') {
65+
locationNode.before(configNode)
66+
}
67+
}
68+
69+
return {
70+
postcssPlugin: '@tailwindcss/upgrade/migrate-at-config',
71+
OnceExit: migrate,
72+
}
73+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { DesignSystem } from '../../tailwindcss/src/design-system'
55
import { DefaultMap } from '../../tailwindcss/src/utils/default-map'
66
import { segment } from '../../tailwindcss/src/utils/segment'
77
import { migrateAtApply } from './codemods/migrate-at-apply'
8+
import { migrateAtConfig } from './codemods/migrate-at-config'
89
import { migrateAtLayerUtilities } from './codemods/migrate-at-layer-utilities'
910
import { migrateMissingLayers } from './codemods/migrate-missing-layers'
1011
import { migrateTailwindDirectives } from './codemods/migrate-tailwind-directives'
@@ -16,6 +17,7 @@ export interface MigrateOptions {
1617
newPrefix: string | null
1718
designSystem: DesignSystem
1819
userConfig: Config
20+
configFilePath: string
1921
}
2022

2123
export async function migrateContents(
@@ -33,6 +35,7 @@ export async function migrateContents(
3335
.use(migrateAtLayerUtilities(stylesheet))
3436
.use(migrateMissingLayers())
3537
.use(migrateTailwindDirectives(options))
38+
.use(migrateAtConfig(stylesheet, options))
3639
.process(stylesheet.root, { from: stylesheet.file ?? undefined })
3740
}
3841

packages/@tailwindcss-upgrade/src/template/prepare-config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export async function prepareConfig(
2222
designSystem: DesignSystem
2323
globs: { base: string; pattern: string }[]
2424
userConfig: Config
25+
configFilePath: string
2526

2627
newPrefix: string | null
2728
}> {
@@ -57,7 +58,13 @@ export async function prepareConfig(
5758
__unstable__loadDesignSystem(input, { base: __dirname }),
5859
])
5960

60-
return { designSystem, globs: compiler.globs, userConfig, newPrefix }
61+
return {
62+
designSystem,
63+
globs: compiler.globs,
64+
userConfig,
65+
newPrefix,
66+
configFilePath: fullConfigPath,
67+
}
6168
} catch (e: any) {
6269
error('Could not load the configuration file: ' + e.message)
6370
process.exit(1)

0 commit comments

Comments
 (0)