referencesImport: support named exports accessed via * imports (#12603)

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
Tim Seckinger 2021-02-21 17:54:43 +01:00 committed by GitHub
parent a93bbce867
commit 774c526390
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 124 additions and 3 deletions

View File

@ -164,13 +164,30 @@ export function isStatementOrBlock(this: NodePath): boolean {
*/
export function referencesImport(
this: NodePath<t.Identifier>,
this: NodePath<t.Expression>,
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;

View File

@ -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);
});
});
});