diff --git a/apps/oxlint/src-js/plugins/source_code.ts b/apps/oxlint/src-js/plugins/source_code.ts index eb4a806868702..bb4ca0bba6739 100644 --- a/apps/oxlint/src-js/plugins/source_code.ts +++ b/apps/oxlint/src-js/plugins/source_code.ts @@ -518,16 +518,7 @@ export const SOURCE_CODE = Object.freeze({ throw new Error('`sourceCode.commentsExistBetween` not implemented yet'); // TODO }, - /** - * Get all the ancestors of a given node. - * @param node - AST node - * @returns All the ancestor nodes in the AST, not including the provided node, - * starting from the root node at index 0 and going inwards to the parent node. - */ - // oxlint-disable-next-line no-unused-vars - getAncestors(node: Node): Node[] { - throw new Error('`sourceCode.getAncestors` not implemented yet'); // TODO - }, + getAncestors, /** * Get the variables that `node` defines. @@ -655,6 +646,26 @@ export function getIndexFromLoc(loc: LineColumn): number { throw new TypeError('Expected `loc` to be an object with integer `line` and `column` properties.'); } +/** + * Get all the ancestors of a given node. + * @param node - AST node + * @returns All the ancestor nodes in the AST, not including the provided node, + * starting from the root node at index 0 and going inwards to the parent node. + */ +function getAncestors(node: Node): Node[] { + const ancestors = []; + + for ( + let ancestor = (node as unknown as { parent: Node }).parent; + ancestor; + ancestor = (ancestor as unknown as { parent: Node }).parent + ) { + ancestors.push(ancestor); + } + + return ancestors.reverse(); +} + // Options for various `SourceCode` methods e.g. `getFirstToken`. export interface SkipOptions { // Number of skipping tokens diff --git a/apps/oxlint/test/fixtures/parent/output.snap.md b/apps/oxlint/test/fixtures/parent/output.snap.md index 636bca91acd1c..14c3bab19ae6d 100644 --- a/apps/oxlint/test/fixtures/parent/output.snap.md +++ b/apps/oxlint/test/fixtures/parent/output.snap.md @@ -3,73 +3,97 @@ # stdout ``` - x parents(check): VariableDeclaration -> Program + x parents(check): VariableDeclaration: + | parent: Program + | ancestors: [ Program ] ,-[files/index.js:1:1] 1 | const obj = { a: [b, c], ...d }; : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `---- - x parents(check): Program -> null + x parents(check): Program: + | parent: undefined + | ancestors: [ ] ,-[files/index.js:1:1] 1 | const obj = { a: [b, c], ...d }; : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `---- - x parents(check): Identifier -> VariableDeclarator + x parents(check): Identifier: + | parent: VariableDeclarator + | ancestors: [ Program, VariableDeclaration, VariableDeclarator ] ,-[files/index.js:1:7] 1 | const obj = { a: [b, c], ...d }; : ^^^ `---- - x parents(check): VariableDeclarator -> VariableDeclaration + x parents(check): VariableDeclarator: + | parent: VariableDeclaration + | ancestors: [ Program, VariableDeclaration ] ,-[files/index.js:1:7] 1 | const obj = { a: [b, c], ...d }; : ^^^^^^^^^^^^^^^^^^^^^^^^^ `---- - x parents(check): ObjectExpression -> VariableDeclarator + x parents(check): ObjectExpression: + | parent: VariableDeclarator + | ancestors: [ Program, VariableDeclaration, VariableDeclarator ] ,-[files/index.js:1:13] 1 | const obj = { a: [b, c], ...d }; : ^^^^^^^^^^^^^^^^^^^ `---- - x parents(check): Identifier -> Property + x parents(check): Identifier: + | parent: Property + | ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property ] ,-[files/index.js:1:15] 1 | const obj = { a: [b, c], ...d }; : ^ `---- - x parents(check): Property -> ObjectExpression + x parents(check): Property: + | parent: ObjectExpression + | ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression ] ,-[files/index.js:1:15] 1 | const obj = { a: [b, c], ...d }; : ^^^^^^^^^ `---- - x parents(check): ArrayExpression -> Property + x parents(check): ArrayExpression: + | parent: Property + | ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property ] ,-[files/index.js:1:18] 1 | const obj = { a: [b, c], ...d }; : ^^^^^^ `---- - x parents(check): Identifier -> ArrayExpression + x parents(check): Identifier: + | parent: ArrayExpression + | ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property, ArrayExpression ] ,-[files/index.js:1:19] 1 | const obj = { a: [b, c], ...d }; : ^ `---- - x parents(check): Identifier -> ArrayExpression + x parents(check): Identifier: + | parent: ArrayExpression + | ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, Property, ArrayExpression ] ,-[files/index.js:1:22] 1 | const obj = { a: [b, c], ...d }; : ^ `---- - x parents(check): SpreadElement -> ObjectExpression + x parents(check): SpreadElement: + | parent: ObjectExpression + | ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression ] ,-[files/index.js:1:26] 1 | const obj = { a: [b, c], ...d }; : ^^^^ `---- - x parents(check): Identifier -> SpreadElement + x parents(check): Identifier: + | parent: SpreadElement + | ancestors: [ Program, VariableDeclaration, VariableDeclarator, ObjectExpression, SpreadElement ] ,-[files/index.js:1:29] 1 | const obj = { a: [b, c], ...d }; : ^ diff --git a/apps/oxlint/test/fixtures/parent/plugin.ts b/apps/oxlint/test/fixtures/parent/plugin.ts index 341f17d540aa6..07e864dcd85ae 100644 --- a/apps/oxlint/test/fixtures/parent/plugin.ts +++ b/apps/oxlint/test/fixtures/parent/plugin.ts @@ -7,31 +7,25 @@ const plugin: Plugin = { rules: { check: { create(context) { + function reportAncestry(node: any) { + context.report({ + message: `${node.type}:\n` + + `parent: ${node.parent?.type}\n` + + // @ts-ignore + `ancestors: [ ${context.sourceCode.getAncestors(node).map(node => node.type).join(', ')} ]`, + node, + }); + } + return { - Program(node) { - context.report({ message: `${node.type} -> ${node.parent}`, node }); - }, - VariableDeclaration(node) { - context.report({ message: `${node.type} -> ${node.parent.type}`, node }); - }, - VariableDeclarator(node) { - context.report({ message: `${node.type} -> ${node.parent.type}`, node }); - }, - Identifier(node) { - context.report({ message: `${node.type} -> ${node.parent.type}`, node }); - }, - ObjectExpression(node) { - context.report({ message: `${node.type} -> ${node.parent.type}`, node }); - }, - Property(node) { - context.report({ message: `${node.type} -> ${node.parent.type}`, node }); - }, - ArrayExpression(node) { - context.report({ message: `${node.type} -> ${node.parent.type}`, node }); - }, - SpreadElement(node) { - context.report({ message: `${node.type} -> ${node.parent.type}`, node }); - }, + Program: reportAncestry, + VariableDeclaration: reportAncestry, + VariableDeclarator: reportAncestry, + Identifier: reportAncestry, + ObjectExpression: reportAncestry, + Property: reportAncestry, + ArrayExpression: reportAncestry, + SpreadElement: reportAncestry, }; }, },