Skip to content

Commit 1d5f37b

Browse files
fix: handle property accesses cases on resolve-binding-path (#218)
Co-authored-by: Jacob Smith <[email protected]>
1 parent 2fcce63 commit 1d5f37b

File tree

2 files changed

+182
-52
lines changed

2 files changed

+182
-52
lines changed

utils/src/ast-grep/resolve-binding-path.test.ts

Lines changed: 121 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import assert from "node:assert/strict";
22
import { describe, it } from "node:test";
33
import astGrep from "@ast-grep/napi";
44
import dedent from "dedent";
5+
import type Js from "@codemod.com/jssg-types/langs/javascript";
6+
import type { SgNode } from "@codemod.com/jssg-types/main";
57

68
import { resolveBindingPath } from "./resolve-binding-path.ts";
79

@@ -12,7 +14,7 @@ describe("resolve-binding-path", () => {
1214
`;
1315

1416
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
15-
const importStatement = rootNode.root().find({
17+
const importStatement = (rootNode.root() as SgNode<Js>).find({
1618
rule: {
1719
kind: "lexical_declaration",
1820
},
@@ -23,13 +25,30 @@ describe("resolve-binding-path", () => {
2325
assert.strictEqual(bindingPath, "util.types.isNativeError");
2426
});
2527

28+
it("should be able to resolve binding path from namespace ESM import", () => {
29+
const code = dedent`
30+
import foo from "node:foo"
31+
`;
32+
33+
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
34+
const importStatement = (rootNode.root() as SgNode<Js>).find({
35+
rule: {
36+
kind: "import_statement",
37+
},
38+
});
39+
40+
const bindingPath = resolveBindingPath(importStatement!, "$.bar");
41+
42+
assert.strictEqual(bindingPath, "foo.bar");
43+
});
44+
2645
it("should be able to solve binding path when destructuring happen", () => {
2746
const code = dedent`
2847
const { types } = require('node:util');
2948
`;
3049

3150
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
32-
const importStatement = rootNode.root().find({
51+
const importStatement = (rootNode.root() as SgNode<Js>).find({
3352
rule: {
3453
kind: "variable_declarator",
3554
},
@@ -46,7 +65,7 @@ describe("resolve-binding-path", () => {
4665
`;
4766

4867
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
49-
const requireStatement = rootNode.root().find({
68+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
5069
rule: {
5170
kind: "variable_declarator",
5271
},
@@ -63,7 +82,7 @@ describe("resolve-binding-path", () => {
6382
`;
6483

6584
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
66-
const importStatement = rootNode.root().find({
85+
const importStatement = (rootNode.root() as SgNode<Js>).find({
6786
rule: {
6887
kind: "variable_declarator",
6988
},
@@ -80,7 +99,7 @@ describe("resolve-binding-path", () => {
8099
`;
81100

82101
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
83-
const functionDeclaration = rootNode.root().find({
102+
const functionDeclaration = (rootNode.root() as SgNode<Js>).find({
84103
rule: {
85104
kind: "function_declaration",
86105
},
@@ -95,7 +114,7 @@ describe("resolve-binding-path", () => {
95114
`;
96115

97116
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
98-
const importStatement = rootNode.root().find({
117+
const importStatement = (rootNode.root() as SgNode<Js>).find({
99118
rule: {
100119
kind: "import_statement",
101120
},
@@ -112,7 +131,7 @@ describe("resolve-binding-path", () => {
112131
`;
113132

114133
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
115-
const importStatement = rootNode.root().find({
134+
const importStatement = (rootNode.root() as SgNode<Js>).find({
116135
rule: {
117136
kind: "import_statement",
118137
},
@@ -129,7 +148,7 @@ describe("resolve-binding-path", () => {
129148
`;
130149

131150
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
132-
const importStatement = rootNode.root().find({
151+
const importStatement = (rootNode.root() as SgNode<Js>).find({
133152
rule: {
134153
kind: "import_statement",
135154
},
@@ -146,7 +165,7 @@ describe("resolve-binding-path", () => {
146165
`;
147166

148167
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
149-
const importStatement = rootNode.root().find({
168+
const importStatement = (rootNode.root() as SgNode<Js>).find({
150169
rule: {
151170
kind: "import_statement",
152171
},
@@ -163,7 +182,7 @@ describe("resolve-binding-path", () => {
163182
`;
164183

165184
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
166-
const requireStatement = rootNode.root().find({
185+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
167186
rule: {
168187
kind: "variable_declarator",
169188
},
@@ -180,7 +199,7 @@ describe("resolve-binding-path", () => {
180199
`;
181200

182201
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
183-
const importStatement = rootNode.root().find({
202+
const importStatement = (rootNode.root() as SgNode<Js>).find({
184203
rule: {
185204
kind: "lexical_declaration",
186205
},
@@ -197,7 +216,7 @@ describe("resolve-binding-path", () => {
197216
`;
198217

199218
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
200-
const importStatement = rootNode.root().find({
219+
const importStatement = (rootNode.root() as SgNode<Js>).find({
201220
rule: {
202221
kind: "import_statement",
203222
},
@@ -214,7 +233,7 @@ describe("resolve-binding-path", () => {
214233
`;
215234

216235
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
217-
const requireStatement = rootNode.root().find({
236+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
218237
rule: {
219238
kind: "variable_declarator",
220239
},
@@ -231,7 +250,7 @@ describe("resolve-binding-path", () => {
231250
`;
232251

233252
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
234-
const requireStatement = rootNode.root().find({
253+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
235254
rule: {
236255
kind: "variable_declarator",
237256
},
@@ -248,7 +267,7 @@ describe("resolve-binding-path", () => {
248267
`;
249268

250269
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
251-
const requireStatement = rootNode.root().find({
270+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
252271
rule: {
253272
kind: "variable_declarator",
254273
},
@@ -265,7 +284,8 @@ describe("resolve-binding-path", () => {
265284
`;
266285

267286
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
268-
const requireStatement = rootNode.root().find({
287+
288+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
269289
rule: {
270290
kind: "variable_declarator",
271291
},
@@ -275,4 +295,89 @@ describe("resolve-binding-path", () => {
275295

276296
assert.strictEqual(bindingPath, "types.isNativeError");
277297
});
298+
299+
it("should resolve correctly when have member-expression", () => {
300+
const code = dedent`
301+
const SlowBuffer = require('buffer').SlowBuffer;
302+
`;
303+
304+
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
305+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
306+
rule: {
307+
kind: "variable_declarator",
308+
},
309+
});
310+
311+
const bindingPath = resolveBindingPath(requireStatement!, "$.SlowBuffer");
312+
313+
assert.strictEqual(bindingPath, "SlowBuffer");
314+
});
315+
316+
it("should resolve correctly when there are multiple property accesses", () => {
317+
const code = dedent`
318+
const variable = require('buffer').a.b;
319+
`;
320+
321+
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
322+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
323+
rule: {
324+
kind: "variable_declarator",
325+
},
326+
});
327+
328+
const bindingPath = resolveBindingPath(requireStatement!, "$.a.b");
329+
330+
assert.strictEqual(bindingPath, "variable");
331+
});
332+
333+
it("should resolve correctly when there are multiple property accesses but not the entire path", () => {
334+
const code = dedent`
335+
const variable = require('buffer').a.b;
336+
`;
337+
338+
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
339+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
340+
rule: {
341+
kind: "variable_declarator",
342+
},
343+
});
344+
345+
const bindingPath = resolveBindingPath(requireStatement!, "$.a.b.c.d.e");
346+
347+
assert.strictEqual(bindingPath, "variable.c.d.e");
348+
});
349+
350+
it("should resolve correctly when there are multiple property accesses and destructuring", () => {
351+
const code = dedent`
352+
const { c: { d } } = require('buffer').a.b;
353+
`;
354+
355+
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
356+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
357+
rule: {
358+
kind: "variable_declarator",
359+
},
360+
});
361+
362+
const bindingPath = resolveBindingPath(requireStatement!, "$.a.b.c.d.e");
363+
364+
assert.strictEqual(bindingPath, "d.e");
365+
});
366+
367+
it("should resolve as undefined when property accesses is different than path to solve", () => {
368+
const code = dedent`
369+
const c = require('buffer').a.g.c;
370+
`;
371+
372+
const rootNode = astGrep.parse(astGrep.Lang.JavaScript, code);
373+
const requireStatement = (rootNode.root() as SgNode<Js>).find({
374+
rule: {
375+
kind: "variable_declarator",
376+
},
377+
});
378+
379+
const bindingPath = resolveBindingPath(requireStatement!, "$.a.b.c.d.e");
380+
381+
assert.strictEqual(bindingPath, undefined);
382+
});
278383
});

0 commit comments

Comments
 (0)