Skip to content

fix(canonicalize): prevent collapse cache pollution across calls#19675

Merged
RobinMalfait merged 8 commits intotailwindlabs:mainfrom
camc314:c/fix-canonicalizeCandidates
Feb 18, 2026
Merged

fix(canonicalize): prevent collapse cache pollution across calls#19675
RobinMalfait merged 8 commits intotailwindlabs:mainfrom
camc314:c/fix-canonicalizeCandidates

Conversation

@camc314
Copy link
Contributor

@camc314 camc314 commented Feb 14, 2026

Summary

fixes schoero/eslint-plugin-better-tailwindcss#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 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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 14, 2026

No actionable comments were generated in the recent review. 🎉


Walkthrough

The PR adds two unit tests for canonicalization: one verifying canonical output for a subset of classes and another exercising repeated collapse canonicalization to ensure idempotence. It updates collapseGroup to short-circuit per-candidate processing when a candidate’s property-values mapping lacks line-height or font-size, skipping further work for those candidates. No public API or exported signatures were changed.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(canonicalize): prevent collapse cache pollution across calls' directly and clearly describes the main change - fixing a cache pollution bug in canonicalization.
Description check ✅ Passed The description is comprehensive and relates directly to the changeset, explaining the bug, its cause, the fix, and providing a test plan.
Linked Issues check ✅ Passed The PR addresses the core requirement from issue #321 by making canonicalizeCandidates order-independent through fixing DefaultMap usage in caches, preventing cache pollution across multiple calls.
Out of Scope Changes check ✅ Passed All changes are focused on fixing the cache pollution bug: modified canonicalization logic, added regression tests, and updated changelog - all directly related to the linked issue requirements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an order-sensitive bug in canonicalizeCandidates where collapse-related caches could be unintentionally mutated during read-only lookups, causing later canonicalization results to depend on earlier calls (important for non-deterministic ESLint lint order).

Changes:

  • Replaced DefaultMap-backed collapse caches with Map to avoid mutation on lookup paths.
  • Added a Map#getOrInsert polyfill and refactored cache access to use explicit “insert-on-miss” behavior.
  • Added a regression test ensuring collapse canonicalization is stable across repeated calls.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/tailwindcss/src/utils/map-get-or-insert.ts Introduces a Map#getOrInsert polyfill used for explicit insert-on-miss cache behavior.
packages/tailwindcss/src/canonicalize-candidates.ts Refactors static-utility and utility-property caches from DefaultMap to Map, and updates collapse logic to use explicit cache population.
packages/tailwindcss/src/canonicalize-candidates.test.ts Adds regression coverage for the order-sensitivity/cross-call cache pollution bug.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 1 to 21
export {}

declare global {
interface Map<K, V> {
getOrInsert(key: K, defaultValue: V): V
}
}

