Skip to content

Conversation

@lilnasy
Copy link
Contributor

@lilnasy lilnasy commented Nov 19, 2025

  • "Slow but working" implementation for Linter plugins: Token-related SourceCode APIs #14829 (comment)
  • Uses the parser from @typescript-eslint/typescript-estree for tokens. The source code is fully parsed and everything but the tokens and comments is discarded.
  • Tests are directly adapted from eslint.
  • Direct commits from maintainers welcome.

Tasks

Future work

Decisions

  • @typescript-eslint/typescript-estree peer-depends on typescript. How should we package it? Currently, typescript is being made an optional dependency.
    • overlookmotel (on discord): ideally bundled, direct runtime dependency otherwise.
  • Deprecated methods are being removed altogether in ESLint 10: getTokenOrCommentBefore, getTokenOrCommentAfter, and isSpaceBetweenTokens. These are surface level deprecations: the functionality was merged with other methods (the includeComments: true option) and plugins can migrate with a one line change. I'm guessing we are targeting fairly modern, actively developed projects. Should we expose them?
  • Program range is different between ESLint and TS-ESLint. Which one should we follow?
    • overlookmotel: they are going to both change and align soon, we should update our range calculation to match when they do.

@github-actions github-actions bot added A-linter Area - Linter A-cli Area - CLI A-linter-plugins Area - Linter JS plugins labels Nov 19, 2025
@lilnasy lilnasy changed the title Feat/linter/plugins/token methods feat(linter/plugins): Token-related SourceCode APIs (TS ESLint implementation) Nov 19, 2025
@github-actions github-actions bot added the C-enhancement Category - New feature or request label Nov 19, 2025
@lilnasy lilnasy force-pushed the feat/linter/plugins/token-methods branch 2 times, most recently from 3e3a5bb to dd03ef0 Compare November 19, 2025 12:35
@overlookmotel
Copy link
Member

overlookmotel commented Nov 19, 2025

Deprecated methods are being removed altogether in ESLint 10: getTokenOrCommentBefore, getTokenOrCommentAfter, and isSpaceBetweenTokens. These are surface level deprecations: the functionality was merged with other methods (the includeComments: true option) and plugins can migrate with a one line change.

Cameron and I had a bit of an argument about exactly this question!

We concluded in the end to keep all the deprecated methods, to maximize compatibility with older plugins, which may take some time to get updated (or in some cases, will never get updated). Our rationale is that most of these methods are just aliases, so no maintenance burden to keep them. The only one which is slightly different from its non-deprecated "brother" is isSpaceBetweenTokens, and that's pretty simple to implement - it just treats JSXText differently (if I remember right).

I'm guessing we are targeting fairly modern, actively developed projects.

That's true, but even actively developed projects may use old unmaintained plugins.

Note: I added the stubs in #15645. That PR also added tests which illustrate the difference in behavior between isSpaceBetween and isSpaceBetweenTokens.

Copy link
Member

@overlookmotel overlookmotel left a comment

Choose a reason for hiding this comment

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

Possibly premature review, as it's still marked as draft, but I'm keen to get it merged so I thought I'd go through it now.

Apart from the comments below, this looks good to me. Sorry for the volume of comments - most are pretty small details.

Once we're happy with getTokens impl, I think we should merge this, and we can add more methods in separate PRs. No need to do the whole API in a single PR.

A couple of points which we can also leave to follow-ups:

  1. We should ideally lazy-load @typescript-eslint/typescript-estree package only when getTokens is first called.

  2. I assume TS-ESLint's parser also generates a ScopeManager. If it does, we may as well cache it, to avoid running scope analysis again if plugin does sourceCode.getTokens() followed by getting sourceCode.scopeManager.

@lilnasy
Copy link
Contributor Author

lilnasy commented Nov 20, 2025

  1. I assume TS-ESLint's parser also generates a ScopeManager. If it does, we may as well cache it, to avoid running scope analysis again if plugin does sourceCode.getTokens() followed by getting sourceCode.scopeManager.

@typescript-eslint/parser does. @typescript-eslint/typescript-estree does not.

The former delegates to the latter for parsing and lexing. I decided to use @typescript-eslint/typescript-estree directly to instantiate scope managers and parsers granularly.

