mirror of
https://github.com/denoland/deno.git
synced 2025-08-03 18:38:33 +00:00
feat(unstable): lint plugins support field selectors (#28324)
This PR adds support for field selectors (`.<field>`) in the lint plugin API. This is supported in ESLint as well, but was missing in our implementation. ```css /* Only search the test expression of an IfStatement */ IfStatement.test ``` Fixes https://github.com/denoland/deno/issues/28314
This commit is contained in:
parent
b4aa3e6d1e
commit
3a1f3455b0
5 changed files with 161 additions and 1 deletions
|
@ -9,6 +9,7 @@
|
|||
/** @typedef {import("./40_lint_types.d.ts").AttrBin} AttrBin */
|
||||
/** @typedef {import("./40_lint_types.d.ts").AttrSelector} AttrSelector */
|
||||
/** @typedef {import("./40_lint_types.d.ts").ElemSelector} ElemSelector */
|
||||
/** @typedef {import("./40_lint_types.d.ts").FieldSelector} FieldSelector */
|
||||
/** @typedef {import("./40_lint_types.d.ts").PseudoNthChild} PseudoNthChild */
|
||||
/** @typedef {import("./40_lint_types.d.ts").PseudoHas} PseudoHas */
|
||||
/** @typedef {import("./40_lint_types.d.ts").PseudoNot} PseudoNot */
|
||||
|
@ -376,6 +377,7 @@ export const PSEUDO_HAS = 6;
|
|||
export const PSEUDO_NOT = 7;
|
||||
export const PSEUDO_FIRST_CHILD = 8;
|
||||
export const PSEUDO_LAST_CHILD = 9;
|
||||
export const FIELD_NODE = 10;
|
||||
|
||||
/**
|
||||
* Parse out all unique selectors of a selector list.
|
||||
|
@ -492,6 +494,26 @@ export function parseSelector(input, toElem, toAttr) {
|
|||
lex.expect(Token.BracketClose);
|
||||
lex.next();
|
||||
continue;
|
||||
} else if (lex.token === Token.Dot) {
|
||||
lex.next();
|
||||
lex.expect(Token.Word);
|
||||
|
||||
const props = [toAttr(lex.value)];
|
||||
lex.next();
|
||||
|
||||
while (lex.token === Token.Dot) {
|
||||
lex.next();
|
||||
lex.expect(Token.Word);
|
||||
|
||||
props.push(toAttr(lex.value));
|
||||
lex.next();
|
||||
}
|
||||
|
||||
current.push({
|
||||
type: FIELD_NODE,
|
||||
props,
|
||||
});
|
||||
continue;
|
||||
} else if (lex.token === Token.Colon) {
|
||||
lex.next();
|
||||
lex.expect(Token.Word);
|
||||
|
@ -710,6 +732,9 @@ export function compileSelector(selector) {
|
|||
case ELEM_NODE:
|
||||
fn = matchElem(node, fn);
|
||||
break;
|
||||
case FIELD_NODE:
|
||||
fn = matchField(node, fn);
|
||||
break;
|
||||
case RELATION_NODE:
|
||||
switch (node.op) {
|
||||
case BinOp.Space:
|
||||
|
@ -960,6 +985,39 @@ function matchElem(part, next) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {FieldSelector} part
|
||||
* @param {MatcherFn} next
|
||||
* @returns {MatcherFn}
|
||||
*/
|
||||
function matchField(part, next) {
|
||||
return (ctx, id) => {
|
||||
let child = id;
|
||||
let parent = ctx.getParent(id);
|
||||
if (parent === 0) return false;
|
||||
|
||||
// Fields are stored left-ro-right but we need to match
|
||||
// them right-to-left because we're matching selectors
|
||||
// in that direction. Matching right to left is done for
|
||||
// performance and reduces the number of potential mismatches.
|
||||
for (let i = part.props.length - 1; i >= 0; i--) {
|
||||
const prop = part.props[i];
|
||||
const value = ctx.getField(parent, prop);
|
||||
|
||||
if (value === -1) return false;
|
||||
if (value !== child) return false;
|
||||
|
||||
if (i > 0) {
|
||||
child = parent;
|
||||
parent = ctx.getParent(parent);
|
||||
if (parent === 0) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx, parent);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AttrExists} attr
|
||||
* @param {MatcherFn} next
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue