[traverse] Allow skipping nodes inserted with .replaceWith() (#9777)

* Avoid regression
This commit is contained in:
Nicolò Ribaudo 2019-10-18 00:16:35 +02:00 committed by GitHub
parent b5b8055cc0
commit 43b623c1f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 1 deletions

View File

@ -67,7 +67,12 @@ export function visit(): boolean {
return false;
}
if (this.call("enter") || this.shouldSkip) {
// Note: We need to check "this.shouldSkip" twice because
// the visitor can set it to true. Usually .shouldSkip is false
// before calling the enter visitor, but it can be true in case of
// a requeued node (e.g. by .replaceWith()) that is then marked
// with .skip().
if (this.shouldSkip || this.call("enter") || this.shouldSkip) {
this.debug("Skip...");
return this.shouldStop;
}
@ -233,6 +238,14 @@ export function setKey(key) {
export function requeue(pathToQueue = this) {
if (pathToQueue.removed) return;
// TODO: Uncomment in Babel 8. If a path is skipped, and then replaced with a
// new one, the new one shouldn't probably be skipped.
// Note that this currently causes an infinite loop because of
// packages/babel-plugin-transform-block-scoping/src/tdz.js#L52-L59
// (b5b8055cc00756f94bf71deb45f288738520ee3c)
//
// pathToQueue.shouldSkip = false;
// TODO(loganfsmyth): This should be switched back to queue in parent contexts
// automatically once #2892 and #4135 have been resolved. See #4140.
// let contexts = this._getQueueContexts();

View File

@ -1,6 +1,7 @@
import cloneDeep from "lodash/cloneDeep";
import traverse from "../lib";
import { parse } from "@babel/parser";
import * as t from "@babel/types";
describe("traverse", function() {
const code = `
@ -174,4 +175,45 @@ describe("traverse", function() {
expect(p).not.toBe(scopes[i]);
});
});
describe("path.skip()", function() {
it("replaced paths can be skipped", function() {
const ast = parse("id");
let skipped;
traverse(ast, {
noScope: true,
Identifier(path) {
path.replaceWith(t.numericLiteral(0));
path.skip();
skipped = true;
},
NumericLiteral() {
skipped = false;
},
});
expect(skipped).toBe(true);
});
// Skipped: see the comment in the `NodePath.requque` method.
it.skip("skipped and requeued paths should be visited", function() {
const ast = parse("id");
let visited = false;
traverse(ast, {
noScope: true,
Identifier(path) {
path.replaceWith(t.numericLiteral(0));
path.skip();
path.requeue();
},
NumericLiteral() {
visited = true;
},
});
expect(visited).toBe(true);
});
});
});