Commit Graph

18 Commits

Author SHA1 Message Date
overlookmotel 0b7ce7edd9 perf(linter/plugins): create global prop vars at top level of modules (#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.
2026-06-03 00:53:31 +00:00
leaysgur 37d49fcd40 refactor(oxlint,oxfmt): Share src-js code via apps/shared (#22452)
New place to share code for use in `src-js`, similar to how `crates/oxc_config` works.

Initially, I considered creating an `apps/oxc_cli` and moving `crates/oxc_config` there. However, it felt unnatural deps for `crates/oxc_linter` -> `apps/oxc_cli`, so I decided to keep the Rust code as is for now and share only the JS code.

Since `apps/shared` exports `.ts` files directly, each app is responsible for bundling them via `@oxapps/shared`. Tests still live in `apps/shared`, so separate CI is also added and hooked by each app.

To start, I've only shared the function that reports TS incompatibility errors. This is just for verification purposes and will be fixed in a later PR.
2026-05-18 07:56:26 +00:00
overlookmotel 7a866138d1 perf(linter/plugins): use Int32Arrays for tokens and comments buffers (#21136)
Continuation of #21132. Use `Int32Array` view of the buffer for all operations related to tokens and comments.
2026-04-07 23:08:00 +00:00
overlookmotel 305350dbb7 docs(linter/plugins): correct comments (#21130)
Comments only change. Correct 2 erroneous comments.
2026-04-07 17:25:41 +00:00
camc314 27374e8f14 feat(linter): add hint about node version when ts config fail to import (#20570)
fixes #20467

I tested this locally:

```
Failed to parse oxlint configuration file.

  x Failed to load config: <fixture>/oxlint.config.ts
  |
  | TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for <fixture>/oxlint.config.ts
  |
  | TypeScript config files require Node.js ^20.19.0 || >=22.12.0.
  | Detected Node.js v22.11.0.
  | Please upgrade Node.js or use a JSON config file instead.
```
2026-03-25 20:13:29 +00:00
overlookmotel 30891bdd6f perf(linter/plugins): share empty Uint32Array across multiple places (#20509)
Tiny optimization. Various vars are initialized as an empty `Uint32Array`. Use a single empty array for all of them.
2026-03-21 13:02:27 +00:00
overlookmotel 0861d9a5c8 fix(linter/plugins): remove getters from Context (#20115)
Fix an incompatibility with `eslint-plugin-jsdoc`. It relies on properties of `context` being values rather than getters:

https://github.com/gajus/eslint-plugin-jsdoc/blob/26276d428631a2625575607cba7875dab6797091/src/buildForbidRuleDefinition.js#L86-L103

When `options` property is a getter, this code throws an error because it attempts to define a property with both `value` and `get` in its descriptor.

This PR replaces the `id` and `options` getters with value properties.

The getters were there to prevent the `Context` object from being mutated by rule code, and allow the object to be frozen, as it is in ESLint. But each rule gets its own `Context` object, so if it chooses to mutate it, it only harms itself, and can't impact other rules/plugins. In ESLint, trying to alter the frozen `Context` object would be a hard error, so it's doubtful that any ESLint plugins do this anyway.

This is no real downside to this change for us, so we might as well make the change, and fix `eslint-plugin-jsdoc` and any other plugins which may depend on ESLint's behavior here.
2026-03-08 17:27:52 +00:00
overlookmotel cfaa754ded refactor(linter/plugins): debug assert that values passed via NAPI are not undefined (#20070)
Follow-on after #19675.

The type for `lint` function that NAPI-RS produces is incorrect. `Option::None` on Rust side is translated to `null`, never `undefined`. Check this with a debug assertion, instead of handling `undefined` at runtime (which is pointless, because it never is `undefined`).
2026-03-06 17:39:15 +00:00
Sysix 87318e72fd fix(oxlint/lsp): load js config with reforcing fs read (#19551)
closes https://github.com/oxc-project/oxc/issues/19526

> This PR fixes a bug in the LSP where JavaScript/TypeScript config files were not being reloaded when changed. The fix implements cache-busting by appending a timestamp query parameter to file URLs during import, forcing Node.js to bypass its module cache.
2026-03-03 18:35:12 +00:00
overlookmotel 05ccf9f21e perf(linter/plugins): transfer tokens via raw transfer (#19893)
Transfer tokens via raw transfer in Oxlint JS plugins.

Deserializer is written by hand. `Token` is not a normal struct, so deserializer cannot be generated.
2026-03-01 21:47:15 +00:00
overlookmotel fe6546f24e refactor(linter/plugins): replace typeAssertIs with debug assertions (#18606)
Pure refactor. Replace `typeAssertIs` with debug assertions. Both perform the same type assertion, but difference is that the debug assertion actually checks that the type is correct at runtime in debug build.
2026-01-26 23:31:24 +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 f0d6b700aa refactor(linter/plugins): add message param to debugAssertIsNonNull (#17170)
Add `message` param to `debugAssertIsNonNull`, so you can provide a custom error message, same as `debugAssert`.
2025-12-20 13:59:11 +00:00
overlookmotel da509be78a refactor(linter/plugins): convert global properties to vars with TSDown plugin, instead of manually (#16961)
Previously we had a pattern of destructuring properties of global objects into top-level vars. e.g.:

```js
const { keys: ObjectKeys, hasOwn } = Object,
  { isArray } = Array;
```

Doing this is more performant, but it makes the code harder to read.

Instead, write code normally, and use a TSDown plugin to convert:

```js
const keys = Object.keys(obj);
```

to:

```js
import { ObjectKeys } from "../utils/globals.ts";
const keys = ObjectKeys(obj);
```

Defining all the destructured globals in a single file (`utils/globals.ts`) is advantageous for bundle size. If the TSDown plugin created top level `const ObjectKeys = Object.keys;` statements in each file, then you can end up with this in bundle as minifier doesn't de-duplicate:

```js
const ObjectKeys = Object.keys;
const ObjectKeys$1 = Object.keys;
const ObjectKeys$2 = Object.keys;
```

The globals we use have to be manually added to `utils/globals.ts`, but TSDown plugin warns when this needs to be done, and doesn't generate a broken build in the meantime.

This plugin is run in debug builds too, so that the tests will catch any bugs.
2025-12-16 17:33:37 +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 0a6a28806b refactor(linter/plugins): add debugAssert function (#15969)
Add a `debugAssert` function, which works the same as `debug_assert!` in Rust. We can use this to add more assertions to rest of code.

Same as `debugAssertIsNonNull` and `typeAssertIs`, this function will be removed entirely in release build - it only performs the check in debug build (which is used in the tests).
2025-11-22 15:54:17 +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
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