PostCSS: Fix Turbopack 'one-revision-behind' bug (#17554)

Closes #17508

This PR fixes another issue we found that caused dev builds with Next.js
and Turbopack to resolve the CSS file that was saved one revision before
the latest update.

When debugging this we noticed that the PostCSS entry is called twice
for every one update when changing the input CSS file directly. That was
caused by the input file itself being added as a _dependency_ so you
would first get the callback that a _dependency_ has updated (at which
point we look at the file system and figure out we need a full-rebuild
because the input.css file has changed) and then another callback for
when the _input file_ has updated. The problem with the second callback
was that the file-system was already scanned for updates and since this
includes the `mtimes` for the input file, we seemingly thought that the
input file did not change. However, the issue is that the first callback
actually came with an outdated PostCSS input AST...

We found that this problem arises when you register the input CSS as a
dependency of itself. This is not expected and we actually guard against
this in the PostCSS client. However, we found that the input `from`
argument is _a relative path when using Next.js with Turbopack_ so that
check was not working as expected.

## Test plan

Added the change to the repro from #17508 and it seems to work fine now.


https://github.com/user-attachments/assets/2acb0078-f961-4498-be1a-b1c72d5ceda1

Also added a unit test to ensure we document that the input file path
can be a relative path.

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
This commit is contained in:
Philipp Spiess 2025-04-04 14:58:50 +02:00 committed by GitHub
parent 2fd7c8d967
commit e085977844
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 27 additions and 1 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Show warning when using unsupported bare value data type in `--value(…)` ([#17464](https://github.com/tailwindlabs/tailwindcss/pull/17464))
- PostCSS: Resolve an issue where changes to the input CSS file showed outdated content when using Turbopack ([#17554](https://github.com/tailwindlabs/tailwindcss/pull/17554))
## [4.1.2] - 2025-04-03

View File

@ -0,0 +1 @@
/* the content for this file is set in the tests */

View File

@ -420,3 +420,25 @@ describe('concurrent builds', () => {
expect(await promise2).toContain('.red')
})
})
test('does not register the input file as a dependency, even if it is passed in as relative path', async () => {
let processor = postcss([
tailwindcss({ base: `${__dirname}/fixtures/example-project`, optimize: { minify: false } }),
])
let result = await processor.process(`@tailwind utilities`, { from: './input.css' })
expect(result.css.trim()).toMatchInlineSnapshot(`
".underline {
text-decoration-line: underline;
}"
`)
// Check for dependency messages
expect(result.messages).not.toContainEqual({
type: 'dependency',
file: expect.stringMatching(/input.css$/g),
parent: expect.any(String),
plugin: expect.any(String),
})
})

View File

@ -223,10 +223,12 @@ function tailwindcss(opts: PluginOptions = {}): AcceptedPlugin {
if (compiler.features & Features.Utilities) {
DEBUG && I.start('Register dependency messages')
// Add all found files as direct dependencies
// Note: With Turbopack, the input file might not be a resolved path
let resolvedInputFile = path.resolve(base, inputFile)
for (let file of context.scanner.files) {
let absolutePath = path.resolve(file)
// The CSS file cannot be a dependency of itself
if (absolutePath === result.opts.from) {
if (absolutePath === resolvedInputFile) {
continue
}
result.messages.push({