Implement CFG (or "code path analysis" as ESLint calls it).
This is the was the last remaining gap in our API.
The implementation is pretty slow, I imagine. I've pulled in ESLint's code for the actual control flow graph construction, and copied ESLint's code for the rest.
Fitting with how we combine enter and exit nodes for leaf nodes (`Identifier`s etc) required skipping creating "steps" for exiting leaf nodes, which should also be a perf gain, as they're so common. But that apart, I've made no effort to optimize it. There's plenty of scope for improving it. I've added comments with some starting points for doing that, and will open an issue in the hope someone would like to pick up on that.
I also cannot for the life of me figure out how to add the CFG event handlers (`onCodePathStart` etc) to `VisitorObject` type, without breaking everything. Me and Claude went at it for about an hour, and got nowhere. So I'm just leaving that for now.
We already supported `:function` class selector, but no others.
Add support for all the classes that ESLint supports: `:statement`, `:declaration`, `:pattern`, `:expression`.
This fixes 260 conformance tests.
`:function` selector class was broken when part of a broader selector e.g. `:function > Identifier`.
Fix it by defining a proper `matchClass` function to pass to ESQuery.
Add error messages to all `debugAssert()` calls, for easier debugging.
When running Oxlint normally, we can easily figure out where errors are coming from stack traces, but in conformance test snapshot, line and column are removed from errors in the snapshot, so it's less easy.
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.
#16834 implemented sorting visit methods by specificity. Speed up the sorting by storing `specificity` as a single integer, rather than 2 separate properties `identifierCount` and `attributeCount`.
See comment in `selector.ts` for further explanation.
Note: I've checked that minifier const-folds and inlines all the consts in release build.
Pure refactor. Move TS type defs related to visitor compilation from `types.ts` into `visitor.ts`. Does not change the types in any way, only moves them.
Only alters comments.
* Make sure all `// @ts-expect-error` comments include "TODO" where it's an issue we need to fix.
* Format all the "TODO" comments consistently.
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.
Implement support for selectors DSL in Oxlint JS plugins.
The implementation is based on ESLint's implementation. Like ESLint, it uses [esquery](https://www.npmjs.com/package/esquery) as the underlying selector parser and matcher.
One detail is not implemented. ESLint ensures that visit functions are called in order of their "specificity" with the most specific called first. This implementation just calls them in the order they're defined. This is a shortcoming and will need to be fixed in future, but I imagine that in practice visitation order makes no difference for the vast majority of rules.
I've made what I hope are some optimizations versus ESLint's version. Notably:
1. Making simple selectors which unconditionally visit a subset of nodes (e.g. `:matches(FunctionDeclaration, FunctionExpression)` bypass the selector match process - as it'd always return `true` anyway.
2. Faster implementations of union and intersection.