verify traversal visitors

This commit is contained in:
Sebastian McKenzie 2015-04-25 22:49:54 +01:00
parent 4c9cb957a9
commit dfe126f3d4
2 changed files with 42 additions and 2 deletions

View File

@ -22,7 +22,11 @@ export const MESSAGES = {
codeGeneratorDeopt: "Note: The code generator has deoptimised the styling of $1 as it exceeds the max of $2.",
missingTemplatesDirectory: "no templates directory - this is most likely the result of a broken `npm publish`. Please report to https://github.com/babel/babel/issues",
unsupportedOutputType: "Unsupported output type $1",
illegalMethodName: "Illegal method name $1"
illegalMethodName: "Illegal method name $1",
traverseNeedsParent: "Must pass a scope and parentPath unless traversing a Program/File got a $1 node",
traverseVerifyRootFunction: "You passed `traverse()` a function when it expected a visitor object, are you sure you didn't mean `{ enter: Function }`?",
traverseVerifyVisitorFunction: "Hey! You passed \`traverse()\` a visitor object with the key $1 that's a straight up `Function` instead of `{ enter: Function }`. You need to normalise it with `traverse.explode(visitor)`.",
traverseVerifyVisitorProperty: "You passed `traverse()` a visitor object with the property $1 that has the invalid property $2"
};
export function get(key: String, ...args) {

View File

@ -1,4 +1,5 @@
import TraversalContext from "./context";
import * as messages from "../messages";
import includes from "lodash/collection/includes";
import * as t from "../types";
@ -7,7 +8,7 @@ export default function traverse(parent, opts, scope, state, parentPath) {
if (!opts.noScope && !scope) {
if (parent.type !== "Program" && parent.type !== "File") {
throw new Error(`Must pass a scope and parentPath unless traversing a Program/File got a ${parent.type} node`);
throw new Error(messages.get("traverseNeedsParent", parent.type));
}
}
@ -15,6 +16,8 @@ export default function traverse(parent, opts, scope, state, parentPath) {
if (!opts.enter) opts.enter = function () { };
if (!opts.exit) opts.exit = function () { };
traverse.verify(opts);
// array of nodes
if (Array.isArray(parent)) {
for (var i = 0; i < parent.length; i++) {
@ -25,6 +28,39 @@ export default function traverse(parent, opts, scope, state, parentPath) {
}
}
/**
* Quickly iterate over some traversal options and validate it.
*/
traverse.verify = function (opts) {
if (opts._verified) return;
if (typeof opts === "function") {
throw new Error(messages.get("traverseVerifyRootFunction"));
}
for (var key in opts) {
// it's all good
if (key === "blacklist") continue;
var opt = opts[key];
if (typeof opt === "function") {
// it's all good, it's fine for this key to be a function
if (key === "enter" || key === "exit") continue;
throw new Error(messages.get("traverseVerifyVisitorFunction", key));
} else if (typeof opt === "object") {
for (var key2 in opt) {
if (key2 === "enter" || key2 === "exit") continue;
throw new Error(messages.get("traverseVerifyVisitorProperty", key, key2));
}
}
}
opts._verified = true;
};
traverse.node = function (node, opts, scope, state, parentPath) {
var keys = t.VISITOR_KEYS[node.type];
if (!keys) return;