Skip to content

fix(linter/plugins): make spreading Comment instances keep loc property#22238

Merged
overlookmotel merged 13 commits into
oxc-project:mainfrom
KuSh:test/unicorn-expiring-todo-comments-jsplugin
Jun 6, 2026
Merged

fix(linter/plugins): make spreading Comment instances keep loc property#22238
overlookmotel merged 13 commits into
oxc-project:mainfrom
KuSh:test/unicorn-expiring-todo-comments-jsplugin

Conversation

@KuSh

@KuSh KuSh commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary: Fixing unicorn/expiring-todo-comments via jsPlugin

Problem

The unicorn/expiring-todo-comments rule failed when run through oxlint's jsPlugin system with error:
TypeError: Either `node` or `loc` is required

The rule spreads Comment instances ({...comment}) to process each line of multi-line comments, but lost the loc property because:

  • Comment's loc was a prototype getter (not an own enumerable property)
  • Spread only copies own enumerable properties

Solution

Define loc as an own accessor property on every Comment instance using
__defineGetter__. Own properties are visible to spread, JSON.stringify, and
for…in iteration, so all three work without any extra workarounds.

The getter function is defined in the class static block and captured in a const
(getCommentLoc). Using a const rather than a let lets V8 skip reassignment checks
at the defineGetter(this, "loc", getCommentLoc) call site — a pattern already
established in the codebase for resetCommentLoc.

As a result:

  • toJSON() is removed — JSON.stringify now works via the own property directly.
  • Object.defineProperty(Comment.prototype, "loc", { enumerable: true }) is removed —
    no longer needed.

Test Results

  • ✅ unicorn/expiring-todo-comments rule works (detects expired TODOs)
  • ✅ updated comments fixture pass
  • ✅ All untouched existing tests pass

@KuSh KuSh requested review from camc314 and overlookmotel as code owners May 7, 2026 21:44
@KuSh KuSh force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch from bf5ebe9 to 14e310b Compare May 7, 2026 21:48
@camc314 camc314 added A-linter Area - Linter A-linter-plugins Area - Linter JS plugins labels May 8, 2026
@overlookmotel

Copy link
Copy Markdown
Member

Thanks for this.

Have you by any chance measured perf before/after this change? Unfortunately we don't have any JS benchmarks at present, so we measure on a rather ad hoc basis, e.g. running Oxlint with some JS plugins on VS Code repo.

I'm asking because generally we've tried to avoid Map / WeakMap and proxies as much as possible, because of the relatively poor performance characteristics.

@KuSh

KuSh commented May 10, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for this.

Have you by any chance measured perf before/after this change? Unfortunately we don't have any JS benchmarks at present, so we measure on a rather ad hoc basis, e.g. running Oxlint with some JS plugins on VS Code repo.

I'm asking because generally we've tried to avoid Map / WeakMap and proxies as much as possible, because of the relatively poor performance characteristics.

No, but if the procedure is written somewhere I could do that. I've tried to find another way but couldn't come up with another one, but will be willing to try if you have some in mind.

Do you know some jsPlugins that exercise comments? unicorn doesn't work without that change so I can't compare with before.

@KuSh KuSh force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch from 14e310b to d6eb543 Compare May 25, 2026 18:35
@KuSh

KuSh commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

@overlookmotel did bench the branch with eslint-comments (thanks HansAuger on discord for the suggestion) and a custom plugin to specifically stress loc access:

Bench Results

Benchmarked on VS Code repo (~10,500 files, 24 threads), 5 runs with 2 warmup each using hyperfine.

stress-loc (10× .loc access per comment — amplifies overhead)

Version Mean ± σ vs #loc
#loc (before) 24.989s ± 1.261s
Proxy (after) 25.385s ± 1.232s +0.396s (+1.6%)

eslint-comments (7 real rules — no-use, disable-enable-pair, etc.)

Version Mean ± σ vs #loc
#loc (before) 15.204s ± 0.658s
Proxy (after) 15.652s ± 0.583s +0.448s (+2.9%)

Verdict

No measurable regression. Both differences are within noise (standard deviations overlap). The Proxy overhead on .loc access (which has no get trap — delegates to target) is negligible in practice.

The fix is safe to land from a performance standpoint.

Configuration used:

Node v24.14.0
oxc @ 5774052

stress-loc

