This PR enables `strictIndexSignatures` option when generating `oxlint` config type declarations, allowing us to implement rule config schemas where the generation would produce a TS error.
See https://github.com/oxc-project/oxc/issues/23299 for the problem.
The generated code now appends `undefined` for some rule config options.
The docs of the option:
```
/**
* Append all index signatures with `| undefined` so that they are strictly typed.
*
* This is required to be compatible with `strictNullChecks`.
*/
strictIndexSignatures: boolean;
```
Closes#19746.
Add `oxlint-plugin-eslint` plugin which exports all ESLint's built-in rules as a JS plugin which can be used with Oxlint.
The code for rules is lazy-loaded on first use, so just using the plugin doesn't load the code for all the rules, only the ones which the user actually uses. However, the `meta` property of each rule is provided eagerly, so that `registerPlugin` in Oxlint can load the plugin without triggering the getters which lazy-load rule code.
All ESLint's rule code is bundled into this plugin package, so the package has no dependencies. This has a few advantages:
1. We control the version of ESLint which the rules are taken from.
2. Avoids "works on my machine" unreproducible bug reports due to differing ESLint versions.
3. Avoids relying on ESLint's `eslint/use-at-your-own-risk` export, which could change without warning.
At present, the plugin contains *all* ESLint's rules, including ones which are natively implemented in Oxlint.
We might choose to scale it back to include only the rules which Oxlint doesn't natively implement. But then we need to consider what happens when we implement more rules in future: Do they disappear from this package without warning? Would that be considered a breaking change? If it would, how long do we keep them in this package?
I propose that in the interests of getting this useful feature released without having to decide all these issues, we just go with it as is for now. JS plugins are not yet stable, so at present we can still make changes later on if we need to, without breaking any promises.
I think we may want to consider alternatively calling oxfmt in order to do this, rather than a bespoke solution here? That would also fix the formatting of typescript code blocks.
Right now, this also doesn't handle `jsonc` code blocks.
But for now, good enough.
Without this fix, the editor suggestions have docs like this:
<img width="976" height="441" alt="Screenshot 2026-03-02 at 8 46 34 PM" src="https://github.com/user-attachments/assets/243af8fb-a7d4-4954-a272-51dadfb9e488" />
AI Disclosure: Changeset generated by Claude Code.
#18824 added `@oxlint/plugins` package. The files in `npm/oxlint-plugins` were generated by the build script `apps/oxlint/scripts/build.ts`.
Instead, populate `npm/oxlint-plugins` in the release CI workflow, just before the package is published.
Alternative to #18796. Part of #18610.
## Overview
Add `@oxlint/plugins` package.
`@oxlint/plugins` package has no dependency on `oxlint`, which solves the problem raised in https://github.com/oxc-project/oxc/pull/18796#discussion_r2751286894, and is also what we wanted in the end anyway. We don't actually want plugins packages to have to depend on `oxlint`.
## Differences from other Oxc packages
This package is different from all our other packages, in that it's intended to work not only with Oxlint's ecosystem, but also with ESLint's ecosystem.
`@oxlint/plugins` will be a *runtime* dependency of plugin packages that the community create, and they may want their plugin to be compatible with both Oxlint and ESLint - this package provides the `eslintCompatPlugin` function for exactly this purpose.
Our aim is to allow plugin authors to migrate from ESLint's `create` API to Oxlint's `createOnce` API with minimum friction, and without breaking changes for their users:
1. Their plugin should remain compatible with ESLint.
2. They do not need to alter the versions of NodeJS they support (no change to `engines` field in `package.json`).
3. They can continue to publish their plugin as a CommonJS package, if that's the format they currently use.
## Package composition
Due to these requirements, the make-up of `@oxlint/plugins` is different from our other packages:
### Dual Format
This package is in "dual package" format, with both ESM and CommonJS entry points.
### Older NodeJS versions support
This package supports NodeJS versions back to v12.22.0.
https://github.com/oxc-project/oxc/blob/9938dc476f8533b7011af03cdbac328b0d00d344/npm/oxlint-plugins/package.json#L40-L42
This matches `engines` field in all ESLint 8.x.x releases:
* 8.0.0: https://www.npmjs.com/package/eslint/v/8.0.0?activeTab=code
* 8.57.1: https://www.npmjs.com/package/eslint/v/8.57.1?activeTab=code
Therefore, `@oxlint/plugins` can be used by any plugin which supports ESLint 8 or later.
Note: Download counts for ESLint 8 are still roughly equal to ESLint 9, even though v9 was released 2 years ago.
#### Notes
* Changing `target` in TSDown to `"node12"` does not result in any changes to built files. See 3rd commit in this PR.
* `exports` field in `package.json` is supported (without experimental flag) from NodeJS v12.17.0 + v14.0.0.
* `.cjs` file extension supported from NodeJS v12.0.0.
## Building the package
This PR adds 2 extra builds to TSDown config, which generate the files for the package. `build.ts` script moves the files into `npm/oxlint-plugins` directory.
Later on, we'll move the logic for moving the files into the CI release workflow, so we don't need to check the generated build files into Git.
## `@oxlint/plugins-dev`
In #18610 we decided to also add an `@oxlint/plugins-dev` to contain `RuleTester`, so we can move that out of `oxlint` package. This PR *does not* add that package. I've skipped that for now as (a) it's trickier and (b) it's much less important.
#15921 added a debug build for Oxlint, and various PRs since have tweaked it. However, I now realize there's little point in generating *both* a release build and debug build.
Just create one build, and use env var `DEBUG` to control whether it has debug assertions included in it or not.
`pnpm run build` produces release build with no debug assertions, `pnpm run build-test` includes the asserts. I've verified that both do what they're meant to.
#15946 means we can now simplify the TSDown build config.
Previously we had to have 2 separate builds for `index.ts` and `cli.ts`, to avoid `assert*` functions ending up in a shared chunk, and not getting removed by minifier. Now that problem is solved, so we can switch to a single build with 2 entry points.
TSDown does create a small extra chunk for its `__toESM` and `__commonJSMin` functions, but I think that's fine.
Annoyingly it doesn't seem to be possible to tell TSDown to generate `.d.ts` files only for the `index.js` chunk, so have to delete this pointless files in build script.
The point of all this is that `RuleTester` needs to use a ton of code from `plugins.ts`, and we wouldn't want all that code duplicated in both `index` and `plugins` chunks (especially once we're bundling TS-ESlint's parser #15861). Now that code will be shared between the two entry points in a shared chunk, not duplicated.
Generate a debug build, in addition to the normal release build.
In the debug build, `DEBUG` global is defined as `true` and `assertIsNonNull` does a runtime assertion. In release build, `DEBUG === false` and minifier removes debug-only code.
Run the tests in debug mode.
Debug build is generated in a separate directory from `dist`, so is *not* included in NPM package.
Bundle the deserializer. Originally it wasn't bundled at my request, so that source maps wouldn't be required for debugging. But it's turning out that we have no problems in the deserializer - our problems are elsewhere!
Previously we lazy-loaded visitor keys file, because it would be rarely used. But it's required for selectors DSL, so statically import it, and include it in bundle.
Also, import it as a TS file, since it's valid TS, even without type annotations.
Pure refactor. Just rename generated `types.js` files to `type_ids.js`. This name better describes what the files contain, and it avoids naming clash with `types.d.ts` in `apps/oxlint/src-js/generated`.
Refactor. Previously build script for `oxlint` package copied files from `napi/parser`. Remove this dependency, by instead making `oxc_ast_tools` generate the files in `apps/oxlint/src-js/generated`, and then copy the files from there to `dist/generated` in the build script.
This ends up with the same result, but divorces `oxlint` package from `oxc-parser`, which is cleaner.
Refactor. Include generated files `types.js` and `walk.js` in bundle, instead of externalizing them. It's only the deserializer code that we want to keep unbundled to make it easier to debug without source maps. `types.js` and `walk.js` aren't too complicated.
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.
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).
We had a runtime `preserveParens` option in deserializers to cater for linter, but it was not used in parser. Convert it to a compile-time option instead.
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.
Now that `apps/oxlint` and `napi/parser` are both ESM packages (#13723 and #14042), we can use `.js` file extensions for all ESM files, rather than the mix of `.js` and `.mjs` file we had previously.
It's less confusing having to remember what's `.js` and what's `.mjs`, and avoids ugly workarounds like #14038 (`.d.mts`??).
the latest version of `oxc-parser` (on npm) does not have resolvable
types because the entry is `.mjs` and the types are `.d.ts` (these need
to match - `.d.mts`). you can see this in the failing CI for [this
renovate
PR](https://github.com/nuxt/nuxt/actions/runs/17940186619/job/51014702361?pr=33290)
but I'm happy to provide a more minimal reproduction if it helps
I also spotted that `napi/parser/test/visit.test.ts` was importing
`visitorKeys` which was not exported from the corresponding declaration,
so took the liberty to add that export as well.
let me know if this needs any changes 🙏
I hope it helps
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: overlookmotel <theoverlookmotel@gmail.com>
Use eager deserialization instead of lazy deserialization in linter plugins, so AST custom rules receive is ESTree-compatible.
Unfortunately this reduces performance significantly, but we'll bring back lazy deserialization later on once we have it producing an ESTree-compatible AST.
## Summary
- Convert npm/oxlint package to ES modules by setting `"type": "module"` in package.json
## Test plan
- [ ] Verify CLI binaries continue to work correctly
- [ ] Test package installation and execution
- [ ] Run existing test suites
- [ ] Confirm language server binary still functions
🤖 Generated with [Claude Code](https://claude.ai/code)
Pure refactor. Root of `napi/parser` was getting really crowded with tons of files, and I'm about to add a load more for the ESTree walker.
Move JS source files into a `src-js` directory, like in `apps/oxlint`, so that the root of the package is just config files and scripts.
Convert `oxlint` to a NAPI package.
This PR:
1. Moves files from `napi/oxlint` into `apps/oxlint`.
2. Converts `oxlint` into a NAPI package.
3. Alters the [release workflow](https://github.com/oxc-project/oxc/pull/13723/files#diff-80cb497c859bbaf7756da6ced2bca3d31933affc96102983eee1ad7d4bcc60fd) and [script](https://github.com/oxc-project/oxc/pull/13723/files#diff-4c4b7045c68805aeeae7c0af94cf7cf7487cb0403496da974a6a23accdc457d0) for building `oxlint` packages.
Most of necessary code modifications and refactoring was done in previous PRs (#13701, #13712, #13722, #13745), so this PR only includes changes necessary for packaging as NAPI.
I've done my best to update release workflow, but am mostly "flying blind" without ability to test it, so very likely there are mistakes.
A few notes and oddities:
* I've kept the `oxlint` NPM package as CommonJS. We could probably switch to ESM, but I thought it better to minimize the changes in this PR.
* NAPI-RS creates `.node` files named with a `-msvc` postfix for Windows. The scripts here remove that postfix so that filename of `.node` files and NPM package names match for all platforms, for simplicity.
* `tasks/website` has a dependency on `oxlint` as a normal library. This resulted in linker errors, so have had to put all the NAPI stuff behind `napi` cargo feature. Tests for `tasks/website` are run separately in CI, to avoid feature unification turning on the `napi` feature for `website`.
I've made a few comments below for the parts which aren't fairly self-explanatory.