diff --git a/packages/babel-traverse/src/path/introspection.ts b/packages/babel-traverse/src/path/introspection.ts index 0e01e4f20c..91a0906d83 100644 --- a/packages/babel-traverse/src/path/introspection.ts +++ b/packages/babel-traverse/src/path/introspection.ts @@ -164,13 +164,30 @@ export function isStatementOrBlock(this: NodePath): boolean { */ export function referencesImport( - this: NodePath, + this: NodePath, moduleSource: string, importName: string, ): boolean { - if (!this.isReferencedIdentifier()) return false; + if (!this.isReferencedIdentifier()) { + if ( + (this.isMemberExpression() || this.isOptionalMemberExpression()) && + (this.node.computed + ? t.isStringLiteral(this.node.property, { value: importName }) + : (this.node.property as t.Identifier).name === importName) + ) { + const object = (this as NodePath< + t.MemberExpression | t.OptionalMemberExpression + >).get("object"); + return ( + object.isReferencedIdentifier() && + object.referencesImport(moduleSource, "*") + ); + } - const binding = this.scope.getBinding(this.node.name); + return false; + } + + const binding = this.scope.getBinding((this.node as t.Identifier).name); if (!binding || binding.kind !== "module") return false; const path = binding.path; diff --git a/packages/babel-traverse/test/introspection.js b/packages/babel-traverse/test/introspection.js index 481561ba9d..980a882566 100644 --- a/packages/babel-traverse/test/introspection.js +++ b/packages/babel-traverse/test/introspection.js @@ -100,4 +100,108 @@ describe("path/introspection", function () { }); }); }); + + describe("referencesImport", function () { + it("accepts a default import", function () { + const program = getPath(`import dep from "source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "default")).toBe(true); + }); + it("rejects a default import from the wrong module", function () { + const program = getPath(`import dep from "wrong-source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "default")).toBe(false); + }); + it("rejects a named instead of default import", function () { + const program = getPath(`import { dep } from "source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "default")).toBe(false); + }); + + it("accepts a named import", function () { + const program = getPath(`import { dep } from "source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "dep")).toBe(true); + }); + it("accepts an aliased named import", function () { + const program = getPath(`import { dep as alias } from "source"; alias;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "dep")).toBe(true); + }); + it("accepts a named import via a namespace import member expression", function () { + const program = getPath(`import * as ns from "source"; ns.dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "dep")).toBe(true); + }); + it("accepts a named import via a namespace import optional member expression", function () { + const program = getPath(`import * as ns from "source"; ns?.dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "dep")).toBe(true); + }); + it("accepts a named import via a namespace import computed member expression", function () { + const program = getPath(`import * as ns from "source"; ns["😅"];`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "😅")).toBe(true); + }); + it("rejects a named import from the wrong module", function () { + const program = getPath(`import { dep } from "wrong-source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "dep")).toBe(false); + }); + it("rejects a default instead of named import", function () { + const program = getPath(`import dep from "source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "dep")).toBe(false); + }); + it('rejects the "export called *" trick', function () { + const program = getPath(`import * as ns from "source"; ns["*"].nested;`, { + sourceType: "module", + plugins: ["moduleStringNames"], + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "nested")).toBe(false); + }); + + it("accepts a namespace import", function () { + const program = getPath(`import * as dep from "source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "*")).toBe(true); + }); + it("rejects a namespace import from the wrong module", function () { + const program = getPath(`import * as dep from "wrong-source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "*")).toBe(false); + }); + it("rejects a default instead of a namespace import", () => { + const program = getPath(`import dep from "source"; dep;`, { + sourceType: "module", + }); + const reference = program.get("body.1.expression"); + expect(reference.referencesImport("source", "*")).toBe(false); + }); + }); });