Skip to content

Commit bfb5732

Browse files
rebasecaserebasecaseRobinMalfait
authored
Fall back to the plugin base when PostCSS has no from option (#19980)
## Summary `@tailwindcss/postcss` derives `inputBasePath` from `result.opts.from`: ```ts let inputFile = result.opts.from ?? '' let inputBasePath = path.dirname(path.resolve(inputFile)) ``` When PostCSS calls the plugin without `from` (some bundlers, including Turbopack, do this for certain CSS inputs), `inputFile` is `''`, `path.resolve('')` returns `process.cwd()`, and `path.dirname(...)` therefore returns the **parent of CWD**. The downstream `compileAst({ base: inputBasePath })` call then asks the resolver to find `tailwindcss` from one level above the project root, which fails with: ``` Can't resolve 'tailwindcss' in '<parent of CWD>' ``` The plugin already computes `base = opts.base ?? process.cwd()` near the top. Reusing that as the fallback gives a sensible default (CWD) and respects an explicit `opts.base` when set. ```diff -let inputBasePath = path.dirname(path.resolve(inputFile)) +let inputBasePath = inputFile + ? path.dirname(path.resolve(inputFile)) + : base ``` ## Test plan Added a test in `packages/@tailwindcss-postcss/src/index.test.ts` that processes `@import 'tailwindcss'` via `processor.process(input)` with no `from` option. Before the fix, this throws `Error: Can't resolve 'tailwindcss' in '<parent of CWD>'`; after the fix, the import resolves and the processor returns non-empty CSS. I wasn't able to run the suite locally — `pnpm build` requires `cargo` for `@tailwindcss/oxide` and I don't have a Rust toolchain set up — so the test has been written to match existing conventions in `index.test.ts` (vitest, plain `postcss([tailwindcss({...})]).process(...)`), and I'm relying on CI to verify. --------- Co-authored-by: rebasecase <rebasecase@localhost> Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
1 parent 3a890c3 commit bfb5732

3 files changed

Lines changed: 12 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Ensure `@plugin` resolves package JavaScript entries instead of browser CSS entries when using `@tailwindcss/vite` ([#19949](https://github.com/tailwindlabs/tailwindcss/pull/19949))
1717
- Fix relative `@import` and `@plugin` paths resolving from the wrong directory when using `@tailwindcss/vite` ([#19965](https://github.com/tailwindlabs/tailwindcss/pull/19965))
1818
- Ensure CSS files containing `@variant` are processed by `@tailwindcss/vite` ([#19966](https://github.com/tailwindlabs/tailwindcss/pull/19966))
19+
- Resolve imports relative to `base` when `result.opts.from` is not provided when using `@tailwindcss/postcss` ([#19980](https://github.com/tailwindlabs/tailwindcss/pull/19980))
1920

2021
## [4.2.4] - 2026-04-21
2122

packages/@tailwindcss-postcss/src/index.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ describe('processing without specifying a base path', () => {
140140
})
141141
})
142142

143+
test('fallback to `base` directory when `result.opts.from` is not provided', async () => {
144+
let processor = postcss([
145+
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
146+
])
147+
148+
let result = await processor.process(`@import 'tailwindcss'`)
149+
150+
expect(result.css.length).toBeGreaterThan(0)
151+
})
152+
143153
describe('plugins', () => {
144154
test('local CJS plugin', async () => {
145155
let processor = postcss([

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
115115
}
116116

117117
let context = getContextFromCache(postcss, inputFile, opts)
118-
let inputBasePath = path.dirname(path.resolve(inputFile))
118+
let inputBasePath = inputFile ? path.dirname(path.resolve(inputFile)) : base
119119

120120
// Whether this is the first build or not, if it is, then we can
121121
// optimize the build by not creating the compiler until we need it.

0 commit comments

Comments
 (0)