Commit Graph

59 Commits

Author SHA1 Message Date
overlookmotel b7416d06a9 refactor(linter/plugins): remove deprecated exports from index entry point (#18828)
Remove re-exports of `definePlugin`, `defineRule` and `RuleTester` from main `index` entry point of `oxlint` package.

We want this to be cheap:

```js
import { defineConfig } from "oxlint";
```

But with the deprecated exports, this would eagerly load ~20,000 lines of JS plugins-related code. See `index.js` in `dist` directory before and after this PR in release build. It was the import from `./lint.js` which was massive.

Personally, I don't think we need to keep the deprecated exports, for 3 reasons:

1. We have very clearly said that JS plugins are experimental and that breaking changes are very possible.
2. Given how broken our types are at present, probably few people were using `definePlugin` and `defineRule` anyway!
3. The fix for users who do get broken is trivial:

```diff
- import { definePlugin } from "oxlint";
+ import { definePlugin } from "@oxlint/plugins";
```
2026-02-03 15:46:17 +00:00
camc314 19b4df79f2 feat(oxlint): introduce defineConfig helper (#18596) 2026-02-01 23:27:17 +00:00
overlookmotel ab4fdddc61 refactor(linter/plugins): rename oxlint/rule-tester to oxlint/plugins-dev (#18793)
Part of #18610.

Our intent is to move `RuleTester` to a separate package `@oxlint/plugins-dev`.

So rename the current entry point which exports `RuleTester` from `oxlint/rule-tester` to `oxlint/plugins-dev`.
2026-02-01 13:23:14 +00:00
overlookmotel 9de9385fd1 refactor(linter/plugins): rename oxlint/plugin to oxlint/plugins (#18792)
Part of #18610.

Our intent is to move `definePlugin`, `defineRule`, and `eslintCompatPlugin` to a separate package `@oxlint/plugins` (plural to avoid confusion with naming convention for plugins `oxlint-plugin-xxx`).

So rename the current entry point which exports these functions from `oxlint/plugin` to `oxlint/plugins` (plural).
2026-02-01 13:23:14 +00:00
camc314 81eb6fa9db refactor(linter): split entry points for custom plugins/config/types (#18601)
This is technically breaking (only for types), but custom plugins are experimental.

The ruletester/defineRule/definePlugin are re-exported but with a `@deprecated` tag which I think is a good enough trade off

I want to avoid users bringing in a ton of custom plugin code when doing `import { defineConfig } from 'oxlint'`
2026-01-27 00:58:08 +00:00
overlookmotel 5cc7c907d6 feat(linter/plugins): pass env from Rust to JS (#17291)
Pass `env` to JS plugins. JS plugins don't do anything with it yet, except allow the user to see it. But they will in a following PR.
2025-12-23 10:53:25 +00:00
overlookmotel f3d439ff42 refactor(linter/plugins): move RuleOptionsSchema type into options.ts (#16901)
Pure refactor. Move the type def for `RuleOptionsSchema` into `options.ts`, which is a more appropriate place for it.
2025-12-15 13:06:01 +00:00
overlookmotel e654c134b1 feat(linter/plugins)!: do not export NodeOrToken type (#16806)
Unnecessary to export this type, as it's just an alias for `Node | Token | Comment`. Remove it.
2025-12-13 22:55:02 +00:00
overlookmotel f814c82292 fix(linter/plugins)!: use TypeScript parser directly for getting tokens (#16805)
Previously we used `@typescript-eslint/typescript-estree` parser to tokenize a source text. `@typescript-eslint/typescript-estree` uses TypeScript parser internally, and then builds the tokens array by traversing TypeScript's AST.

Instead, use TypeScript parser directly, and copy the code from TS-ESLint for converting tokens from TypeScript's format to TS-ESTree format.

### Why?

This has a few advantages:

1. Remove a dependency.
2. TS-ESLint's tokens did not have `start` or `end` properties, which we want for consistency with our AST. By reimplementing ourselves, we can add those properties to tokens.
3. TS-ESLint does a bunch of other work besides compiling the tokens array - it converts the AST too. We discard the AST that TS-ESLint produces, so this work is pointless. By cutting out TS-ESLint, we also cut out that unnecessary work, and do only what we need to - tokenizing.
4. TS-ESLint would throw an error if TypeScript produces any errors (even non-fatal ones), and there is no option to tell TS-ESLint not to do that. This caused a bunch of conformance tests to fail, because ESLint's tests include invalid code.
5. Can add workarounds for TypeScript bugs - e.g. not being able to parse JSX closing fragments containing line breaks (`<>hello<\n/>`).

### Implementation detail

We no longer need to get comments from TypeScript. We use the comments produced by Oxc parser instead.

### `CommentToken` type

A side effect is that there's no longer any need for separate `Comment` and `CommentToken` types - as we use our own comments now. So this PR removes `CommentToken` type.

Have marked this as a breaking change due to removal of this type.
2025-12-13 22:55:01 +00:00
overlookmotel f7751cca90 fix(linter/plugins): fix TS types for RuleTester (#16546)
Fix TS types for `RuleTester`. Merging the `RuleTester` class and namespace was working OK within the repo, but not in the built bundle. Fix that by defining the namespace in same file as the class.
2025-12-05 14:58:38 +00:00
overlookmotel 3a49158a30 fix(linter/plugins): improve TS types for tokens (#16502)
Improve the type def for `Token`.

Previously `Token` included `CommentToken`. This wasn't quite right because the `tokens` property of `Program` is `Token[]`, but it doesn't include comments.

So make `Token` the type for just code tokens, and use `Token | CommentToken` everywhere where the value can be either a code token or a comment token.

Also, remove the `BlockCommentToken` and `LineCommentToken` types. It seems unnecessarily detailed. `CommentToken` now covers both with `type: "Line" | "Block"`.
2025-12-04 16:39:55 +00:00
overlookmotel 3f3d646280 refactor(linter/plugins): imports use .ts extension (#16390)
Pure refactor. Use `.ts` extension when importing `.ts` files.
2025-12-02 14:08:49 +00:00
overlookmotel 5da1a6326d feat(linter/plugins): introduce RuleTester (#16206)
Part of #16018.

Implement `RuleTester` class for testing rules. Has same API as ESLint's `RuleTester`.

A few features not implemented yet:

* Fixes are not executed, or checked against provided `output`.
* Suggestions are not checked (we don't yet have support for suggestions in JS plugins).
* No use of config provided through `RuleTester` class constructor or via `RuleTester.setDefaultConfig`.
2025-11-30 23:51:14 +00:00
overlookmotel 9de386dc9f refactor(linter/plugins): move placeholder replacement into own function (#16302)
Pure refactor. Move placeholder replacement into it's own function, so it can also be used by rule tester (#16206).
2025-11-30 23:51:13 +00:00
overlookmotel c4b65f86c1 refactor(linter/plugins): move definePlugin and defineRule into own file (#16169)
Pure refactor. Move `definePlugin` and `defineRule` into their own module. Rule tester will live in this directory too.
2025-11-26 21:33:37 +00:00
overlookmotel fa3b070026 refactor(linter/plugins): prepare for rule options (#16158)
Preparatory work for supporting rule options.

Store options for all rules in an `allOptions` array. Currently this array only contains a single value for empty options, but in future we'll populate it with options extracted from configs, sent over from Rust side.

Am adding this now, before full implementation (#14825), as it's required for rule tester.
2025-11-26 12:23:27 +00:00
Boshen 523a30a016 style(all): apply oxfmt default options (#16091)
Embrace the defaults!

---------

Co-authored-by: Yuji Sugiura <y.sugiura.0316@gmail.com>
2025-11-25 15:56:24 +09:00
overlookmotel 2bd3cb66f7 fix(apps, editors, napi): fix oxlint-disable comments (#16014)
Fix a few `oxlint-disable` comments. 2 were unnecessary, the other was malformed.
2025-11-23 23:19:34 +00:00
overlookmotel ae137042f5 refactor(linter/plugins): rename assertion functions (#15968)
Rename assertion functions to clarify that they're not "real" assertions which we can rely on for validating input, and are only for compile-time testing or debug assertions.

`typeAssertIs` is not a great name, but I couldn't come up with anything better. `debugAssertIs` would be not be accurate, as it'd imply that in debug builds in performs an actual assertion, which it doesn't.

In any case, we should replace `typeAssertIs` with specialized assertion functions which do actually perform runtime assertions in debug builds (e.g. `typeAssertIs<Fix>()` -> `debugAssertIsFix()`).
2025-11-22 15:54:17 +00:00
Arsh 986cac1899 feat(linter/plugins): Token-related SourceCode APIs (TS ESLint implementation) (#15861)
- "Slow but working" implementation for
https://github.com/oxc-project/oxc/issues/14829#issuecomment-3512679496
- 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
- [x] `getTokens`
- [x] Move token types to `src-js/plugins/tokens.ts`
- [x] Bundle typescript, handling `_filename` usage in ES modules.
- [x] ~[Prevent class name mangling by
`tsdown`](https://github.com/oxc-project/oxc/pull/15861#discussion_r2542816947).
Could be deferred.~
https://github.com/oxc-project/oxc/pull/15906#pullrequestreview-3487271352
- [x] [Test regex-divide ambiguous
syntax](https://github.com/oxc-project/oxc/pull/15861#discussion_r2542919409).
- [x] Test for conformance directly against `eslint` tests.
- [ ] [Parse as JSX only if source text is
JSX](https://github.com/oxc-project/oxc/pull/15861#discussion_r2542850640).
Could be deferred.
- [ ] Lazy load the parser on demand. Could be deferred.

### Future work
- [ ] `getFirstToken`
- [ ] `getFirstTokens`
- [ ] `getLastToken`
- [ ] `getLastTokens`
- [ ] `getTokenBefore`
- [ ] `getTokenOrCommentBefore`
- [ ] `getTokensBefore`
- [ ] `getTokenAfter`
- [ ] `getTokenOrCommentAfter`
- [ ] `getTokensAfter`
- [ ] `getTokensBetween`
- [ ] `getFirstTokenBetween`
- [ ] `getFirstTokensBetween`
- [ ] `getLastTokenBetween`
- [ ] `getLastTokensBetween`
- [ ] `getTokenByRangeStart`
- [ ] `isSpaceBetween`
- [ ] `isSpaceBetweenTokens`

### Decisions
- [x] `@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.**
- [x] 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?
- **[overlookmotel and
camc314](https://github.com/oxc-project/oxc/pull/15861#issuecomment-3552651295):
yes.**
- [x] `Program` range is different between ESLint and TS-ESLint. Which
one should we follow?
-
**[overlookmotel](https://github.com/oxc-project/oxc/pull/15861#discussion_r2547915407):
they are going to both change and align soon, we should update our range
calculation to match when they do.**

---------

Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
2025-11-21 17:39:02 +00:00
overlookmotel 512e43a204 refactor(linter/plugins): move assertions into own file (#15945)
Pure refactor. Split up `plugins/utils.ts` into 3 files in `utils` directory.

Most importantly, assertion functions are by themselves in an `asserts.ts` file. This paves the way for the next PR (#15946).
2025-11-21 14:42:25 +00:00
overlookmotel a72b093b05 refactor(linter/plugins): enable TS strictNullChecks (#15886)
Adapted from #15868. Enable `strictNullChecks` in TSConfig for `apps/oxlint`.

Rather than using `!` all over the place, I've added a function `assertIsNonNull`, which performs the same function. Idea is that we can later on produce a debug build where `assertIsNonNull` does an actual runtime assertion (rather than just a TS `asserts`) that the value is not `null` or `undefined`. We can use the debug build in tests, and catch any logic errors.

Unfortunately, TypeScript doesn't seem able to track side effects, so there seems to be no way to tell it "after calling `initAst()`, `ast` is always non-null". Don't want to switch to a having a `getAst` function which returns a non-null value, as that'd introduce unnecessary temp vars, and function calls, on hot paths. So this seems to be the best we can do without incurring runtime cost.
2025-11-20 15:52:49 +00:00
overlookmotel e2a0997911 perf(linter/plugins): recycle empty visitor object in ESLint compat mode (#15693)
Small perf optimization. In ESLint compat mode, `create` returns an empty visitor object when `before` hook returns `false`. Reuse the same object, instead of creating a new one each time.
2025-11-14 12:55:21 +00:00
overlookmotel 2eec3f5daf refactor(linter/plugins): improve types for options (#15696)
Similar to #15695. `options` is serialized from JSON, so can only contain a limited set of values.
2025-11-14 12:48:25 +00:00
overlookmotel 53bf16ad61 refactor(linter/plugins): improve types for settings (#15695)
Improve types for `settings`. `settings` is deserialized from JSON, so can only contain a limited set of values.
2025-11-14 12:48:25 +00:00
overlookmotel 22cb1f0977 refactor(linter/plugins): add type annotation (#15694)
Add a type annotation where TS was inferring `any`.
2025-11-14 12:06:45 +00:00
overlookmotel 197f074436 refactor(linter/plugins): improve Diagnostic + Suggestion types (#15643)
Improve `Diagnostic` and `Suggestion` types, by using `RequireAtLeastOne` utility type from `type-fest` package, to enforce that:

* Either `message` or `messageId` property must be provided.
* Either `node` or `loc` property must be provided.

Change to TSDown config `dts: { resolve: true }` is get it to bundle `type-fest` into the `.d.ts` file.
2025-11-12 22:19:42 +00:00
overlookmotel 24d00f42d0 fix(linter/plugins): add types for suggested fixes (#15636)
Add `Suggestion` type, and `suggest` field to `Diagnostic`.

Suggestions are not implemented yet, but this completes the interface for diagnostics defined by ESLint's docs.
2025-11-12 15:15:59 +00:00
overlookmotel d2ff4baab2 refactor(linter/plugins): move report function and diagnostic types to separate file (#15635)
Pure refactor. Move `report` function and `Diagnostic` types into a separate file. `context.ts` was getting too long.
2025-11-12 15:15:58 +00:00
overlookmotel c4abee47e1 refactor(linter/plugins): move location-related types into location.ts (#15634)
Pure refactor.

Move TS type defs for location-related types into `location.ts`. `types.ts` is too crowded.

Also add JSDoc comments to these type defs.
2025-11-12 13:59:37 +00:00
overlookmotel 257360f94e fix(linter/plugins): fill in TS type def for RuleMeta (#15629)
Fill out the TS type def for `RuleMeta`, based on [ESLint's docs](https://eslint.org/docs/latest/extend/custom-rules).

Most of the properties can be ignored at runtime in Oxlint (they're primarily useful for generating docs), but this has also surfaced a few features we need to implement. These are marked with "TODO" comments, and I'll also open issues for them shortly.
2025-11-12 13:22:14 +00:00
overlookmotel 287a538ec8 feat(linter/plugins): implement Context#get* deprecated methods (#15606)
Implement 4 methods on `Context` which are present in ESLint, but deprecated.

* `getFilename`
* `physicalFilename`
* `getCwd`
* `getSourceCode`

After some discussion, @camc314 and I concluded that we should add these methods despite their deprecation. The reasons behind that decision are documented in a comment in this PR.
2025-11-12 09:32:17 +00:00
overlookmotel 3aabfac770 docs(linter/plugins): alter comments on FILE_CONTEXT used in ESLint-compat Context shim (#15605)
Change to comments only. Remove comments from methods in `Context` shim for ESLint compatibility mode. Refer to the main implementation instead.
2025-11-11 17:55:47 +00:00
overlookmotel 2de35be346 refactor(linter/plugins): add comments on Context#extend (#15604)
Add comments on `extend` methods explaining why they're OK to use in `createContext`.
2025-11-11 17:55:47 +00:00
overlookmotel 731e906177 refactor(linter/plugins): improve internal types (#15487)
Pure refactor. Improve internal types, and silence an invalid lint warning.
2025-11-08 15:00:40 +00:00
overlookmotel 6957fb9a39 fix(linter/plugins): do not allow access to Context#id in createOnce (#15489)
Revert #15447. It's easy to allow `Context#id` to be accessed in `createOnce` when running rule in Oxlint, but not possible to do in ESLint compat mode with current API, because `id` isn't a property of the rule, so `defineRule` doesn't know it.

We could support it by making `definePlugin` compulsory for ESLint interop, and get it to set `id` property on `Context` objects properly.

But that's a bit tricky, so just disable access for now. It's probably not very useful anyway - a rule should know what its own name is!
2025-11-08 14:46:46 +00:00
overlookmotel 740963051d fix(linter/plugins): allow access to cwd in createOnce in ESLint interop mode (#15488)
#15301 made `Context#cwd` available in `createOnce`. Make it available in ESLint compat mode too. Ditto the `extend` method added in #15477.

Define all other `Context` methods/getters on prototype of `Context` in ESLint compat mode, for completeness.
2025-11-08 14:46:45 +00:00
overlookmotel 2f9735dcd8 feat(linter/plugins): implement context.languageOptions (#15486)
Implement `Context#languageOptions`.
2025-11-08 14:18:54 +00:00
leaysgur a8e5181ab7 chore(infra): dogfooding oxfmt (#14979)
Fixes #14803

- Added `oxfmt` and `oxfmtrc.jsonc`
- Remove `dprint` plugin and useless ignore paths
- Apply `dprint` again
- Apply `oxfmt`
2025-10-28 01:16:46 +00:00
overlookmotel cd068aab76 refactor(linter/plugins): move token-related SourceCode methods into separate file (#14770)
Pure refactor. Move methods of `SourceCode` related to tokens into a separate file, to make main `source_code.js` file less crowded.
2025-10-19 12:16:19 +00:00
overlookmotel 5e99ed3c43 refactor(linter/plugins): allow nullish values as message or messageId (#14422)
It makes the types a bit shit, but so far we accept `null` or `undefined` for all properties, and treat them the same as the property being absent. Extend this convention to `message` and `messageId` properties.

Also split out getting the message from a diagnostic into a separate function, to make this pattern simpler via early return.
2025-10-07 19:25:34 +00:00
camc314 529e88ee47 feat(linter/plugins): support messageIds (#14415) 2025-10-07 17:11:29 +00:00
overlookmotel a2914fe8d8 feat(linter/plugins): add loc field getter to all AST nodes (#14355)
Add `loc` to all AST nodes.

`loc` is computed lazily, as it's quite an expensive operation - because it requires searching the source text for line breaks and building a translation table.

We can consider making `loc` a "real" property once we have a faster translation table built on Rust side.
2025-10-05 14:02:12 +00:00
overlookmotel 9a902c0a41 fix(linter/plugins): make range field non-optional on AST types (#14354)
Alter AST types exported from `oxlint` to make the `range` field non-optional - it's always present on AST nodes in Oxlint's AST.
2025-10-05 13:47:32 +00:00
overlookmotel 93d81641b2 refactor(linter/plugins): export AST types direct from oxlint package (#14353)
Export AST types directly from `oxlint` package, instead of depending on `@oxc-project/types`. This enables altering the types to reflect that `range` field is always present on AST node in Oxlint's AST (#14354).
2025-10-05 13:47:32 +00:00
overlookmotel 5505a86121 feat(linter/plugins): include range field in AST (#14321)
Add `range` field to AST nodes, using the support for `range` in raw transfer added in #14319.

Use `range` field rather than `start` + `end` in nodes passed to `context.report` and fixer methods - for closer alignment with ESLint.
2025-10-04 15:47:47 +00:00
overlookmotel 1347de43ea feat(linter/plugins): accept diagnostics with loc (#14304)
Allow calling `context.report` with `loc` instead of `node`.
2025-10-02 17:00:34 +00:00
overlookmotel 65873ba277 refactor(linter/plugins): add stubs for all SourceCode methods (#14285)
Add stub implementations for all methods and properties of `SourceCode`. All unimplemented methods/getters just throw an error.
2025-10-02 08:32:03 +00:00
overlookmotel bdf901033e feat(linter/plugins): add SourceCode API (#14281)
Add support for ESLint's `SourceCode` API. At present the only method on `SourceCode` is `getText`. More methods will be added in further PRs to follow.
2025-10-01 16:36:28 +00:00
overlookmotel 989ce2f552 refactor(linter/plugins): convert Node type to interface (#14280)
Make `Node` type an `interface Node` instead of `type Node`. We can then use it in more places.
2025-10-01 15:32:10 +00:00