diff --git a/src/babel/messages.js b/src/babel/messages.js index 8c8c5528c1..7926eda3c3 100644 --- a/src/babel/messages.js +++ b/src/babel/messages.js @@ -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) { diff --git a/src/babel/traversal/index.js b/src/babel/traversal/index.js index 07801b333b..95e606fcda 100644 --- a/src/babel/traversal/index.js +++ b/src/babel/traversal/index.js @@ -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;