Skip to content

Commit 26254c4

Browse files
author
Daniel Del Core
committed
adds isDecendantOfType util
1 parent b9a28ab commit 26254c4

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

.changeset/five-llamas-clap.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@codeshift/utils': patch
3+
---
4+
5+
Adds isDecendantOfType as a way to ensure a node has a specific parent

packages/utils/src/nodes.spec.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { API, FileInfo } from 'jscodeshift';
2+
import { applyTransform } from '@codeshift/test-utils';
3+
import { isDecendantOfType } from '@codeshift/utils';
4+
5+
describe('nodes', () => {
6+
describe('isDecendantOfType', () => {
7+
it('detects decendant callExpression', async () => {
8+
let result = 0;
9+
const transform = (file: FileInfo, api: API) => {
10+
const j = api.jscodeshift;
11+
12+
result = j(file.source)
13+
.find(j.Literal)
14+
.filter(literal =>
15+
isDecendantOfType(j, literal, j.CallExpression),
16+
).length;
17+
};
18+
19+
await applyTransform(transform, 'console.log("bar");');
20+
21+
expect(result).toEqual(1);
22+
});
23+
24+
it('correctly reports missing decendant', async () => {
25+
let result = 0;
26+
const transform = (file: FileInfo, api: API) => {
27+
const j = api.jscodeshift;
28+
29+
result = j(file.source)
30+
.find(j.Literal)
31+
.filter(literal =>
32+
isDecendantOfType(j, literal, j.CallExpression),
33+
).length;
34+
};
35+
36+
await applyTransform(transform, 'const foo = "bar";');
37+
38+
expect(result).toEqual(0);
39+
});
40+
41+
it('correctly reports deeply nested decendant', async () => {
42+
let result = 0;
43+
const transform = (file: FileInfo, api: API) => {
44+
const j = api.jscodeshift;
45+
46+
result = j(file.source)
47+
.find(j.Literal)
48+
.filter(literal =>
49+
isDecendantOfType(j, literal, j.CallExpression),
50+
).length;
51+
};
52+
53+
await applyTransform(transform, 'console.log(console.log("bar"));');
54+
55+
expect(result).toEqual(1);
56+
});
57+
});
58+
});

packages/utils/src/nodes.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ASTNode } from 'jscodeshift';
1+
import core, { ASTNode, ASTPath } from 'jscodeshift';
22

33
/**
44
* The isNodeOfType function uses generics to check if a node of type ASTNode is of a specified type.
@@ -14,3 +14,22 @@ export const isNodeOfType = <Expected extends ASTNode>(
1414
node: ASTNode,
1515
type: Expected['type'],
1616
): node is Expected => node.type === type;
17+
18+
/**
19+
* Determines if the current node is a decendant of a parent node of specific type
20+
*
21+
* Example:
22+
*
23+
* ```ts
24+
* const isDecendantOfImportSpecifier = isDecendantOfType(node, j.ImportSpecifier);
25+
* ```
26+
*/
27+
export const isDecendantOfType = (
28+
j: core.JSCodeshift,
29+
source: ASTPath<any>,
30+
type: any,
31+
): boolean => {
32+
const closestNodes = j(source).closest(type);
33+
const count: number = closestNodes.length;
34+
return count > 0;
35+
};

website/docs/api/codeshift-utils.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ If the check passes, the type of node is narrowed to the expected type, ensuring
3434
const isImportSpecifier = isNodeOfType(node, 'ImportSpecifier');
3535
```
3636

37+
### `isDecendantOfType`
38+
39+
`isDecendantOfType(j, path, type)`
40+
41+
The `isDecendantOfType` function traverses the AST to determind if the given path is a child of a node of the specified type.
42+
43+
**Returns**
44+
45+
`boolean`
46+
47+
**Example**
48+
49+
```jsx
50+
const isChildOfImportSpecifier = isDecendantOfType(j, path, j.ImportSpecifier);
51+
```
52+
3753
## Imports
3854

3955
### `hasImportDeclaration`

0 commit comments

Comments
 (0)