tailwindcss/packages
Cameron 5a4a7eba3a
fix(canonicalize): prevent collapse cache pollution across calls (#19675)
## Summary

fixes
https://github.com/schoero/eslint-plugin-better-tailwindcss/issues/321

This PR fixes an order-sensitive canonicalization bug. This bug caused
issues when running eslint-plugin-better-tailwindcss as the order in
which files are linted in is not consistent. This caused, in some
scenarios, `canonicalizeCandidates(..., { collapse: true,
logicalToPhysical: true, rem: 16 })` to stop collapsing valid
combinations (for example `h-4 + w-4 -> size-4`) after unrelated prior
calls.

To reproduce this issue:
```
# checkout this branch
$ git checkout c/fix-canonicalizeCandidates
# Revert the fix to the current `main` branch
$ git checkout main ./packages/tailwindcss/src/canonicalize-candidates.ts
# Run the tests
$ pnpm run test
```

This should produce a failure like so:

```

 FAIL   tailwindcss  src/canonicalize-candidates.test.ts > regressions > collapse canonicalization is not affected by previous calls
AssertionError: expected [ 'underline', 'h-4', 'w-4' ] to deeply equal [ 'underline', 'size-4' ]

- Expected
+ Received

  [
    "underline",
-   "size-4",
+   "h-4",
+   "w-4",
  ]

 ❯ src/canonicalize-candidates.test.ts:1167:66
    1165|     designSystem.canonicalizeCandidates(['underline', 'mb-4'], options)
    1166| 
    1167|     expect(designSystem.canonicalizeCandidates(target, options)).toEqual(['underline', 'size-4'])
       |                                                                  ^
    1168|   })
    1169| })
```

```
# reset all changes on this branch
git reset --hard
# run the tests again (they should now pass)
pnpm run test
```

The cause of this bug is that the canonicalization caches used
`DefaultMap` in places where lookups were expected to be read-only.
`DefaultMap.get` inserts missing entried, which mutated shared cache
state during intermediate lookups and made later canonicalization
results depend on prior calls.

By replacing the use of `DefaultMap` with a plain `Map`, it avoids
inserting into the map on lookup paths. I've polyfilled
[`Map#getOrInsert`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsert)
as it is not widely available yet, and used that where appropriate.


## Test plan

I wrote a test that fails on `main` branch, I then fixed the issue, and
validated that the test now passes.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-02-18 12:16:59 +00:00
..
@tailwindcss-browser docs: update package README CI badge to main (#19692) 2026-02-18 11:52:07 +01:00
@tailwindcss-cli Update enhanced-resolve 5.18.4 → 5.19.0 (minor) (#19658) 2026-02-18 11:59:11 +01:00
@tailwindcss-node Update enhanced-resolve 5.18.4 → 5.19.0 (minor) (#19658) 2026-02-18 11:59:11 +01:00
@tailwindcss-postcss docs: update package README CI badge to main (#19692) 2026-02-18 11:52:07 +01:00
@tailwindcss-standalone Update bun 1.3.7 → 1.3.9 (patch) (#19678) 2026-02-18 12:00:11 +01:00
@tailwindcss-upgrade Update enhanced-resolve 5.18.4 → 5.19.0 (minor) (#19658) 2026-02-18 11:59:11 +01:00
@tailwindcss-vite docs: update package README CI badge to main (#19692) 2026-02-18 11:52:07 +01:00
@tailwindcss-webpack Add @tailwindcss/webpack loader for Tailwind CSS v4 (#19610) 2026-01-29 14:16:31 +00:00
internal-example-plugin Prefix internal modules with internal-* (#14074) 2024-07-29 10:58:07 -04:00
tailwindcss fix(canonicalize): prevent collapse cache pollution across calls (#19675) 2026-02-18 12:16:59 +00:00
tsconfig.base.json Improve debug logs (#15303) 2024-12-11 15:27:20 +01:00