if (typeof Map.prototype.getOrInsert !== 'function') {
Object.defineProperty(Map.prototype, 'getOrInsert', {
configurable: true,
enumerable: false,
writable: true,
value: function <K, V>(this: Map<K, V>, key: K, defaultValue: V): V {
if (!this.has(key)) {
this.set(key, defaultValue)
}

return this.get(key)!
},
})
Copy link

Copilot AI Feb 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module both augments the global Map TypeScript interface and patches Map.prototype at runtime. Since tailwindcss is a published library, this leaks a non-standard API into consumers’ global types/runtime and can lead to conflicts with other tooling or future platform implementations. Prefer a local helper (e.g. getOrInsert(map, key, createValue)), or a small wrapper class used only for these caches, to avoid global/prototype mutation.

Suggested change
export {}
declare global {
interface Map<K, V> {
getOrInsert(key: K, defaultValue: V): V
}
}
if (typeof Map.prototype.getOrInsert !== 'function') {
Object.defineProperty(Map.prototype, 'getOrInsert', {
configurable: true,
enumerable: false,
writable: true,
value: function <K, V>(this: Map<K, V>, key: K, defaultValue: V): V {
if (!this.has(key)) {
this.set(key, defaultValue)
}
return this.get(key)!
},
})
export function getOrInsert<K, V>(map: Map<K, V>, key: K, defaultValue: V): V {
if (!map.has(key)) {
map.set(key, defaultValue)
}
return map.get(key)!

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a fair point, I didn't think about the fact this was a library vs runtime code.

I'll leave this up to a maintainer to decide if this should be fixed or not - please let me know and i'll change it

@RobinMalfait RobinMalfait force-pushed the c/fix-canonicalizeCandidates branch from ec8049a to 2c5a71f Compare February 18, 2026 11:10
It does mean that the `result` can be empty now, so in that case we
fallback to an empty Set.

While it's true that consecutive calls to
`designSystem.canonicalizeCandidates(candidates, options)` will update
some shared caches for this design system, it's done on purpose to
reduce the amount of work necessary to compute canonicalizations.

However, this resulted in the fact that some caches become polluted by
previous runs. This on its own is not the issue, but we made some
assumptions as-if it's a fresh run. Ignoring unrelated values should do
the trick here.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/tailwindcss/src/canonicalize-candidates.ts (1)

2604-2618: Pre-existing bug in the intersection polyfill — returns a copy of a instead of the true intersection on Node.js < 22.

The loop iterates over b and calls result.delete(item) only when !result.has(item) — but if item is not in result (a copy of a), the delete is a no-op. Items in a that are absent from b are never removed. The function effectively returns new Set(a).

This does not affect correctness — the signature comparison at line 399 gates every potential replacement, so only genuinely valid candidates pass through. It does, however, degrade performance in polyfill environments: potentialReplacements is the full otherUtilities[0] set rather than the narrower intersection, so the inner for…of loop iterates over more candidates than necessary.

♻️ Corrected polyfill
  let result = new Set<T>(a)
- for (let item of b) {
-   if (!result.has(item)) {
-     result.delete(item)
-   }
- }
+ for (let item of result) {
+   if (!b.has(item)) {
+     result.delete(item)
+   }
+ }
  return result
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/tailwindcss/src/canonicalize-candidates.ts` around lines 2604 -
2618, The polyfill in function intersection<T>(a: Set<T>, b: Set<T>) is wrong:
it creates result = new Set(a) then iterates b but deletes only when
!result.has(item), which never removes elements of a that are not in b; fix by
computing the true intersection — either create an empty Set and add items from
a that are present in b, or iterate over result (a's copy) and delete any item
not present in b — ensuring the polyfill returns only items common to both a and
b for Node.js <22 environments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/tailwindcss/src/canonicalize-candidates.ts`:
- Around line 2604-2618: The polyfill in function intersection<T>(a: Set<T>, b:
Set<T>) is wrong: it creates result = new Set(a) then iterates b but deletes
only when !result.has(item), which never removes elements of a that are not in
b; fix by computing the true intersection — either create an empty Set and add
items from a that are present in b, or iterate over result (a's copy) and delete
any item not present in b — ensuring the polyfill returns only items common to
both a and b for Node.js <22 environments.

Alright silly me. The DefaultMap of course caches the moment you use
`.get(…)` which results in the cache pollution. We _only_ want to do
that if the properties of the current candidate already contains the
line-height or font-size properties.

If we do it unconditionally, then `['underline', 'text-sm', 'w-4',
'h-4']` will have the `line-height` and `font-size` properties set for
every candidate in this list but we only want it for the `text-sm` one.

If we don't do this, the `h-4` will get a `line-height: new Set()` which
is empty and therefore an intersection won't work anymore.
@RobinMalfait
Copy link
Member

Hey, thanks for the PR!

We track some caches on a per designSystem instance to reduce the amount of computation that we have to do. You are correct that subsequent calls influence the caches.

While looking at the tests and the implementation, I noticed something where your current test has:

designSystem.canonicalizeCandidates(['underline', 'h-4', 'w-4'], options)

Which correctly becomes ['underline', 'size-4']. However, in a subsequent call we added text-sm and then the size-4 was not produced. This made me think about the simpler case:

designSystem.canonicalizeCandidates(['underline', 'h-4', 'w-4', 'text-sm'], options)

Turns out that this on its own also doesn't work.

I added an additional test for this test case, and turns out while doing some debugging fixing this also means that your original test passes. We do collect properties/values so we can use some intersections later. However, we did do something silly where we added some properties of some text utilities to the property/value cache of unrelated candidates (classes). If we only do that for the relevant candidates then we should be good.

@RobinMalfait RobinMalfait enabled auto-merge (squash) February 18, 2026 12:09
@RobinMalfait RobinMalfait merged commit 5a4a7eb into tailwindlabs:main Feb 18, 2026
8 checks passed
@camc314
Copy link
Contributor Author

camc314 commented Feb 20, 2026

I thought there would be a simpler solution!

Thanks for getting to this and maintaining Tailwind!

MrBns pushed a commit to MrBns/tailwindcss-official that referenced this pull request Feb 28, 2026
Here is everything you need to know about this update. Please take a
good look at what changed and the test results before merging this pull
request.

### What changed?




#### ✳️ eslint (9.25.1 → 9.27.0) ·
[Repo](https://github.com/eslint/eslint) ·
[Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)



<details>
<summary>Release Notes</summary>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.27.0">9.27.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/d71e37f450f4ae115ec394615e21523685f0d370"><code
class="notranslate">d71e37f</code></a> feat: Allow flags to be set in
ESLINT_FLAGS env variable (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19717">#19717</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/ba456e000e104fd7f2dbd27eebbd4f35e6c18934"><code
class="notranslate">ba456e0</code></a> feat: Externalize MCP server (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19699">#19699</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/07c1a7e839ec61bd706c651428606ea5955b2bb0"><code
class="notranslate">07c1a7e</code></a> feat: add <code
class="notranslate">allowRegexCharacters</code> to <code
class="notranslate">no-useless-escape</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19705">#19705</a>)
(sethamus)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/7bc6c71ca350fa37531291e1d704be6ed408c5dc"><code
class="notranslate">7bc6c71</code></a> feat: add no-unassigned-vars rule
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19618">#19618</a>)
(Jacob Bandes-Storch)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/ee4036429758cdaf7f77c52f1c2b74b5a2bb7b66"><code
class="notranslate">ee40364</code></a> feat: convert
no-array-constructor suggestions to autofixes (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19621">#19621</a>)
(sethamus)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/32957cde72196c7e41741db311786d881c1613a1"><code
class="notranslate">32957cd</code></a> feat: support TS syntax in <code
class="notranslate">max-params</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19557">#19557</a>)
(Nitin Kumar)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/5687ce7055d30e2d5ef800b3d5c3096c3fc42c0e"><code
class="notranslate">5687ce7</code></a> fix: correct mismatched removed
rules (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19734">#19734</a>)
(루밀LuMir)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/dc5ed337fd18cb59801e4afaf394f6b84057b601"><code
class="notranslate">dc5ed33</code></a> fix: correct types and tighten
type definitions in <code class="notranslate">SourceCode</code> class
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19731">#19731</a>)
(루밀LuMir)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/de1b5deba069f770140f3a7dba2702c1016dcc2a"><code
class="notranslate">de1b5de</code></a> fix: correct <code
class="notranslate">service</code> property name in <code
class="notranslate">Linter.ESLintParseResult</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19713">#19713</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/60c3e2cf9256f3676b7934e26ff178aaf19c9e97"><code
class="notranslate">60c3e2c</code></a> fix: sort keys in
eslint-suppressions.json to avoid git churn (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19711">#19711</a>)
(Ron Waldon-Howe)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/9da90ca3c163adb23a9cc52421f59dedfce34fc9"><code
class="notranslate">9da90ca</code></a> fix: add <code
class="notranslate">allowReserved</code> to <code
class="notranslate">Linter.ParserOptions</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19710">#19710</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/fbb8be9256dc7613fa0b87e87974714284b78a94"><code
class="notranslate">fbb8be9</code></a> fix: add <code
class="notranslate">info</code> to <code
class="notranslate">ESLint.DeprecatedRuleUse</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19701">#19701</a>)
(Francesco Trotta)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/25de55055d420d7c8b794ae5fdaeb67947c613d9"><code
class="notranslate">25de550</code></a> docs: Update description of
frozen rules to mention TypeScript (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19736">#19736</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/bd5def66d1a3f9bad7da3547b5dff6003e67d9d3"><code
class="notranslate">bd5def6</code></a> docs: Clean up configuration
files docs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19735">#19735</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/4d0c60d0738cb32c12e4ea132caa6fab6d5ed0a7"><code
class="notranslate">4d0c60d</code></a> docs: Add Neovim to editor
integrations (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19729">#19729</a>)
(Maria José Solano)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/71317ebeaf1c542114e4fcda99ee26115d8e4a27"><code
class="notranslate">71317eb</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/4c289e685e6cf87331f4b1e6afe34a4feb8e6cc8"><code
class="notranslate">4c289e6</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/f0f0d46ab2f87e439642abd84b6948b447b66349"><code
class="notranslate">f0f0d46</code></a> docs: clarify that unused
suppressions cause non-zero exit code (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19698">#19698</a>)
(Milos Djermanovic)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/8ed32734cc22988173f99fd0703d50f94c60feb8"><code
class="notranslate">8ed3273</code></a> docs: fix internal usages of
<code class="notranslate">ConfigData</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19688">#19688</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/eb316a83a49347ab47ae965ff95f81dd620d074c"><code
class="notranslate">eb316a8</code></a> docs: add <code
class="notranslate">fmt</code> and <code
class="notranslate">check</code> sections to <code
class="notranslate">Package.json Conventions</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19686">#19686</a>)
(루밀LuMir)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/a3a255924866b94ef8d604e91636547600edec56"><code
class="notranslate">a3a2559</code></a> docs: fix wording in Combine
Configs (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19685">#19685</a>)
(Milos Djermanovic)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/c8d17e11dc63909e693eaed5b5ccc50e698ac3b3"><code
class="notranslate">c8d17e1</code></a> docs: Update README (GitHub
Actions Bot)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/f8f1560de633aaf24a7099f89cbbfed12a762a32"><code
class="notranslate">f8f1560</code></a> chore: upgrade @eslint/js@9.27.0
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19739">#19739</a>)
(Milos Djermanovic)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/ecaef7351f9f3220aa57409bf98db3e55b07a02a"><code
class="notranslate">ecaef73</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/596fdc62047dff863e990c3246b32da97ae9a14e"><code
class="notranslate">596fdc6</code></a> chore: update dependency
@arethetypeswrong/cli to ^0.18.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19732">#19732</a>)
(renovate[bot])</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/f791da040189ada1b1ec15856557b939ffcd978b"><code
class="notranslate">f791da0</code></a> chore: remove unbalanced curly
brace from <code class="notranslate">.editorconfig</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19730">#19730</a>)
(Maria José Solano)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/e86edee0918107e4e41e908fe59c937b83f00d4e"><code
class="notranslate">e86edee</code></a> refactor: Consolidate Config
helpers (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19675">#19675</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/cf3635299e09570b7472286f25dacd8ab24e0517"><code
class="notranslate">cf36352</code></a> chore: remove shared types (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19718">#19718</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/f60f2764971a33e252be13e560dccf21f554dbf1"><code
class="notranslate">f60f276</code></a> refactor: Easier RuleContext
creation (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19709">#19709</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/58a171e8f0dcc1e599ac22bf8c386abacdbee424"><code
class="notranslate">58a171e</code></a> chore: update dependency
@eslint/plugin-kit to ^0.3.1 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19712">#19712</a>)
(renovate[bot])</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/3a075a29cfb43ef08711c2e433fb6f218855886d"><code
class="notranslate">3a075a2</code></a> chore: update dependency
@eslint/core to ^0.14.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19715">#19715</a>)
(renovate[bot])</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/44bac9d15c4e0ca099d0b0d85e601f3b55d4e167"><code
class="notranslate">44bac9d</code></a> ci: run tests in Node.js 24 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19702">#19702</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/35304dd2b0d8a4b640b9a25ae27ebdcb5e124cde"><code
class="notranslate">35304dd</code></a> chore: add missing <code
class="notranslate">funding</code> field to packages (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19684">#19684</a>)
(루밀LuMir)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/f305beb82c51215ad48c5c860f02be1b34bcce32"><code
class="notranslate">f305beb</code></a> test: mock <code
class="notranslate">process.emitWarning</code> to prevent output
disruption (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19687">#19687</a>)
(Francesco Trotta)</li>
</ul></blockquote>
<h4><a
href="https://github.com/eslint/eslint/releases/tag/v9.26.0">9.26.0</a></h4>

<blockquote><h2 dir="auto">Features</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/e9754e7433edf665602ceba4f7f8fbca559c974f"><code
class="notranslate">e9754e7</code></a> feat: add reportGlobalThis to
no-shadow-restricted-names (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19670">#19670</a>)
(sethamus)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/0fa2b7a3666f1eedcc091446dc860037c9bafa5c"><code
class="notranslate">0fa2b7a</code></a> feat: add suggestions for <code
class="notranslate">eqeqeq</code> rule (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19640">#19640</a>)
(Nitin Kumar)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/dcbdcc9c6be628240269b41f7bb576dde1e6f5b3"><code
class="notranslate">dcbdcc9</code></a> feat: Add MCP server (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19592">#19592</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/2dfd83ef4ee054f748732581c422508c45d6f1bf"><code
class="notranslate">2dfd83e</code></a> feat: add <code
class="notranslate">ignoreDirectives</code> option in <code
class="notranslate">no-unused-expressions</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19645">#19645</a>)
(sethamus)</li>
</ul>
<h2 dir="auto">Bug Fixes</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/96e84de55ad17c96e5b6f2dece75145542505469"><code
class="notranslate">96e84de</code></a> fix: check cache file existence
before deletion (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19648">#19648</a>)
(sethamus)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/d683aebc8e0792e4f80bd1488c705c90f22c317e"><code
class="notranslate">d683aeb</code></a> fix: don't crash on tests with
circular references in <code class="notranslate">RuleTester</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19664">#19664</a>)
(Milos Djermanovic)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/9736d5d15870c9185da7d140becb9a15aa69057d"><code
class="notranslate">9736d5d</code></a> fix: add <code
class="notranslate">namespace</code> to <code
class="notranslate">Plugin.meta</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19661">#19661</a>)
(Milos Djermanovic)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/17bae69e02fff6f26487a3cbd9c3c3218088949c"><code
class="notranslate">17bae69</code></a> fix: update <code
class="notranslate">RuleTester.run()</code> type (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19634">#19634</a>)
(Nitin Kumar)</li>
</ul>
<h2 dir="auto">Documentation</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/dd98d63f09c9324124734206d904d31d433a7c92"><code
class="notranslate">dd98d63</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/c25e858d2d7e9bd3e53dcb32c9af5251d6f0569e"><code
class="notranslate">c25e858</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/b2397e9bef5ca7faf7e100ecebc20e457bf0b588"><code
class="notranslate">b2397e9</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/addd0a6a62d1b89dc7ab49cbd08c5a6af3e7da29"><code
class="notranslate">addd0a6</code></a> docs: fix formatting of unordered
lists in Markdown (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19660">#19660</a>)
(Milos Djermanovic)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/a21b38db0276ab3373c95ebc7b1ef1910b79dfe6"><code
class="notranslate">a21b38d</code></a> docs: Update README (GitHub
Actions Bot)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/c0721a7f34264da0a32ade8432511eeda4a2c1b9"><code
class="notranslate">c0721a7</code></a> docs: fix double space in command
(<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19657">#19657</a>)
(CamWass)</li>
</ul>
<h2 dir="auto">Chores</h2>
<ul dir="auto">
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/5b247c859f1b653297a9b9135d92a59742a669cc"><code
class="notranslate">5b247c8</code></a> chore: upgrade to <code
class="notranslate">@eslint/js@9.26.0</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19681">#19681</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/d6fa4ac031c2fe24fb778e84940393fbda3ddf77"><code
class="notranslate">d6fa4ac</code></a> chore: package.json update for
@eslint/js release (Jenkins)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/09586905be394c05839996a5ea812adfac44d320"><code
class="notranslate">0958690</code></a> chore: disambiguate internal
types <code class="notranslate">LanguageOptions</code> and <code
class="notranslate">Rule</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19669">#19669</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/f1c858e3c1e9712ef398588bf5ed68bc19fad3f2"><code
class="notranslate">f1c858e</code></a> chore: fix internal type
references to <code class="notranslate">Plugin</code> and <code
class="notranslate">Rule</code> (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19665">#19665</a>)
(Francesco Trotta)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/40dd2998cedddb75e0514b2c5cc855293c85da41"><code
class="notranslate">40dd299</code></a> refactor: One-shot ESQuery
selector analysis (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19652">#19652</a>)
(Nicholas C. Zakas)</li>
<li>
<a
href="https://bounce.depfu.com/github.com/eslint/eslint/commit/1cfd7024226cd9c42ceb75732f79e3bc36e8305c"><code
class="notranslate">1cfd702</code></a> chore: update dependency
@eslint/json to ^0.12.0 (<a
href="https://bounce.depfu.com/github.com/eslint/eslint/pull/19656">#19656</a>)
(renovate[bot])</li>
</ul></blockquote>
<p><em>Does any of this look wrong? <a
href="https://depfu.com/packages/npm/eslint/feedback">Please let us
know.</a></em></p>
</details>

<details>
<summary>Commits</summary>
<p><a
href="https://github.com/eslint/eslint/compare/3ed4b3652d9fe3dfa4017d22a6ddbd15e3c6cd7a...b9080cf28d88f934941a545a033eb960eceeadbd">See
the full diff on Github</a>. The new version differs by 58 commits:</p>
<ul>
<li><a
href="https://github.com/eslint/eslint/commit/b9080cf28d88f934941a545a033eb960eceeadbd"><code>9.27.0</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/b7a5c66129c6e504368d1fc452f58c538e4d48e6"><code>Build:
changelog update for 9.27.0</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/f8f1560de633aaf24a7099f89cbbfed12a762a32"><code>chore:
upgrade @eslint/js@9.27.0 (#19739)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/ecaef7351f9f3220aa57409bf98db3e55b07a02a"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/25de55055d420d7c8b794ae5fdaeb67947c613d9"><code>docs:
Update description of frozen rules to mention TypeScript
(tailwindlabs#19736)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/bd5def66d1a3f9bad7da3547b5dff6003e67d9d3"><code>docs:
Clean up configuration files docs (tailwindlabs#19735)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/d71e37f450f4ae115ec394615e21523685f0d370"><code>feat:
Allow flags to be set in ESLINT_FLAGS env variable
(tailwindlabs#19717)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/5687ce7055d30e2d5ef800b3d5c3096c3fc42c0e"><code>fix:
correct mismatched removed rules (tailwindlabs#19734)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/596fdc62047dff863e990c3246b32da97ae9a14e"><code>chore:
update dependency @arethetypeswrong/cli to ^0.18.0
(tailwindlabs#19732)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/ba456e000e104fd7f2dbd27eebbd4f35e6c18934"><code>feat:
Externalize MCP server (tailwindlabs#19699)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/dc5ed337fd18cb59801e4afaf394f6b84057b601"><code>fix:
correct types and tighten type definitions in `SourceCode` class
(tailwindlabs#19731)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/4d0c60d0738cb32c12e4ea132caa6fab6d5ed0a7"><code>docs:
Add Neovim to editor integrations (tailwindlabs#19729)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/f791da040189ada1b1ec15856557b939ffcd978b"><code>chore:
remove unbalanced curly brace from `.editorconfig`
(tailwindlabs#19730)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/e86edee0918107e4e41e908fe59c937b83f00d4e"><code>refactor:
Consolidate Config helpers (tailwindlabs#19675)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/07c1a7e839ec61bd706c651428606ea5955b2bb0"><code>feat:
add `allowRegexCharacters` to `no-useless-escape`
(tailwindlabs#19705)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/cf3635299e09570b7472286f25dacd8ab24e0517"><code>chore:
remove shared types (tailwindlabs#19718)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/f60f2764971a33e252be13e560dccf21f554dbf1"><code>refactor:
Easier RuleContext creation (tailwindlabs#19709)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/71317ebeaf1c542114e4fcda99ee26115d8e4a27"><code>docs:
Update README</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/de1b5deba069f770140f3a7dba2702c1016dcc2a"><code>fix:
correct `service` property name in `Linter.ESLintParseResult` type
(tailwindlabs#19713)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/58a171e8f0dcc1e599ac22bf8c386abacdbee424"><code>chore:
update dependency @eslint/plugin-kit to ^0.3.1 (tailwindlabs#19712)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/3a075a29cfb43ef08711c2e433fb6f218855886d"><code>chore:
update dependency @eslint/core to ^0.14.0 (tailwindlabs#19715)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/60c3e2cf9256f3676b7934e26ff178aaf19c9e97"><code>fix:
sort keys in eslint-suppressions.json to avoid git churn
(tailwindlabs#19711)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/4c289e685e6cf87331f4b1e6afe34a4feb8e6cc8"><code>docs:
Update README</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/9da90ca3c163adb23a9cc52421f59dedfce34fc9"><code>fix:
add `allowReserved` to `Linter.ParserOptions` type
(tailwindlabs#19710)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/7bc6c71ca350fa37531291e1d704be6ed408c5dc"><code>feat:
add no-unassigned-vars rule (tailwindlabs#19618)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/ee4036429758cdaf7f77c52f1c2b74b5a2bb7b66"><code>feat:
convert no-array-constructor suggestions to autofixes
(tailwindlabs#19621)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/fbb8be9256dc7613fa0b87e87974714284b78a94"><code>fix:
add `info` to `ESLint.DeprecatedRuleUse` type (tailwindlabs#19701)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/f0f0d46ab2f87e439642abd84b6948b447b66349"><code>docs:
clarify that unused suppressions cause non-zero exit code
(tailwindlabs#19698)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/44bac9d15c4e0ca099d0b0d85e601f3b55d4e167"><code>ci:
run tests in Node.js 24 (tailwindlabs#19702)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/32957cde72196c7e41741db311786d881c1613a1"><code>feat:
support TS syntax in `max-params` (tailwindlabs#19557)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/35304dd2b0d8a4b640b9a25ae27ebdcb5e124cde"><code>chore:
add missing `funding` field to packages (tailwindlabs#19684)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/8ed32734cc22988173f99fd0703d50f94c60feb8"><code>docs:
fix internal usages of `ConfigData` type (tailwindlabs#19688)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/f305beb82c51215ad48c5c860f02be1b34bcce32"><code>test:
mock `process.emitWarning` to prevent output disruption
(tailwindlabs#19687)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/eb316a83a49347ab47ae965ff95f81dd620d074c"><code>docs:
add `fmt` and `check` sections to `Package.json Conventions`
(tailwindlabs#19686)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/a3a255924866b94ef8d604e91636547600edec56"><code>docs:
fix wording in Combine Configs (tailwindlabs#19685)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/c8d17e11dc63909e693eaed5b5ccc50e698ac3b3"><code>docs:
Update README</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/8bbabc4691d97733a422180c71eba6c097b35475"><code>9.26.0</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/16f5ff799122737e2c4b853b441e86f224878942"><code>Build:
changelog update for 9.26.0</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/5b247c859f1b653297a9b9135d92a59742a669cc"><code>chore:
upgrade to `@eslint/js@9.26.0` (tailwindlabs#19681)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/d6fa4ac031c2fe24fb778e84940393fbda3ddf77"><code>chore:
package.json update for @eslint/js release</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/e9754e7433edf665602ceba4f7f8fbca559c974f"><code>feat:
add reportGlobalThis to no-shadow-restricted-names
(tailwindlabs#19670)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/0fa2b7a3666f1eedcc091446dc860037c9bafa5c"><code>feat:
add suggestions for `eqeqeq` rule (tailwindlabs#19640)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/dd98d63f09c9324124734206d904d31d433a7c92"><code>docs:
Update README</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/96e84de55ad17c96e5b6f2dece75145542505469"><code>fix:
check cache file existence before deletion (tailwindlabs#19648)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/c25e858d2d7e9bd3e53dcb32c9af5251d6f0569e"><code>docs:
Update README</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/09586905be394c05839996a5ea812adfac44d320"><code>chore:
disambiguate internal types `LanguageOptions` and `Rule`
(tailwindlabs#19669)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/dcbdcc9c6be628240269b41f7bb576dde1e6f5b3"><code>feat:
Add MCP server (tailwindlabs#19592)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/b2397e9bef5ca7faf7e100ecebc20e457bf0b588"><code>docs:
Update README</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/d683aebc8e0792e4f80bd1488c705c90f22c317e"><code>fix:
don&tailwindlabs#39;t crash on tests with circular references in `RuleTester`
(tailwindlabs#19664)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/f1c858e3c1e9712ef398588bf5ed68bc19fad3f2"><code>chore:
fix internal type references to `Plugin` and `Rule`
(tailwindlabs#19665)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/9736d5d15870c9185da7d140becb9a15aa69057d"><code>fix:
add `namespace` to `Plugin.meta` type (tailwindlabs#19661)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/40dd2998cedddb75e0514b2c5cc855293c85da41"><code>refactor:
One-shot ESQuery selector analysis (tailwindlabs#19652)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/addd0a6a62d1b89dc7ab49cbd08c5a6af3e7da29"><code>docs:
fix formatting of unordered lists in Markdown (tailwindlabs#19660)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/a21b38db0276ab3373c95ebc7b1ef1910b79dfe6"><code>docs:
Update README</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/c0721a7f34264da0a32ade8432511eeda4a2c1b9"><code>docs:
fix double space in command (tailwindlabs#19657)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/1cfd7024226cd9c42ceb75732f79e3bc36e8305c"><code>chore:
update dependency @eslint/json to ^0.12.0 (tailwindlabs#19656)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/2dfd83ef4ee054f748732581c422508c45d6f1bf"><code>feat:
add `ignoreDirectives` option in `no-unused-expressions`
(tailwindlabs#19645)</code></a></li>
<li><a
href="https://github.com/eslint/eslint/commit/17bae69e02fff6f26487a3cbd9c3c3218088949c"><code>fix:
update `RuleTester.run()` type (tailwindlabs#19634)</code></a></li>
</ul>
</details>












---
![Depfu
Status](https://depfu.com/badges/edd6acd35d74c8d41cbb540c30442adf/stats.svg)

[Depfu](https://depfu.com) will automatically keep this PR
conflict-free, as long as you don't add any commits to this branch
yourself. You can also trigger a rebase manually by commenting with
`@depfu rebase`.

<details><summary>All Depfu comment commands</summary>
<blockquote><dl>
<dt>@​depfu rebase</dt><dd>Rebases against your default branch and
redoes this update</dd>
<dt>@​depfu recreate</dt><dd>Recreates this PR, overwriting any edits
that you've made to it</dd>
<dt>@​depfu merge</dt><dd>Merges this PR once your tests are passing and
conflicts are resolved</dd>
<dt>@​depfu cancel merge</dt><dd>Cancels automatic merging of this
PR</dd>
<dt>@​depfu close</dt><dd>Closes this PR and deletes the branch</dd>
<dt>@​depfu reopen</dt><dd>Restores the branch and reopens this PR (if
it's closed)</dd>
<dt>@​depfu pause</dt><dd>Ignores all future updates for this dependency
and closes this PR</dd>
<dt>@​depfu pause [minor|major]</dt><dd>Ignores all future minor/major
updates for this dependency and closes this PR</dd>
<dt>@​depfu resume</dt><dd>Future versions of this dependency will
create PRs again (leaves this PR as is)</dd>
</dl></blockquote>
</details>

Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

plugin does not work correctly with oxlint

3 participants