import type { Comment, Plugin, Rule } from "#oxlint/plugins";
const stressLocRule: Rule = {
  create(context) {
    const { sourceCode } = context;
    let programNode: any;
    let totalComments = 0;
    return {
      Program(node) {
        programNode = node;
        const comments = sourceCode.getAllComments();
        for (const comment of comments) {
          totalComments++;
          // 10 iterations per comment to amplify any overhead signal.
          for (let i = 0; i < 10; i++) {
            const loc = comment.loc;
            const _startLine = loc.start.line;
            const _startColumn = loc.start.column;
            const _endLine = loc.end.line;
            const _endColumn = loc.end.column;
          }
          const _type = comment.type;
          const _value = comment.value;
          const _range = comment.range;
        }
      },
      "Program:exit"() {
        if (totalComments > 0 && programNode) {
          context.report({ node: programNode, message: `processed ${totalComments} comments` });
        }
      },
    };
  },
};
const plugin: Plugin = {
  meta: { name: "comment-perf" },
  rules: { "stress-loc": stressLocRule },
};
export default plugin;

Configured with:

{
  "jsPlugins": ["./test/fixtures/comment_perf/plugin.ts"],
  "rules": {
    "comment-perf/stress-loc": "error"
  }
}

eslint-comments

Installed as devDependencies at ^4.7.1, which resolved to package version 4.7.1 (latest).
Configured with:

{
  "jsPlugins": [{ "name": "eslint-comments", "specifier": "@eslint-community/eslint-plugin-eslint-comments" }],
  "rules": {
    "eslint-comments/disable-enable-pair": "error",
    "eslint-comments/no-duplicate-disable": "error",
    "eslint-comments/no-unlimited-disable": "error",
    "eslint-comments/no-restricted-disable": "error",
    "eslint-comments/no-use": "error",
    "eslint-comments/require-description": "error",
    "eslint-comments/no-aggregating-enable": "error"
    // Skipped no-unused-disable (heavy patching, likely incompatible).
  }
}

@KuSh KuSh force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch from d6eb543 to a261679 Compare May 26, 2026 11:29
@overlookmotel

overlookmotel commented May 27, 2026

Copy link
Copy Markdown
Member

If you have time, could you try altering the stress rule to this:

const stressLocRule: Rule = {
  create(context) {
    const { sourceCode } = context;

    return {
      Program(node) {
        const comments = sourceCode.getAllComments();
        let count = 0;

        // 10 iterations per comment to amplify any overhead signal
        for (let i = 0; i < 10; i++) {
          for (const comment of comments) {
            const loc = comment.loc;
            count += loc.start.line;
            count += loc.start.column;
            count += loc.end.line;
            count += loc.end.column;
            count += comment.type.length;
            count += comment.value.length;
            count += comment.range[0];
            count += comment.range[1];
          }
        }

        if (count > 0) {
          context.report({ node, message: `got ${count} count` });
        }
      },
    };
  },
};

In your original version, possible that that const loc = comment.loc; line is hoisted out of the inner for loop by V8 - or indeed that it optimizes out most of the code as no-op.

count here is a meaningless number, but as it's acted on later, none of the code can be removed as no-op.

Also, if I've understood Proxy correctly, it has a perf impact on accessing other fields too (e.g. comment.value) so ideally we want to repeat those accesses 10x too.

@KuSh

KuSh commented May 28, 2026

Copy link
Copy Markdown
Contributor Author

Microbenchmark Results

source: https://gist.github.com/KuSh/7af1829312429dd6e638a47de430a86a

No reset (steady state after warmup)

Variant Direct vs C Spread vs C
C:proto 0.262 1.0× 2.155 1.0×
B:modvar 0.975 3.7× 2.689 1.25×
E:static# 0.949 3.6× 2.705 1.26×
SR:selfRp 0.993 3.8× 2.682 1.24×
D:delloc 1.200 4.6× 2.945 1.37×
A:proxy 3.057 11.7× 107.145 49.7×

With reset (per file cycle)

Variant Direct vs C Spread vs C
C:proto 0.963 1.0× 2.325 1.0×
B:modvar 0.878 0.9× 2.892 1.24×
E:static# 0.919 1.0× 2.915 1.25×
D:delloc 1.039 1.1× 2.933 1.26×
SR:selfRp 2.216 2.3× 29.627 12.7×
A:proxy 3.353 3.5× 108.550 46.7×

Conclusions

Without reset, SR ≈ B/E — all three within noise. After warmup, SR's self-replaced data properties are as fast as B/E's getter + #loc path. All ~3.7× vs C.

