Skip to content

Commit

Permalink
Fix scope of computed method keys
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Feb 17, 2021
1 parent 99130bc commit 448dce5
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 1 deletion.
4 changes: 4 additions & 0 deletions packages/babel-traverse/src/path/context.ts
Expand Up @@ -119,6 +119,10 @@ export function setScope(this: NodePath) {
if (this.opts && this.opts.noScope) return;

let path = this.parentPath;

// Skip method scope if is computed method key
if (this.key === "key" && path.isMethod()) path = path.parentPath;

let target;
while (path && !target) {
if (path.opts && path.opts.noScope) return;
Expand Down
11 changes: 10 additions & 1 deletion packages/babel-traverse/src/scope/index.ts
Expand Up @@ -359,7 +359,16 @@ export default class Scope {
static contextVariables = ["arguments", "undefined", "Infinity", "NaN"];

get parent() {
const parent = this.path.findParent(p => p.isScope());
let parent,
path = this.path;
do {
// Skip method scope if coming from inside computed key
const isKey = path.key === "key";
path = path.parentPath;
if (isKey && path.isMethod()) path = path.parentPath;
if (path && path.isScope()) parent = path;
} while (path && !parent);

return parent?.scope;
}

Expand Down
68 changes: 68 additions & 0 deletions packages/babel-traverse/test/scope.js
Expand Up @@ -168,6 +168,74 @@ describe("scope", () => {
});
});

describe("computed method key", () => {
describe("should not have visibility of declarations inside method body", () => {
it("when path is computed key", () => {
expect(
getPath(`var a = "outside"; ({ [a]() { let a = "inside" } })`)
.get("body.1.expression.properties.0.key")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");

expect(
getPath(
`var a = "outside"; class foo { [a]() { let a = "inside" } }`,
)
.get("body.1.body.body.0.key")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");
});

it("when path is in nested scope which is computed key", () => {
expect(
getPath(`var a = "outside"; ({ [() => a]() { let a = "inside" } })`)
.get("body.1.expression.properties.0.key.body")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");

expect(
getPath(
`var a = "outside"; class foo { [() => a]() { let a = "inside" } }`,
)
.get("body.1.body.body.0.key.body")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");
});

it("when path is in nested scope within computed key", () => {
expect(
getPath(
`var a = "outside"; ({ [(() => a)() + ""]() { let a = "inside" } })`,
)
.get("body.1.expression.properties.0.key.left.callee.body")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");

expect(
getPath(
`var a = "outside"; class foo { [(() => a)() + ""]() { let a = "inside" } }`,
)
.get("body.1.body.body.0.key.left.callee.body")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");
});
});

it("should not have visibility on parameter bindings", () => {
expect(
getPath(`var a = "outside"; ({ [a](a = "inside") {} })`)
.get("body.1.expression.properties.0.key")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");

expect(
getPath(`var a = "outside"; class foo { [a](a = "inside") {} }`)
.get("body.1.body.body.0.key")
.scope.getBinding("a").path.node.init.value,
).toBe("outside");
});
});

it("variable declaration", function () {
expect(getPath("var foo = null;").scope.getBinding("foo").path.type).toBe(
"VariableDeclarator",
Expand Down

0 comments on commit 448dce5

Please sign in to comment.