feat(unstable): refactor js lint plugin AST (#27615)

This PR changes the underlying buffer backed AST format we use for
JavaScript-based linting plugins. It adds support for various new types,
makes traversal code a lot easier and is more polished compared to
previous iterations.

Here is a quick summary (in no particular order):

- Node prop data is separate from traversal, which makes traversal code
so much easier to reason about. Previously, it was interleaved with node
prop data
- spans are in a separate table as well, as they are rarely needed.
- schema is separate from SWC conversion logic, which makes 
- supports recursive plain objects
- supports numbers
- supports bigint
- supports regex
- adds all SWC nodes

Apologies, this is kinda a big PR, but it's worth it imo.

_Marking as draft because I need to update some tests tomorrow._
This commit is contained in:
Marvin Hagemeister 2025-01-14 13:31:02 +01:00 committed by Bartek Iwańczuk
parent f6820dec52
commit 526c66dd4e
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
8 changed files with 13757 additions and 3245 deletions

View file

@ -744,8 +744,7 @@ export function compileSelector(selector) {
fn = matchNthChild(node, fn);
break;
case PSEUDO_HAS:
// FIXME
// fn = matchIs(part, fn);
// TODO(@marvinhagemeister)
throw new Error("TODO: :has");
case PSEUDO_NOT:
fn = matchNot(node.selectors, fn);
@ -767,8 +766,7 @@ export function compileSelector(selector) {
*/
function matchFirstChild(next) {
return (ctx, id) => {
const parent = ctx.getParent(id);
const first = ctx.getFirstChild(parent);
const first = ctx.getFirstChild(id);
return first === id && next(ctx, first);
};
}
@ -779,8 +777,7 @@ function matchFirstChild(next) {
*/
function matchLastChild(next) {
return (ctx, id) => {
const parent = ctx.getParent(id);
const last = ctx.getLastChild(parent);
const last = ctx.getLastChild(id);
return last === id && next(ctx, id);
};
}
@ -955,7 +952,9 @@ function matchElem(part, next) {
else if (part.elem === 0) return false;
const type = ctx.getType(id);
if (type > 0 && type === part.elem) return next(ctx, id);
if (type > 0 && type === part.elem) {
return next(ctx, id);
}
return false;
};
@ -968,7 +967,16 @@ function matchElem(part, next) {
*/
function matchAttrExists(attr, next) {
return (ctx, id) => {
return ctx.hasAttrPath(id, attr.prop, 0) ? next(ctx, id) : false;
try {
ctx.getAttrPathValue(id, attr.prop, 0);
return next(ctx, id);
} catch (err) {
if (err === -1) {
return false;
}
throw err;
}
};
}
@ -979,9 +987,15 @@ function matchAttrExists(attr, next) {
*/
function matchAttrBin(attr, next) {
return (ctx, id) => {
if (!ctx.hasAttrPath(id, attr.prop, 0)) return false;
const value = ctx.getAttrPathValue(id, attr.prop, 0);
if (!matchAttrValue(attr, value)) return false;
try {
const value = ctx.getAttrPathValue(id, attr.prop, 0);
if (!matchAttrValue(attr, value)) return false;
} catch (err) {
if (err === -1) {
return false;
}
throw err;
}
return next(ctx, id);
};
}