Skip to content

Commit 63390c9

Browse files
Throw a useful error when tailwindcss is used as a PostCSS plugin (#14378)
While upgrading a project to Tailwind CSS v4, I forgot to remove the `tailwindcss` import from the PostCSS config. As a result of this, I was greeted with the following message: ``` node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ [Failed to load PostCSS config: Failed to load PostCSS config (searchPath: /Users/philipp/dev/project): [TypeError] Invalid PostCSS Plugin found at: plugins[0] (@/Users/philipp/dev/project/postcss.config.js) TypeError: Invalid PostCSS Plugin found at: plugins[0] ``` I don't think this was particularly helpful, so I’m proposing we add a default function export to the `tailwindcss` package so when it's used inside PostCSS, we can control the error message. So I changed it to something along these lines: ``` It looks like you're trying to use the \`tailwindcss\` package as a PostCSS plugin. This is no longer possible since Tailwind CSS v4. If you want to continue to use Tailwind CSS with PostCSS, please install \`@tailwindcss/postcss\` and change your PostCSS config file. at w (/Users/philipp/dev/project/node_modules/tailwindcss/node_modules/tailwindcss/dist/lib.js:1:21233) at Object.<anonymous> (/Users/philipp/dev/project/node_modules/tailwindcss/postcss.config.cjs:3:13) at Module._compile (node:internal/modules/cjs/loader:1358:14) at Module._extensions..js (node:internal/modules/cjs/loader:1416:10) at Module.load (node:internal/modules/cjs/loader:1208:32) at Module._load (node:internal/modules/cjs/loader:1024:12) at cjsLoader (node:internal/modules/esm/translators:348:17) at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:297:7) at ModuleJob.run (node:internal/modules/esm/module_job:222:25) at async ModuleLoader.import (node:internal/modules/esm/loader:316:24) ``` This is also a good place to link to the migration guides once we have them 🙂 --------- Co-authored-by: Adam Wathan <[email protected]>
1 parent d2b5731 commit 63390c9

File tree

8 files changed

+103
-11
lines changed

8 files changed

+103
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Make sure tuple theme values in JS configs take precedence over `@theme default` values ([#14359](https://github.com/tailwindlabs/tailwindcss/pull/14359))
2121
- Improve IntelliSense completions for `border` utilities ([#14370](https://github.com/tailwindlabs/tailwindcss/pull/14370))
2222

23+
### Changed
24+
25+
- Improve the error message when the `tailwindcss` package is used as a PostCSS plugin ([#14378](https://github.com/tailwindlabs/tailwindcss/pull/14378))
26+
2327
## [4.0.0-alpha.23] - 2024-09-05
2428

2529
### Added
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { expect } from 'vitest'
2+
import { css, js, json, test } from '../utils'
3+
4+
const variantConfig = {
5+
string: {
6+
'postcss.config.js': js`
7+
module.exports = {
8+
plugins: {
9+
tailwindcss: {},
10+
},
11+
}
12+
`,
13+
},
14+
ESM: {
15+
'postcss.config.mjs': js`
16+
import tailwindcss from 'tailwindcss'
17+
export default {
18+
plugins: [tailwindcss()],
19+
}
20+
`,
21+
},
22+
CJS: {
23+
'postcss.config.cjs': js`
24+
let tailwindcss = require('tailwindcss')
25+
module.exports = {
26+
plugins: [tailwindcss()],
27+
}
28+
`,
29+
},
30+
}
31+
32+
for (let variant of Object.keys(variantConfig)) {
33+
test(
34+
`can not use \`tailwindcss\` as a postcss module (${variant})`,
35+
{
36+
fs: {
37+
...variantConfig[variant],
38+
'package.json': json`
39+
{
40+
"dependencies": {
41+
"postcss": "^8",
42+
"postcss-cli": "^10",
43+
"tailwindcss": "workspace:^"
44+
}
45+
}
46+
`,
47+
'src/index.css': css`@import 'tailwindcss';`,
48+
},
49+
},
50+
async ({ exec }) => {
51+
expect(
52+
exec('pnpm postcss src/index.css --output dist/out.css', undefined, { ignoreStdErr: true }),
53+
).rejects.toThrowError(
54+
`It looks like you're trying to use \`tailwindcss\` directly as a PostCSS plugin. The PostCSS plugin has moved to a separate package, so to continue using Tailwind CSS with PostCSS you'll need to install \`@tailwindcss/postcss\` and update your PostCSS configuration.`,
55+
)
56+
},
57+
)
58+
}

integrations/utils.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,18 @@ interface ChildProcessOptions {
2323
cwd?: string
2424
}
2525

26+
interface ExecOptions {
27+
ignoreStdErr?: boolean
28+
}
29+
2630
interface TestConfig {
2731
fs: {
2832
[filePath: string]: string
2933
}
3034
}
3135
interface TestContext {
3236
root: string
33-
exec(command: string, options?: ChildProcessOptions): Promise<string>
37+
exec(command: string, options?: ChildProcessOptions, execOptions?: ExecOptions): Promise<string>
3438
spawn(command: string, options?: ChildProcessOptions): Promise<SpawnedProcess>
3539
getFreePort(): Promise<number>
3640
fs: {
@@ -84,7 +88,11 @@ export function test(
8488

8589
let context = {
8690
root,
87-
async exec(command: string, childProcessOptions: ChildProcessOptions = {}) {
91+
async exec(
92+
command: string,
93+
childProcessOptions: ChildProcessOptions = {},
94+
execOptions: ExecOptions = {},
95+
) {
8896
let cwd = childProcessOptions.cwd ?? root
8997
if (debug && cwd !== root) {
9098
let relative = path.relative(root, cwd)
@@ -101,7 +109,7 @@ export function test(
101109
},
102110
(error, stdout, stderr) => {
103111
if (error) {
104-
console.error(stderr)
112+
if (execOptions.ignoreStdErr !== true) console.error(stderr)
105113
reject(error)
106114
} else {
107115
resolve(stdout.toString())
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import colors from './colors.ts'
22

3+
// This file exists so that `colors.ts` can be written one time but be
4+
// compatible with both CJS and ESM. Without it we get a `.default` export when
5+
// using `require` in CJS.
6+
37
// @ts-ignore
48
export = colors
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import defaultTheme from './default-theme.ts'
22

3+
// This file exists so that `default-theme.ts` can be written one time but be
4+
// compatible with both CJS and ESM. Without it we get a `.default` export when
5+
// using `require` in CJS.
6+
37
// @ts-ignore
48
export = defaultTheme

packages/tailwindcss/src/index.cts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import postcssPlugin, * as tailwindcss from './index.ts'
2+
3+
// This file exists so that `index.ts` can be written one time but be
4+
// compatible with both CJS and ESM. Without it we get a `.default` export when
5+
// using `require` in CJS.
6+
7+
for (let key in tailwindcss) {
8+
if (key === 'default') continue
9+
// @ts-ignore
10+
postcssPlugin[key] = tailwindcss[key]
11+
}
12+
13+
// @ts-ignore
14+
export = postcssPlugin

packages/tailwindcss/src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,3 +498,9 @@ function getVersion() {
498498
return version
499499
}
500500
}
501+
502+
export default function postcssPluginWarning() {
503+
throw new Error(
504+
`It looks like you're trying to use \`tailwindcss\` directly as a PostCSS plugin. The PostCSS plugin has moved to a separate package, so to continue using Tailwind CSS with PostCSS you'll need to install \`@tailwindcss/postcss\` and update your PostCSS configuration.`,
505+
)
506+
}

packages/tailwindcss/tsup.config.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
import { defineConfig } from 'tsup'
22

33
export default defineConfig([
4-
{
5-
format: ['esm', 'cjs'],
6-
minify: true,
7-
dts: true,
8-
entry: {
9-
lib: 'src/index.ts',
10-
},
11-
},
124
{
135
format: ['esm'],
146
minify: true,
157
dts: true,
168
entry: {
9+
lib: 'src/index.ts',
1710
plugin: 'src/plugin.ts',
1811
colors: 'src/compat/colors.ts',
1912
'default-theme': 'src/compat/default-theme.ts',
@@ -25,6 +18,7 @@ export default defineConfig([
2518
dts: true,
2619
entry: {
2720
plugin: 'src/plugin.cts',
21+
lib: 'src/index.cts',
2822
colors: 'src/compat/colors.cts',
2923
'default-theme': 'src/compat/default-theme.cts',
3024
},

0 commit comments

Comments
 (0)