Skip to content

Commit 71fb9cd

Browse files
authored
Improve @tailwindcss/upgrade and pnpm workspaces support (#18065)
This PR fixes an issue where an error such as: <img width="1702" alt="image" src="https://github.com/user-attachments/assets/4e6f75c7-3182-4497-939e-96cff08c55ae" /> Will be thrown during the upgrade process. This can happen when you are using `pnpm` and your CSS file includes a `@import "tailwindcss";`. In this scenario, `tailwindcss` will be loaded from a shared `.pnpm` folder outside of the current working directory. In this case, we are also not interested in migrating _that_ file, but we also don't want the upgrade process to just crash. I didn't see an option to ignore errors like this, so wrapped it in a try/catch instead. It also fixes another issue where if you are using a pnpm workspace and run the upgrade tool from the root, then it throws you an error that you cannot add dependencies to the workspace root unless `-w` or `--workspace-root` flags are passed. For this, we disable the check entirely using the `--ignore-workspace-root-check` flag. If we always used the `--workspace-root` flag, then the dependencies would always be added to the root, regardless of where you are running the script from which is not what we want. ## Test plan Before: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/78246876-3eb6-4539-a557-d3d366f1b3a3" /> After: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/a65e4421-d7c5-4d83-b35d-934708543e25" /> Before: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/53772661-2c4a-4212-84d9-a556a0ad320f" /> After: <img width="1816" alt="image" src="https://github.com/user-attachments/assets/5bfaf20e-34b8-44fd-9b59-e72d36738879" />
1 parent c7d368b commit 71fb9cd

File tree

4 files changed

+185
-20
lines changed

4 files changed

+185
-20
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- Upgrade: Do not migrate declarations that look like candidates in `<style>` blocks ([#18057](https://github.com/tailwindlabs/tailwindcss/pull/18057))
13+
- Upgrade: Improve `pnpm` workspaces support ([#18065](https://github.com/tailwindlabs/tailwindcss/pull/18065))
1314

1415
## [4.1.7] - 2025-05-15
1516

integrations/upgrade/index.test.ts

Lines changed: 169 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import path from 'node:path'
12
import { isRepoDirty } from '../../packages/@tailwindcss-upgrade/src/utils/git'
2-
import { candidate, css, html, js, json, test, ts } from '../utils'
3+
import { candidate, css, html, js, json, test, ts, yaml } from '../utils'
34

45
test(
56
'error when no CSS file with @tailwind is used',
@@ -2967,6 +2968,155 @@ test(
29672968
},
29682969
)
29692970

2971+
test(
2972+
'upgrades can run in a pnpm workspace',
2973+
{
2974+
fs: {
2975+
'package.json': json`{}`,
2976+
'pnpm-workspace.yaml': yaml`
2977+
#
2978+
packages:
2979+
- project-a
2980+
`,
2981+
'project-a/package.json': json`
2982+
{
2983+
"dependencies": {
2984+
"tailwindcss": "^4"
2985+
},
2986+
"devDependencies": {
2987+
"@tailwindcss/upgrade": "workspace:^"
2988+
}
2989+
}
2990+
`,
2991+
'project-a/src/index.html': html`
2992+
<!-- Migrating 'ring', 'rounded' and 'outline-none' are unsafe in v4 -> v4 migrations -->
2993+
<div class="ring rounded outline"></div>
2994+
2995+
<!-- Variant order is also unsafe to change in v4 projects -->
2996+
<div class="file:hover:flex *:hover:flex"></div>
2997+
<div class="hover:file:flex hover:*:flex"></div>
2998+
2999+
<!-- These are safe to migrate: -->
3000+
<div
3001+
class="!flex bg-red-500/[var(--my-opacity)] [@media(pointer:fine)]:flex bg-right-bottom object-left-top"
3002+
></div>
3003+
`,
3004+
'project-a/src/input.css': css`
3005+
@import 'tailwindcss';
3006+
3007+
.foo {
3008+
@apply !bg-[var(--my-color)];
3009+
}
3010+
`,
3011+
},
3012+
},
3013+
async ({ root, exec, fs, expect }) => {
3014+
let stdout = await exec('npx @tailwindcss/upgrade', {
3015+
cwd: path.join(root, 'project-a'),
3016+
})
3017+
3018+
expect(/Path .*? is not in cwd/.test(stdout)).toBe(false)
3019+
3020+
expect(await fs.dumpFiles('./project-a/src/**/*.{css,html}')).toMatchInlineSnapshot(`
3021+
"
3022+
--- ./project-a/src/index.html ---
3023+
<!-- Migrating 'ring', 'rounded' and 'outline-none' are unsafe in v4 -> v4 migrations -->
3024+
<div class="ring rounded outline"></div>
3025+
3026+
<!-- Variant order is also unsafe to change in v4 projects -->
3027+
<div class="file:hover:flex *:hover:flex"></div>
3028+
<div class="hover:file:flex hover:*:flex"></div>
3029+
3030+
<!-- These are safe to migrate: -->
3031+
<div
3032+
class="flex! bg-red-500/(--my-opacity) pointer-fine:flex bg-bottom-right object-top-left"
3033+
></div>
3034+
3035+
--- ./project-a/src/input.css ---
3036+
@import 'tailwindcss';
3037+
3038+
.foo {
3039+
@apply bg-(--my-color)!;
3040+
}
3041+
"
3042+
`)
3043+
},
3044+
)
3045+
3046+
test(
3047+
'upgrades can run in a pnpm workspace root',
3048+
{
3049+
fs: {
3050+
'pnpm-workspace.yaml': yaml`
3051+
#
3052+
packages:
3053+
- .
3054+
`,
3055+
'package.json': json`
3056+
{
3057+
"dependencies": {
3058+
"tailwindcss": "^4"
3059+
},
3060+
"devDependencies": {
3061+
"@tailwindcss/upgrade": "workspace:^"
3062+
}
3063+
}
3064+
`,
3065+
'src/index.html': html`
3066+
<!-- Migrating 'ring', 'rounded' and 'outline-none' are unsafe in v4 -> v4 migrations -->
3067+
<div class="ring rounded outline"></div>
3068+
3069+
<!-- Variant order is also unsafe to change in v4 projects -->
3070+
<div class="file:hover:flex *:hover:flex"></div>
3071+
<div class="hover:file:flex hover:*:flex"></div>
3072+
3073+
<!-- These are safe to migrate: -->
3074+
<div
3075+
class="!flex bg-red-500/[var(--my-opacity)] [@media(pointer:fine)]:flex bg-right-bottom object-left-top"
3076+
></div>
3077+
`,
3078+
'src/input.css': css`
3079+
@import 'tailwindcss';
3080+
3081+
.foo {
3082+
@apply !bg-[var(--my-color)];
3083+
}
3084+
`,
3085+
},
3086+
},
3087+
async ({ exec, fs, expect }) => {
3088+
let stdout = await exec('npx @tailwindcss/upgrade')
3089+
3090+
expect(stdout).not.toContain(
3091+
'Running this command will add the dependency to the workspace root',
3092+
)
3093+
3094+
expect(await fs.dumpFiles('./src/**/*.{css,html}')).toMatchInlineSnapshot(`
3095+
"
3096+
--- ./src/index.html ---
3097+
<!-- Migrating 'ring', 'rounded' and 'outline-none' are unsafe in v4 -> v4 migrations -->
3098+
<div class="ring rounded outline"></div>
3099+
3100+
<!-- Variant order is also unsafe to change in v4 projects -->
3101+
<div class="file:hover:flex *:hover:flex"></div>
3102+
<div class="hover:file:flex hover:*:flex"></div>
3103+
3104+
<!-- These are safe to migrate: -->
3105+
<div
3106+
class="flex! bg-red-500/(--my-opacity) pointer-fine:flex bg-bottom-right object-top-left"
3107+
></div>
3108+
3109+
--- ./src/input.css ---
3110+
@import 'tailwindcss';
3111+
3112+
.foo {
3113+
@apply bg-(--my-color)!;
3114+
}
3115+
"
3116+
`)
3117+
},
3118+
)
3119+
29703120
test(
29713121
'upgrade <style> blocks carefully',
29723122
{
@@ -2980,21 +3130,21 @@ test(
29803130
}
29813131
`,
29823132
'src/index.vue': html`
2983-
<template
3133+
<template>
29843134
<div class="!flex"></div>
29853135
</template>
29863136
29873137
<style>
2988-
@reference "./input.css";
3138+
@reference "./input.css";
29893139
2990-
.foo {
2991-
@apply !bg-red-500;
2992-
}
3140+
.foo {
3141+
@apply !bg-red-500;
3142+
}
29933143
2994-
.bar {
2995-
/* Do not upgrade the key: */
2996-
flex-shrink: 0;
2997-
}
3144+
.bar {
3145+
/* Do not upgrade the key: */
3146+
flex-shrink: 0;
3147+
}
29983148
</style>
29993149
`,
30003150
'src/input.css': css`
@@ -3016,21 +3166,21 @@ test(
30163166
expect(await fs.dumpFiles('./src/**/*.{css,vue}')).toMatchInlineSnapshot(`
30173167
"
30183168
--- ./src/index.vue ---
3019-
<template
3169+
<template>
30203170
<div class="flex!"></div>
30213171
</template>
30223172
30233173
<style>
3024-
@reference "./input.css";
3174+
@reference "./input.css";
30253175
3026-
.foo {
3027-
@apply !bg-red-500;
3028-
}
3176+
.foo {
3177+
@apply !bg-red-500;
3178+
}
30293179
3030-
.bar {
3031-
/* Do not upgrade the key: */
3032-
flex-shrink: 0;
3033-
}
3180+
.bar {
3181+
/* Do not upgrade the key: */
3182+
flex-shrink: 0;
3183+
}
30343184
</style>
30353185
30363186
--- ./src/input.css ---

packages/@tailwindcss-upgrade/src/codemods/css/analyze.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@ export async function analyze(stylesheets: Stylesheet[]) {
1212
let processingQueue: (() => Promise<Result>)[] = []
1313
let stylesheetsByFile = new DefaultMap<string, Stylesheet | null>((file) => {
1414
// We don't want to process ignored files (like node_modules)
15-
if (isIgnored(file)) {
15+
try {
16+
if (isIgnored(file)) {
17+
return null
18+
}
19+
} catch {
20+
// If the file is not part of the current working directory (which can
21+
// happen if you import `tailwindcss` and it's loading a shared file from
22+
// pnpm) then this will throw.
1623
return null
1724
}
1825

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ export function pkg(base: string) {
3131
args.push(SAVE_DEV[packageManager] || SAVE_DEV.default)
3232
}
3333

34+
// Allow running the `pnpm` command in the workspace root without
35+
// erroring. Can't just use `--workspace-root` because that will force
36+
// install dependencies in the workspace root.
37+
if (packageManager === 'pnpm') {
38+
args.push('--ignore-workspace-root-check')
39+
}
40+
3441
let command = `${packageManager} add ${args.join(' ')}`
3542
try {
3643
return await exec(command, { cwd: base })

0 commit comments

Comments
 (0)