@lilnasy lilnasy force-pushed the feat/linter/plugins/token-methods branch from faf3560 to de1f77e Compare November 20, 2025 12:21
graphite-app bot pushed a commit that referenced this pull request Nov 20, 2025
…pshot (#15906)

Contents of this test snapshot depends on the constructor names of scope class instances. This is a little fragile, as they can change depending on how the code is bundled (see #15861 (comment)).

At some point, we'll full minify the bundle, at which point class names will be come random gibberish like `e`, `t`, `aZ`, etc.

Avoid this problem by outputting `type` field of scope objects instead.
@overlookmotel
Copy link
Member

The former delegates to the latter for parsing and lexing. I decided to use @typescript-eslint/typescript-estree directly to instantiate scope managers and parsers granularly.

Ah ha that makes perfect sense. I had misunderstood.

@lilnasy lilnasy force-pushed the feat/linter/plugins/token-methods branch 6 times, most recently from e23e179 to 173e5a9 Compare November 20, 2025 20:05
@lilnasy lilnasy marked this pull request as ready for review November 20, 2025 21:03
@lilnasy lilnasy requested a review from camc314 as a code owner November 20, 2025 21:03
Copilot AI review requested due to automatic review settings November 20, 2025 21:03
@lilnasy lilnasy force-pushed the feat/linter/plugins/token-methods branch from 9fb3811 to ab159d9 Compare November 20, 2025 21:04
overlookmotel added a commit that referenced this pull request Nov 24, 2025
# Oxlint
### 💥 BREAKING CHANGES

- cbb27fd ast: [**BREAKING**] Add `TSGlobalDeclaration` type (#15712)
(overlookmotel)

### 🚀 Features

- 72660f7 linter: Support auto generate config document for tuple lint
option (#15904) (Duc Nghiem Xuan)
- 0c1f82b linter/plugins: Add `tokens` property to `Program` (#16020)
(overlookmotel)
- 9e61beb linter/plugins: Implement `SourceCode#getFirstToken()`
(#16002) (Arsh)
- 9a548dd linter/plugins: Implement `SourceCode#getLastTokens()`
(#16000) (Arsh)
- 0b6cb11 linter/plugins: Implement `SourceCode#getFirstTokens()`
(#15976) (Arsh)
- 166781e linter/plugins: Implement `SourceCode#getTokenAfter()`
(#15973) (Arsh)
- 6ae232b linter: Expose type errors via tsgolint (#15917) (camc314)
- 2bfdd26 linter/plugins: Implement `SourceCode#getTokensAfter()`
(#15971) (Arsh)
- 45fffc1 linter/plugins: Implement `SourceCode#getTokenBefore()`
(#15956) (Arsh)
- 776e473 linter/plugins: Implement `SourceCode#getTokensBefore()`
(#15955) (Arsh)
- 595867a oxlint: Generate markdownDescription fields for oxlint JSON
schema. (#15959) (connorshea)
- 5569317 vscode: Add quick actions to status bar tooltip (#15962)
(Sysix)
- 986cac1 linter/plugins: Token-related `SourceCode` APIs (TS ESLint
implementation) (#15861) (Arsh)
- a21f9e4 linter: Implement unicorn/prefer-bigint-literals rule (#15923)
(Michiel Vrins)
- 4b9d8d2 linter/type-aware: Include range with tsconfig diagnostics
(#15916) (camc314)
- 220d01e editor: Improve the status bar item for the VS Code extension
by adding a tooltip. (#15819) (connorshea)

### 🐛 Bug Fixes

- 2bd3cb6 apps, editors, napi: Fix `oxlint-disable` comments (#16014)
(overlookmotel)
- 81f5360 linter/prefer-number-properties: Get fixer to replace entire
call expr (#15979) (camc314)
- e4ba07f language_server: Always write to memory file system (#15975)
(Sysix)
- a8a2032 linter: Support missing `range` for internal diagnostics
(#15964) (camc314)
- 619a226 oxlint/lsp: Don't register `textDocument/formatting`
capability (#15882) (Sysix)
- 6ab1a20 linter: Fix no_useless_spread producing invalid syntax when
removing empty object spreads (#15905) (camc314)
- be4b6df linter: Unicorn/prefer-string-replace-all incorrectly escapes
backslashes (#15901) (camc314)
- 9fa9ef2 linter: Gracefully fail when using import plugin, large file
counf and JS plugins (#15864) (camc314)
- c027398 linter/plugins: Correct bindings package names glob in TSDown
config (#15871) (overlookmotel)
- b622ef8 linter: Fix `oxc/bad_array_method_on_arguments` rule behavior.
(#15854) (connorshea)
- aa06c3f linter: Recognize NewExpression as value context in
no-unused-private-class-members (#15843) (camc314)
- e89c5ba typescript/prefer-namespace-keyword: Skip nested
`TSModuleDeclaration`s (#15806) (overlookmotel)
- 646d020 linter/exhaustive-dependencies: Prevent is_callee_of_call_expr
flag from leaking into nested expressions (#15832) (camc314)
- 46bd6bd linter/plugins: Pin `@typescript-eslint/scope-manager`
dependency (#15807) (overlookmotel)
- fba31fa linter: Patch `@typescript-eslint/scope manager` (#15214)
(Arsh)
- 50307c1 linter/jest: Ignore `expect` identifier in argument position
(#15785) (camc314)

### ⚡ Performance

- 024b48a linter/plugins: Lazy-load tokens parsing code (#16011)
(overlookmotel)
- 15365c9 linter/plugins: Reduce var assignments (#15953)
(overlookmotel)
- 84d1f4f linter/plugins: Downgrade some checks to debug-only (#15922)
(overlookmotel)
- a49f704 linter/typescript: Avoid searching source text unless required
(#15805) (overlookmotel)

### 📚 Documentation

- ceffa5a linter: Add config option docs for various rules. (#16024)
(connorshea)
- 9a0ed13 linter: Fix config option docs for eslint/operator-assignment
rule. (#16030) (connorshea)
- 0b18005 linter: Add config docs generation for rules with Regex
arguments (#15978) (connorshea)
- 48d18e0 linter: Improve diagnostic message for promise/catch-or-return
rule (#15980) (connorshea)
- 6c72e84 linter: Use backticks for code elements across more rule
diagnostics (#15958) (connorshea)
- a63dad7 linter/plugins: Add comment (#15952) (overlookmotel)
- 81ea642 vscode: Use markdownDescription for better formatting in
VSCode Settings (#15889) (connorshea)
- db6a110 linter/plugins: Fix JSDoc comment (#15884) (overlookmotel)
- 1487271 linter: Add config option docs for `jsdoc/require-param` and
`jsdoc/require-returns` rules (#15857) (connorshea)
- fbf0fd4 linter/plugins: Add JSDoc comments to `Plugin` and `Rule`
types (#15815) (overlookmotel)
- ac5e4b5 linter/plugins: Add JSDoc comments and improve comments
(#15814) (overlookmotel)
- 9b7b083 linter: Fix error in `curly` `"all"` example (#15801)
(camc314)
- 65a3520 linter: Improve diagnostic for consistent-type-definitions
rule. (#15752) (connorshea)

### 🛡️ Security

- f9b9276 deps: Update dependency rolldown to v1.0.0-beta.51 (#15856)
(renovate[bot])
# Oxfmt
### 💥 BREAKING CHANGES

- a937890 formatter: [**BREAKING**] Default to `lineWidth: 100` (#15933)
(leaysgur)
- 03d5f5a formatter/sort-imports: [**BREAKING**] Change default order to
`natural` with `natord` crate (#15828) (leaysgur)
- cbb27fd ast: [**BREAKING**] Add `TSGlobalDeclaration` type (#15712)
(overlookmotel)

### 🚀 Features

- 7818e22 formatter/sort-imports: Support `options.groups` (#15831)
(leaysgur)
- f9a502c oxfmt: `oxfmt --lsp` support (#15765) (leaysgur)

### 🐛 Bug Fixes

- 4817486 formatter: Revert `FormatElement::BestFitting` printing logic
(#16028) (Dunqing)
- 5562dd6 formatter: Incorrect formatting method chain with trailing
comments (#16027) (Dunqing)
- 6d14c8b formatter: Comments in export class decorators are printing
incorrectly (#15897) (Dunqing)
- 683c764 formatter: Correct a few minor mismatched typescript tests
(#15894) (Dunqing)
- c11cc07 formatter: Improve formatting for default type on type
parameters (#15893) (Dunqing)
- 0bff596 formatter: Handle JSX expresssion dangling comment (#15890)
(leaysgur)
- 16a9dc8 formatter: Inconsistent printing of class extends and
interface extends (#15892) (Dunqing)
- 300b496 formatter: Inconsistent CallExpression and NewExpression
around member chain and logical expression (#15858) (Dunqing)

### ⚡ Performance

- 65174cc formatter: Reduce the size of `TextWidth` to 4 byte (#15827)
(Dunqing)
- 4fe3aac formatter: Use `ArenaVec` and `ArenaBox` (#15420) (Dunqing)

Co-authored-by: overlookmotel <557937+overlookmotel@users.noreply.github.com>
overlookmotel pushed a commit that referenced this pull request Nov 24, 2025
overlookmotel pushed a commit that referenced this pull request Nov 24, 2025
overlookmotel pushed a commit that referenced this pull request Nov 24, 2025
overlookmotel pushed a commit that referenced this pull request Nov 24, 2025
overlookmotel pushed a commit that referenced this pull request Nov 24, 2025
overlookmotel pushed a commit that referenced this pull request Nov 24, 2025
overlookmotel pushed a commit that referenced this pull request Nov 24, 2025
overlookmotel pushed a commit that referenced this pull request Nov 25, 2025
…6071)

- Follow up to #15861.
- Implements the performance optimization pointed out in #15952.
leaysgur pushed a commit that referenced this pull request Nov 26, 2025
…6071)

- Follow up to #15861.
- Implements the performance optimization pointed out in #15952.
overlookmotel added a commit that referenced this pull request Nov 27, 2025
…eenTokens()` using tokens (#16055)

- Follow up to #15498 and
#15861
- Note: JSX special case (as part of the `isSpaceBetweenTokens()`
method) is being removed in ESLint v10 (see
[eslint/eslint#20137](https://github.com/eslint/eslint/pull/20137/files#diff-e85eaebdcf6af45c06572019cbd900cff91518c789049c3bd0e8724c18645ab0))

### Tasks:
- [x] Token-based reimplementation of `isSpaceBetween()`
- [x] Token-based reimplementation of `isSpaceBetweenTokens()`
- [x] Pass existing tests from #15498
- [x] New tests for JS syntax
- [x] New tests for JSX syntax

---------

Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-cli Area - CLI A-linter Area - Linter A-linter-plugins Area - Linter JS plugins C-enhancement Category - New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants