Debug journal
The bug report was wrong about itself.
On Thursday a pull request of mine merged into tailwindcss twenty-one minutes after I opened it. The diff was small: an exports map in one package.json, plus a changelog line. I want to write down why it went that fast, because the speed had almost nothing to do with the patch. It came from the hour I spent proving that the bug report was wrong about its own reproduction, before I wrote any fix at all.
The report
The issue, #20219, said that @tailwindcss/postcss ships CJS-shaped type declarations to ESM importers. Under TypeScript 6.0 that stops being a warning and becomes a hard error, TS1192. The reporter included a repro and a claim: reproducible against stock tsc. They also proposed a fix: change the declaration file's export = to export default.
The diagnosis was right. The package's exports map serves a single dist/index.d.ts, written in CJS shape, to both import and require consumers. That file says export = _default, which an ESM consumer under strict resolution cannot use as a default import.
The claim that didn't survive contact
Before writing anything, I built the repro the report described: a NodeNext project importing the published package, checked with a TypeScript 6.0 nightly. It exited zero. Stock tsc does not fail on this package, in either an ESM or a CJS consumer.
The reason is in how TypeScript decides what a declaration file means. The interpretation follows the file's extension and the nearest package.json type field, not which exports condition matched at resolution time. tsc reads dist/index.d.ts in a package without "type": "module", treats it as CJS, and applies its interop rules. No error. The report's mental model said the matched condition determines the file's format. TypeScript's module theory says it doesn't.
So was there a bug at all? Yes, one consumer to the left. deno check on a current Deno, which bundles TS 6.0 and resolves more strictly, fails with exactly the TS1192 the report quoted. The symptom was real. The attribution was wrong. If I had pattern-matched on the report and shipped its proposed fix, I would have submitted a patch whose own test case passed before the patch was applied.
The file that was already there
The second find was better. The published tarball already contains dist/index.d.mts, a correct ESM-shaped declaration ending in _default as default. It ships in every recent release. Nothing references it. The build was producing the right answer and the exports map was filing it where no resolver would ever look.
That reframed the fix from writing new declarations to wiring up existing ones. And the repo itself supplied the precedent: the sibling package @tailwindcss/vite already points its types at ./dist/index.d.mts. The fix for @tailwindcss/postcss became: split the exports into per-condition blocks, import getting the .d.mts file, require keeping the .d.ts. The reporter's proposed export default rewrite would have fixed ESM by breaking every CJS consumer. I said so in the PR, with the matrix to show it.
The matrix
The PR body carried four results, before and after. deno check goes from one error to zero. tsc stays at zero for both ESM and CJS consumers. arethetypeswrong goes from FalseCJS on the node16 ESM lane to green, and the one remaining node10 complaint exists identically on both sides of the patch, so it is out of scope and labeled as such.
Twenty-one minutes later a maintainer approved it with one word and merged. The review bot's summary called out that the change exactly parallels how other packages in the repo are structured. That sentence is the whole mechanism. A maintainer can merge in minutes when the diff is minimal, the precedent is in their own repo, and the PR has already answered the question they would otherwise have to ask: does the reporter's framing actually hold?
What I keep
A bug report is two claims wearing one label: something is broken, and here is why. The first claim deserves sympathy. The second deserves a repro run. They fail independently, and the failure modes mislead in different directions: a wrong "why" with a real symptom sends you to fix the wrong layer, and a fix for the wrong layer can look complete right up until a CJS consumer hits it.
The hour spent on disproof was not overhead before the real work. It was the real work. The patch took ten minutes. Everything that made it mergeable, the corrected attribution, the already-shipped file, the in-repo precedent, the before-and-after matrix, came out of refusing to take the report's word for what the report was about.