With reset, SR collapsesObject.defineProperty on every reset + every first access makes it 2.3× on direct access and 12.7× on spread vs C. B/E stay competitive at 0.9-1.0× on direct access (computeLoc dominates, making all variants similar) and 1.24-1.25× on spread.

B/E are the only viable approach — correct spread behavior, cheap reset (#loc = null), competitive performance.

@KuSh

KuSh commented May 29, 2026

Copy link
Copy Markdown
Contributor Author

Complete Benchmark Results

Benchmark Proxy A Variant B Speedup
5000-comment stress file (single file, 1 rule) 229.7 ± 11.5 ms 197.7 ± 8.7 ms 1.16× (16%)
VS Code src/ (10 rules × 10 iterations, whole tree) 21.073 ± 0.149 s 20.159 ± 0.204 s 1.045× (4.5%)

The stress file isolates just the loc access overhead (no file I/O or parsing noise) — Proxy is 16% slower there. On the full VS Code tree, the Proxy overhead is diluted but still measurable at ~0.9s saved per run (4.5%) — real-world improvement with no downsides.

@overlookmotel

Copy link
Copy Markdown
Member

Excellent work! Thanks loads for digging deep into this.

I have to say I'm mystified why "B:modvar" is performing better than "C:proto" with reset.

As I mentioned on Discord, I'm mostly away from keyboard until next week. I'll take a closer look as soon as I'm back in the saddle. Again, thanks loads for getting into this in depth.

For the record, more context here: https://discord.com/channels/1079625926024900739/1080712072012238858/threads/1508788046110396476

@KuSh KuSh force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch 2 times, most recently from e527b4d to ef640fb Compare May 29, 2026 01:47
@overlookmotel overlookmotel force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch from ef640fb to e89fee8 Compare June 1, 2026 13:26
@overlookmotel

Copy link
Copy Markdown
Member

I'm going through this now. Have moved the docs corrections to #22891, since they're orthogonal to this PR (so deleted last commit from this PR), and rebased on main. Hope you don't mind me fiddling with your work.

@KuSh

KuSh commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

No problem at all! Thanks for that

@overlookmotel

Copy link
Copy Markdown
Member

OK, I've dug into this.

Firstly, there was a problem with the benchmark. If you changed the order of the variants, the results changed. Claude's explanation:

Why the original benchmark was misleading: inline-cache pollution

All variants were measured through a single shared access function (testDirectAccess) in one
process. But each variant is a different V8 hidden class (map), and the property loads in that
function (comment.loc, .type, .range, …) are backed by inline caches keyed on the map. Feeding
one function four different maps drives those caches monomorphic → polymorphic → megamorphic — and
the Proxy variant poisons them completely. Worse, the "no-reset" pass ran all variants through
the function before the "with-reset" pass measured anything, so the later numbers were dominated by
megamorphic dispatch rather than the loc strategy under test.

That produced a first-mover advantage: whichever variant ran first got a clean monomorphic cache
during its own measurement (until the next variant's warmup deoptimized the function), so it looked
fastest. Reordering the variants changed the ranking — the tell-tale sign the harness, not the code,
was being measured.

Fix: run each variant in its own child process. Each process only ever sees one Comment class,
so the access function stays monomorphic — which also matches production, where there's only ever one
Comment class. Results are then order-independent.

Adapted version which fixes this: https://gist.github.com/overlookmotel/9687d97e8decbb08787e03cc2e888bdb

Results

variant construct min construct median reset min reset median no-reset min no-reset median with-reset min with-reset median
C:proto 0.0209 0.0218 0.0035 0.0142 0.2140 0.2177 0.2324 0.2371
B:modvar 0.2715 0.2963 0.0034 0.0144 0.2136 0.2181 0.2313 0.2378
D:defGet 0.1970 0.2143 0.0034 0.0143 0.2138 0.2190 0.2314 0.2355
SR:selfRp 0.2625 0.2911 0.3195 0.3242 0.2678 0.2738 1.6973 1.7249
A:proxy 0.0544 0.0549 0.0001 0.0001 2.6068 2.6385 2.7924 2.9478

construct is ms to build a pool of 5000; reset / no-reset / with-reset are ms/file.

Run on Mac Mini M4 Pro.

Upshot

  • Fastest version which has the desired { ...comment } behavior is much slower to construct new Comment instances than what we have now.
  • But the field access and reset times are unchanged.
  • In fact, once the code is tiered up to TurboFan optimizing compiler (which it will be if comments are touched enough to make it matter), the generated assembly for loc field access is identical to current version.
  • Proxy + Map is faster on construction and reset, but loses badly on field access, which is the hotter path (I switched to Map as WeakMap doesn't have a clear method, and all the keys are long-lived pooled objects anyway so the "weakness" doesn't buy anything).

"B:modvar" is 13.5x slower to construct a Comment. I managed to trim that down a bit in variant "D:defGet" by using Object.prototype.__defineGetter__ instead of Object.defineProperty, but it's still 10x slower than what we have at present.

However, that doesn't matter too much! As Comment instances are pooled, and re-used over and over, Comments only get created while linting the first few files, and thereafter we only hit the fast path.

So, in short, I think we can do this!

Next steps

Code

I think variant D is the winner. That's IMO what we should go with in this PR.

Note: The weird pattern with copying the functions defined in the static block into consts is intentional. It allows V8 to optimize better - the fact that LOC_GETTER_D is a const allows it to skip checks in defineGetter(this, "loc", LOC_GETTER_D) because it knows it can't be reassigned.

If I may ask, please try to avoid any extraneous changes from the previous versions surviving. Small diff is the name of the game, and AI doesn't seem to always get that.

Tests

Could you please reduce the test case to not require eslint-plugin-unicorn as a dependency? It can just reproduce the pattern that plugin requires, without the plugin itself.

Or testing the spread behavior could probably be folded into one of the existing fixtures that tests comments, rather than introducing a new fixture.

Tokens

We should probably apply the same change to Token. That has the same object pool pattern, and the same problem with spread.

@KuSh If you can be bothered, that'd be great (in a separate PR). Otherwise no worries, I'll do it.

Note: Unfortunately we should not apply this change to AST nodes too. AST node objects are not pooled, so the increased construction cost would be a killer. We'll have to find another way to tackle that.

Store Locations in a Map?

On assumption that loc field is rarely accessed, maybe it'd be a good idea to store loc objects in a Map<Comment, Location> (borrowing from your original approach). Advantages:

  1. Remove #loc field -> each Comment is 8 bytes smaller.
  2. Much faster reset. Just map.clear().

However, this makes accessing loc more expensive. Whether it's overall a positive/negative depends on how many comments have their loc accessed. I don't know how we know what's the common behavior. Maybe my assumption that it's relatively rare is completely wrong!

Anyway, that'd be best left to a separate PR.


Just to say again, thanks very much for bringing this up and chasing down the perf impact. Really appreciate your dogged determination!

graphite-app Bot pushed a commit that referenced this pull request Jun 2, 2026
#22919)

Micro-optimization.`Token` and `Comment` classes create functions defined in static blocks to reset the value of `#loc` private property on class instances. Copy these functions into `const` vars, to avoid checks at these functions' call sites. Previously they were stored in `let` bindings, which incurs a check at each call site (because the value might have been re-assigned). The difference in Turbofan-optimized code is just an 8-byte load and a comparison, but why keep that when we can lose it?

Discovered the perf difference while digging into #22238.
graphite-app Bot pushed a commit that referenced this pull request Jun 3, 2026
…#22928)

Oxlint's JS-side code is processed by a TSDown plugin that transforms global property accesses to `const`s imported from `globals.ts`. e.g.:

```js
function foo(obj) {
  return Object.keys(obj);
}
```

is transformed to:

```js
import { ObjectKeys } from "./utils/globals.ts";

function foo(obj) {
  return ObjectKeys(obj);
}
```

The purpose of this transform is:

1\. Better perf - avoid a property lookup/check on each call.
2\. More robust in the face of user code (plugins) altering globals e.g. `Object.keys = function myWeirdFunction() {}`.

While delving into the assembly that V8 produces while reviewing #22238, I discovered that in fact the transform was having a _negative_ effect on perf, because TSDown puts the global `const`s in a separate chunk `global.js`. Because the consts are in a different module, this actually makes them _more_ costly to access, rather than less.

In fact, it turns out that TurboFan doesn't insert a check at all in the original code - it instead has machinery to trigger de-opt if the global is reassigned. So the original perf rationale for this transform was completely misguided - it doesn't in fact produce a perf gain at all! We still want to keep it for the robustness advantage, but we don't want it to be perf _loss_, as it was before this PR.

This PR fixes the perf by instead defining consts inline in each file. The example above is now transformed to:

```js
const ObjectKeys = Object.keys;

function foo(obj) {
  return ObjectKeys(obj);
}
```

This also removes the need to manually maintain the `globals.ts` file. It's now automatic.

Additionally, improve the transform in a few minor ways:

1. Support deeper nested global props e.g. `Object.prototype.toString`.
2. Skip converting e.g. `Array.prototype.slice.call(arguments)` (because `.call` requires `this`, so `ArrayPrototypeSliceCall(arguments)` is incorrect).
3. Do replace methods which take `this` (e.g. `Promise.resolve`) when they're not used in a method call.

Note: The robustness advantage of this transform may appear to be marginal, and not worth the bother of having this transform at all. Personally, I think it's worthwhile. Oxlint JS-side code has access to a slice of Rust's memory as a `Uint8Array`. Writing to that buffer could cause UB, reading/writing out of bounds, or other serious problems on Rust side, so IMO it's critical to protect against that in every way we can.
@KuSh KuSh force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch from d27809d to 89b0ef2 Compare June 3, 2026 19:40
@KuSh

KuSh commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

@overlookmotel done, I'll create another PR for Token

Comment thread apps/oxlint/src-js/plugins/comments.ts Outdated
Comment thread apps/oxlint/test/fixtures/comments/plugin.ts Outdated
KuSh and others added 7 commits June 6, 2026 00:12
…lugin

Adds a test fixture to exercise the jsPlugin infrastructure with the
real eslint-plugin-unicorn package. This test exposes a bug where
context.report({ loc: ..., messageId: ... }) fails with 'Either node
or loc is required'.

Changes:
- Add eslint-plugin-unicorn as dev dependency to apps/oxlint
- Add test fixture at test/fixtures/unicorn_expiring_todo_comments/
- Use alias 'unicorn-js' since 'unicorn' is reserved for native rules
- Replace private #loc field with WeakMap storage
- Add Proxy in constructor to make loc enumerable for spread
- Fix unicorn/expiring-todo-comments rule failing with spread comments
Replace Proxy (ownKeys/getOwnPropertyDescriptor traps) with Object.defineProperty + #loc private field for Comment.loc.

Variant B: static #LOC_DESC shared getter via Object.defineProperty in constructor,

Benchmarks (VS Code src/, 10 rules x 10 iterations):
  Proxy  (A): 21.07s
  Variant B:   20.16s  (4.5% faster)
…-plugin-unicorn dep and integrate spread test in the comment fixture
@KuSh KuSh force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch from aa7fe1e to 12fada5 Compare June 5, 2026 22:12
@KuSh KuSh force-pushed the test/unicorn-expiring-todo-comments-jsplugin branch from 12fada5 to ef3b353 Compare June 5, 2026 22:23
@KuSh KuSh requested a review from overlookmotel June 5, 2026 22:29

@overlookmotel overlookmotel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you!

I pushed a few trivial commits - set field order back to what it was before, added comments, and simplified the tests.

The feedback I gave about the tests before was completely wrong! Usually we report not assert, but in this case it seems we diverge from that rule. Sorry I wasted your time with that.

Hope you don't mind me tweaking. Keen to get this merged!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes oxlint’s jsPlugin interoperability for rules (notably unicorn/expiring-todo-comments) that clone comment objects via object spread, by ensuring Comment.loc is preserved as an own enumerable property.

Changes:

  • Defines loc as an own accessor property on each Comment instance via __defineGetter__, while still caching the computed value in #loc.
  • Removes the toJSON() workaround and prototype-level loc enumerability tweak, since serialization/spread now work through the instance property.
  • Extends the comments plugin fixture to assert { ...comment } includes loc and preserves object identity.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
apps/oxlint/src-js/plugins/comments.ts Changes Comment.loc to be an own enumerable accessor so spreading/serialization keep loc, while retaining lazy caching/reset behavior.
apps/oxlint/test/fixtures/comments/plugin.ts Updates fixture assertions to validate loc survives object spread cloning.

Comment thread apps/oxlint/src-js/plugins/comments.ts Outdated
Comment thread apps/oxlint/src-js/plugins/comments.ts Outdated
@overlookmotel overlookmotel changed the title fix(linter/plugins): make spreading Comment instances keep loc property fix(linter/plugins): make spreading Comment instances keep loc property Jun 6, 2026
@overlookmotel overlookmotel merged commit 27de044 into oxc-project:main Jun 6, 2026
28 checks passed
overlookmotel added a commit that referenced this pull request Jun 6, 2026
…erty (#22947)

Follow-up of #22238, same logic applied to `Token` class

## Problem
  
`Token` instances are object-pooled and reused across files, similar to
`Comment`.
Their `loc` property is defined as a **prototype getter**, which means
it is not an own
property and is therefore invisible to the spread operator:

```js
const spread = { ...token };
"loc" in spread; // false ❌
```

This silently drops `loc` whenever a JS plugin (or rule) spreads a token
— e.g. when
building a diagnostic from `{ ...token, message: "…" }`.

`JSON.stringify` had the same problem and was patched with a `toJSON()`
workaround.
A separate `Object.defineProperty(Token.prototype, "loc", { enumerable:
true })` call
was used to make `loc` show up in `for…in` iteration, but it still
didn't fix spread.

##  Fix

Define `loc` as an own accessor property on every `Token` instance using
`__defineGetter__`. Own properties are visible to spread,
`JSON.stringify,` and
`for…in` iteration, so all three work without any extra workarounds.

The getter function is defined in the class static block and captured in
a `const`
(`getTokenLoc`). Using a `const` rather than a `let` lets V8 skip
reassignment checks
at the `defineGetter(this, "loc", getTokenLoc)` call site — a pattern
already
established in the codebase for `resetLoc`.

As a result:
- `toJSON()` is removed — `JSON.stringify` now works via the own
property directly.
- `Object.defineProperty(Token.prototype, "loc", { enumerable: true })`
is removed —
no longer needed.

##  Test

A spread assertion is added to the existing `tokens` fixture:

```js
const spread = { ...firstToken };
assert("loc" in spread);           // was false before this fix
assert.deepEqual(spread.loc, firstToken.loc);
```

---------

Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
@KuSh KuSh deleted the test/unicorn-expiring-todo-comments-jsplugin branch June 6, 2026 13:16
@KuSh

KuSh commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Thanks a lot for the support @overlookmotel, glad it is finally merged.

Boshen added a commit that referenced this pull request Jun 8, 2026
# Oxlint
### 🚀 Features

- e805174 linter: Add schema for `jest/vitest/max-expects` (#23105)
(Sysix)
- 7850577 linter: Add schema for `jest/vitest/expect-expect` (#23104)
(Sysix)
- 75f641a linter: Add schema for `jest/vitest/consistent-test-it`
(#23103) (Sysix)
- 5125f89 linter/unicorn: Support no-null `checkArguments` option
(#23098) (camc314)
- b8b9797 linter: Add schema for `import-max-dependencies` (#23096)
(Sysix)
- 65cb47a linter/eslint: Support no-unused-expressions
`ignoreDirectives` option (#23097) (camc314)
- f6c36d5 linter: Add schema for `import/prefer-default-export` (#23091)
(Sysix)
- 0d4a5d1 linter: Add schema for `eslint/sort-vars` (#23090) (Sysix)
- fdb5bf5 linter: Add schema for `eslint/radix` (#23082) (Sysix)
- 05b4dcf linter: Add schema for `eslint/prefer-const` (#23081) (Sysix)
- 5a06c4d linter/vue: Implement next-tick-style rule (#23041) (Alex
Peshkov)
- e38a36a linter: Add schema for `eslint/operator-assignment` (#23080)
(Sysix)
- 907cee7 linter: Add schema for `eslint/no-warning-comments` (#23075)
(Sysix)
- 9470bb2 linter: Add schema for `eslint/no-unused-vars` (#23073)
(Sysix)
- 234b5cf linter: Add schema for `eslint/no-shadow` (#23072) (Sysix)
- de0dd8b linter: Add schema for `eslint/no-restricted-exports` (#23020)
(Sysix)
- faa3e0d linter: Add schema for `eslint/no-param-reassign` (#23018)
(Sysix)
- dbc9c27 linter: Add schema for `eslint/no-magic-numbers` (#23017)
(Sysix)
- 38d3569 linter: Add schema for `eslint/no-inner-declarations` (#23016)
(Sysix)
- 008fa41 linter: Add schema for `eslint/no-constant-condition` (#22991)
(Sysix)
- ca44623 linter: Add schema for `eslint/no-empty-function` (#22988)
(Sysix)
- 43eb04d linter: Add schema for `eslint/id-match` (#22987) (Sysix)
- a800f27 linter: Add schema for `eslint/capitalized-comments` (#22984)
(Sysix)
- 96e2d32 linter: Add schema for `eslint/id-length` (#22963) (Sysix)
- 545493f linter: Add schema for `eslint/complexity` (#22960) (Sysix)
- 5f0b558 linter: Add schema for `eslint/class-methods-use-this`
(#22959) (Sysix)
- 719b720 linter: Add schema for simple rule configurations (#22948)
(Sysix)
- fd00966 linter: Add right schema for `eslint/max-*` rules (#22923)
(Sysix)
- 1226d78 linter: Fill schema with rule configurations (#22907) (Sysix)
- 8f423c1 linter/vue: Implement `require-direct-export` rule (#17623)
(yefan)
- 78e915b linter/vue: Implement no-reserved-props rule (#22914) (bab)
- 0f200a9 linter/vue: Implement require-prop-types rule (#22083) (Alex
Peshkov)
- 5da9da9 linter/vue: Implement no-reserved-keys rule (#21780) (bab)
- 75e14a8 linter/vue: Implement prop-name-casing rule (#22892) (bab)
- 85efabf semantic: Make building the class table optional, off by
default (#22862) (Boshen)

### 🐛 Bug Fixes

- a49b0cf linter/no-map-spread: Remove ineffective autofix (#22956)
(camc314)
- cf53285 parser: Report reserved type-declaration names in the parser
(#23035) (Boshen)
- 0383e61 linter: Fix schema for rules without a config (#22946) (Sysix)
- 4d722e0 parser: Report duplicate switch `default` clause in the parser
(#23012) (Boshen)
- 6cb34b8 linter/plugins: Make spreading `Token` instances keep `loc`
property (#22947) (Nicolas Le Cam)
- 27de044 linter/plugins: Make spreading `Comment` instances keep `loc`
property (#22238) (Nicolas Le Cam)
- 742fd0b linter/double-comparisons: Make fixer a suggestion (#22968)
(camc314)
- 93f4494 linter: Respect default child config plugin when extending
parent config (#22903) (Sysix)
- 594ed86 linter: Deny unknown options for some rules (#22924) (Sysix)
- 3253038 linter/expect-expect: Align default rule options (#22890)
(camc314)
- bbe44ea linter: Respect default plugins from extended config (#22896)
(Sysix)

### ⚡ Performance

- 0b7ce7e linter/plugins: Create global prop vars at top level of
modules (#22928) (overlookmotel)
- 0f7c319 linter/plugins: Define class `#loc` setter functions as
`const`s (#22919) (overlookmotel)

### 📚 Documentation

- 7b0380d linter: Remove preserve-caught-error note (#22994) (camc314)
- dadafe3 oxlint, oxfmt: Mention migrate skills in npm READMEs (#22965)
(Boshen)
# Oxfmt
### 🚀 Features

- 3da77e0 oxfmt: Format `parser:json5` files by `oxc_formatter_json`
(#22990) (leaysgur)
- c786f0d oxfmt: Format `parser:jsonc` files by `oxc_formatter_json`
(#22913) (leaysgur)
- 27a6db8 formatter_json: Implement jsonc variant (#22912) (leaysgur)

### 🐛 Bug Fixes

- 2aedd52 oxfmt: Avoid JS promise rejects for all TSFN call sites
(#23107) (leaysgur)
- 01e0871 formatter,formatter_json: Handle PS/LS as line terminator
(#22978) (leaysgur)
- 23902d9 formatter_json: Handle CR only line breaks (#22977) (leaysgur)
- 136b72b formatter_json: Use line_suffix for line comment outside array
(#22931) (leaysgur)
- 44e40fa formatter_json: Expand line comment inside array (#22911)
(leaysgur)
- 2c86896 formatter_json: Avoid example binary name collision (#22904)
(camc314)

### 📚 Documentation

- cc69d8d formatter_json: Update AGENTS.md (#22981) (leaysgur)
- 0490721 formatter_json: Update AGENTS.md (#22976) (leaysgur)
- dadafe3 oxlint, oxfmt: Mention migrate skills in npm READMEs (#22965)
(Boshen)
- f88961a oxfmt: Annotate each config option with supported languages
(#22953) (leaysgur)
- 7e514bf formatter_json: Update AGENTS.md (#22930) (leaysgur)

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-linter Area - Linter A-linter-plugins Area - Linter JS plugins

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants