[Pkg-javascript-commits] [node-ast-types] 01/03: Imported Upstream version 0.9.0

Julien Puydt julien.puydt at laposte.net
Wed Aug 17 05:42:13 UTC 2016


This is an automated email from the git hooks/post-receive script.

jpuydt-guest pushed a commit to branch master
in repository node-ast-types.

commit fee62b53cb1f83aaae74d4ff2aa5f01d094e2e13
Author: Julien Puydt <julien.puydt at laposte.net>
Date:   Wed Aug 17 07:30:59 2016 +0200

    Imported Upstream version 0.9.0
---
 .travis.yml         |    1 +
 def/babel.js        |  212 ++++----
 def/core.js         |  730 ++++++++++++++--------------
 def/e4x.js          |  170 +++----
 def/es6.js          |  426 ++++++++--------
 def/es7.js          |   54 ++-
 def/esprima.js      |  110 ++---
 def/flow.js         |  541 +++++++++++----------
 def/jsx.js          |  159 +++---
 def/mozilla.js      |   86 ++--
 fork.js             |   46 ++
 lib/equiv.js        |  272 +++++------
 lib/node-path.js    |  782 ++++++++++++++---------------
 lib/path-visitor.js |  683 +++++++++++++-------------
 lib/path.js         |  577 +++++++++++-----------
 lib/scope.js        |  560 ++++++++++-----------
 lib/shared.js       |   79 +--
 lib/types.js        | 1349 ++++++++++++++++++++++++++-------------------------
 main.js             |   48 +-
 package.json        |    4 +-
 test/run.js         |   12 +-
 21 files changed, 3505 insertions(+), 3396 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 6875ba4..4bce056 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,6 @@
 language: node_js
 node_js:
+  - "6.0"
   - "5.0"
   - "4.0"
   - "iojs"
diff --git a/def/babel.js b/def/babel.js
index 608ffbc..3d9352b 100644
--- a/def/babel.js
+++ b/def/babel.js
@@ -1,105 +1,107 @@
-require("./es7");
-
-var types = require("../lib/types");
-var defaults = require("../lib/shared").defaults;
-var def = types.Type.def;
-var or = types.Type.or;
-
-def("Noop")
-  .bases("Node")
-  .build();
-
-def("DoExpression")
-  .bases("Expression")
-  .build("body")
-  .field("body", [def("Statement")]);
-
-def("Super")
-  .bases("Expression")
-  .build();
-
-def("BindExpression")
-  .bases("Expression")
-  .build("object", "callee")
-  .field("object", or(def("Expression"), null))
-  .field("callee", def("Expression"));
-
-def("Decorator")
-  .bases("Node")
-  .build("expression")
-  .field("expression", def("Expression"));
-
-def("Property")
-  .field("decorators",
-         or([def("Decorator")], null),
-         defaults["null"]);
-
-def("MethodDefinition")
-  .field("decorators",
-         or([def("Decorator")], null),
-         defaults["null"]);
-
-def("MetaProperty")
-  .bases("Expression")
-  .build("meta", "property")
-  .field("meta", def("Identifier"))
-  .field("property", def("Identifier"));
-
-def("ParenthesizedExpression")
-  .bases("Expression")
-  .build("expression")
-  .field("expression", def("Expression"));
-
-def("ImportSpecifier")
-  .bases("ModuleSpecifier")
-  .build("imported", "local")
-  .field("imported", def("Identifier"));
-
-def("ImportDefaultSpecifier")
-  .bases("ModuleSpecifier")
-  .build("local");
-
-def("ImportNamespaceSpecifier")
-  .bases("ModuleSpecifier")
-  .build("local");
-
-def("ExportDefaultDeclaration")
-  .bases("Declaration")
-  .build("declaration")
-  .field("declaration", or(def("Declaration"), def("Expression")));
-
-def("ExportNamedDeclaration")
-  .bases("Declaration")
-  .build("declaration", "specifiers", "source")
-  .field("declaration", or(def("Declaration"), null))
-  .field("specifiers", [def("ExportSpecifier")], defaults.emptyArray)
-  .field("source", or(def("Literal"), null), defaults["null"]);
-
-def("ExportSpecifier")
-  .bases("ModuleSpecifier")
-  .build("local", "exported")
-  .field("exported", def("Identifier"));
-
-def("ExportNamespaceSpecifier")
-  .bases("Specifier")
-  .build("exported")
-  .field("exported", def("Identifier"));
-
-def("ExportDefaultSpecifier")
-  .bases("Specifier")
-  .build("exported")
-  .field("exported", def("Identifier"));
-
-def("ExportAllDeclaration")
-  .bases("Declaration")
-  .build("exported", "source")
-  .field("exported", or(def("Identifier"), null))
-  .field("source", def("Literal"));
-
-def("CommentBlock")
-    .bases("Comment")
-    .build("value", /*optional:*/ "leading", "trailing");
-
-def("CommentLine")
-    .bases("Comment")
-    .build("value", /*optional:*/ "leading", "trailing");
+module.exports = function (fork) {
+    fork.use(require("./es7"));
+
+    var types = fork.use(require("../lib/types"));
+    var defaults = fork.use(require("../lib/shared")).defaults;
+    var def = types.Type.def;
+    var or = types.Type.or;
+
+    def("Noop")
+        .bases("Node")
+        .build();
+
+    def("DoExpression")
+        .bases("Expression")
+        .build("body")
+        .field("body", [def("Statement")]);
+
+    def("Super")
+        .bases("Expression")
+        .build();
+
+    def("BindExpression")
+        .bases("Expression")
+        .build("object", "callee")
+        .field("object", or(def("Expression"), null))
+        .field("callee", def("Expression"));
+
+    def("Decorator")
+        .bases("Node")
+        .build("expression")
+        .field("expression", def("Expression"));
+
+    def("Property")
+        .field("decorators",
+            or([def("Decorator")], null),
+            defaults["null"]);
+
+    def("MethodDefinition")
+        .field("decorators",
+            or([def("Decorator")], null),
+            defaults["null"]);
+
+    def("MetaProperty")
+        .bases("Expression")
+        .build("meta", "property")
+        .field("meta", def("Identifier"))
+        .field("property", def("Identifier"));
+
+    def("ParenthesizedExpression")
+        .bases("Expression")
+        .build("expression")
+        .field("expression", def("Expression"));
+
+    def("ImportSpecifier")
+        .bases("ModuleSpecifier")
+        .build("imported", "local")
+        .field("imported", def("Identifier"));
+
+    def("ImportDefaultSpecifier")
+        .bases("ModuleSpecifier")
+        .build("local");
+
+    def("ImportNamespaceSpecifier")
+        .bases("ModuleSpecifier")
+        .build("local");
+
+    def("ExportDefaultDeclaration")
+        .bases("Declaration")
+        .build("declaration")
+        .field("declaration", or(def("Declaration"), def("Expression")));
+
+    def("ExportNamedDeclaration")
+        .bases("Declaration")
+        .build("declaration", "specifiers", "source")
+        .field("declaration", or(def("Declaration"), null))
+        .field("specifiers", [def("ExportSpecifier")], defaults.emptyArray)
+        .field("source", or(def("Literal"), null), defaults["null"]);
+
+    def("ExportSpecifier")
+        .bases("ModuleSpecifier")
+        .build("local", "exported")
+        .field("exported", def("Identifier"));
+
+    def("ExportNamespaceSpecifier")
+        .bases("Specifier")
+        .build("exported")
+        .field("exported", def("Identifier"));
+
+    def("ExportDefaultSpecifier")
+        .bases("Specifier")
+        .build("exported")
+        .field("exported", def("Identifier"));
+
+    def("ExportAllDeclaration")
+        .bases("Declaration")
+        .build("exported", "source")
+        .field("exported", or(def("Identifier"), null))
+        .field("source", def("Literal"));
+
+    def("CommentBlock")
+        .bases("Comment")
+        .build("value", /*optional:*/ "leading", "trailing");
+
+    def("CommentLine")
+        .bases("Comment")
+        .build("value", /*optional:*/ "leading", "trailing");
+};
\ No newline at end of file
diff --git a/def/core.js b/def/core.js
index 2942c4b..800b317 100644
--- a/def/core.js
+++ b/def/core.js
@@ -1,367 +1,369 @@
-var types = require("../lib/types");
-var Type = types.Type;
-var def = Type.def;
-var or = Type.or;
-var shared = require("../lib/shared");
-var defaults = shared.defaults;
-var geq = shared.geq;
-
-// Abstract supertype of all syntactic entities that are allowed to have a
-// .loc field.
-def("Printable")
-    .field("loc", or(
-        def("SourceLocation"),
-        null
-    ), defaults["null"], true);
-
-def("Node")
-    .bases("Printable")
-    .field("type", String)
-    .field("comments", or(
-        [def("Comment")],
-        null
-    ), defaults["null"], true);
-
-def("SourceLocation")
-    .build("start", "end", "source")
-    .field("start", def("Position"))
-    .field("end", def("Position"))
-    .field("source", or(String, null), defaults["null"]);
-
-def("Position")
-    .build("line", "column")
-    .field("line", geq(1))
-    .field("column", geq(0));
-
-def("File")
-    .bases("Node")
-    .build("program")
-    .field("program", def("Program"));
-
-def("Program")
-    .bases("Node")
-    .build("body")
-    .field("body", [def("Statement")]);
-
-def("Function")
-    .bases("Node")
-    .field("id", or(def("Identifier"), null), defaults["null"])
-    .field("params", [def("Pattern")])
-    .field("body", def("BlockStatement"));
-
-def("Statement").bases("Node");
+module.exports = function (fork) {
+    var types = fork.use(require("../lib/types"));
+    var Type = types.Type;
+    var def = Type.def;
+    var or = Type.or;
+    var shared = fork.use(require("../lib/shared"));
+    var defaults = shared.defaults;
+    var geq = shared.geq;
+
+    // Abstract supertype of all syntactic entities that are allowed to have a
+    // .loc field.
+    def("Printable")
+        .field("loc", or(
+            def("SourceLocation"),
+            null
+        ), defaults["null"], true);
+
+    def("Node")
+        .bases("Printable")
+        .field("type", String)
+        .field("comments", or(
+            [def("Comment")],
+            null
+        ), defaults["null"], true);
+
+    def("SourceLocation")
+        .build("start", "end", "source")
+        .field("start", def("Position"))
+        .field("end", def("Position"))
+        .field("source", or(String, null), defaults["null"]);
+
+    def("Position")
+        .build("line", "column")
+        .field("line", geq(1))
+        .field("column", geq(0));
+
+    def("File")
+        .bases("Node")
+        .build("program")
+        .field("program", def("Program"));
+
+    def("Program")
+        .bases("Node")
+        .build("body")
+        .field("body", [def("Statement")]);
+
+    def("Function")
+        .bases("Node")
+        .field("id", or(def("Identifier"), null), defaults["null"])
+        .field("params", [def("Pattern")])
+        .field("body", def("BlockStatement"));
+
+    def("Statement").bases("Node");
 
 // The empty .build() here means that an EmptyStatement can be constructed
 // (i.e. it's not abstract) but that it needs no arguments.
-def("EmptyStatement").bases("Statement").build();
-
-def("BlockStatement")
-    .bases("Statement")
-    .build("body")
-    .field("body", [def("Statement")]);
-
-// TODO Figure out how to silently coerce Expressions to
-// ExpressionStatements where a Statement was expected.
-def("ExpressionStatement")
-    .bases("Statement")
-    .build("expression")
-    .field("expression", def("Expression"));
-
-def("IfStatement")
-    .bases("Statement")
-    .build("test", "consequent", "alternate")
-    .field("test", def("Expression"))
-    .field("consequent", def("Statement"))
-    .field("alternate", or(def("Statement"), null), defaults["null"]);
-
-def("LabeledStatement")
-    .bases("Statement")
-    .build("label", "body")
-    .field("label", def("Identifier"))
-    .field("body", def("Statement"));
-
-def("BreakStatement")
-    .bases("Statement")
-    .build("label")
-    .field("label", or(def("Identifier"), null), defaults["null"]);
-
-def("ContinueStatement")
-    .bases("Statement")
-    .build("label")
-    .field("label", or(def("Identifier"), null), defaults["null"]);
-
-def("WithStatement")
-    .bases("Statement")
-    .build("object", "body")
-    .field("object", def("Expression"))
-    .field("body", def("Statement"));
-
-def("SwitchStatement")
-    .bases("Statement")
-    .build("discriminant", "cases", "lexical")
-    .field("discriminant", def("Expression"))
-    .field("cases", [def("SwitchCase")])
-    .field("lexical", Boolean, defaults["false"]);
-
-def("ReturnStatement")
-    .bases("Statement")
-    .build("argument")
-    .field("argument", or(def("Expression"), null));
-
-def("ThrowStatement")
-    .bases("Statement")
-    .build("argument")
-    .field("argument", def("Expression"));
-
-def("TryStatement")
-    .bases("Statement")
-    .build("block", "handler", "finalizer")
-    .field("block", def("BlockStatement"))
-    .field("handler", or(def("CatchClause"), null), function() {
-        return this.handlers && this.handlers[0] || null;
-    })
-    .field("handlers", [def("CatchClause")], function() {
-        return this.handler ? [this.handler] : [];
-    }, true) // Indicates this field is hidden from eachField iteration.
-    .field("guardedHandlers", [def("CatchClause")], defaults.emptyArray)
-    .field("finalizer", or(def("BlockStatement"), null), defaults["null"]);
-
-def("CatchClause")
-    .bases("Node")
-    .build("param", "guard", "body")
-    .field("param", def("Pattern"))
-    .field("guard", or(def("Expression"), null), defaults["null"])
-    .field("body", def("BlockStatement"));
-
-def("WhileStatement")
-    .bases("Statement")
-    .build("test", "body")
-    .field("test", def("Expression"))
-    .field("body", def("Statement"));
-
-def("DoWhileStatement")
-    .bases("Statement")
-    .build("body", "test")
-    .field("body", def("Statement"))
-    .field("test", def("Expression"));
-
-def("ForStatement")
-    .bases("Statement")
-    .build("init", "test", "update", "body")
-    .field("init", or(
-        def("VariableDeclaration"),
-        def("Expression"),
-        null))
-    .field("test", or(def("Expression"), null))
-    .field("update", or(def("Expression"), null))
-    .field("body", def("Statement"));
-
-def("ForInStatement")
-    .bases("Statement")
-    .build("left", "right", "body")
-    .field("left", or(
-        def("VariableDeclaration"),
-        def("Expression")))
-    .field("right", def("Expression"))
-    .field("body", def("Statement"));
-
-def("DebuggerStatement").bases("Statement").build();
-
-def("Declaration").bases("Statement");
-
-def("FunctionDeclaration")
-    .bases("Function", "Declaration")
-    .build("id", "params", "body")
-    .field("id", def("Identifier"));
-
-def("FunctionExpression")
-    .bases("Function", "Expression")
-    .build("id", "params", "body");
-
-def("VariableDeclaration")
-    .bases("Declaration")
-    .build("kind", "declarations")
-    .field("kind", or("var", "let", "const"))
-    .field("declarations", [def("VariableDeclarator")]);
-
-def("VariableDeclarator")
-    .bases("Node")
-    .build("id", "init")
-    .field("id", def("Pattern"))
-    .field("init", or(def("Expression"), null));
-
-// TODO Are all Expressions really Patterns?
-def("Expression").bases("Node", "Pattern");
-
-def("ThisExpression").bases("Expression").build();
-
-def("ArrayExpression")
-    .bases("Expression")
-    .build("elements")
-    .field("elements", [or(def("Expression"), null)]);
-
-def("ObjectExpression")
-    .bases("Expression")
-    .build("properties")
-    .field("properties", [def("Property")]);
-
-// TODO Not in the Mozilla Parser API, but used by Esprima.
-def("Property")
-    .bases("Node") // Want to be able to visit Property Nodes.
-    .build("kind", "key", "value")
-    .field("kind", or("init", "get", "set"))
-    .field("key", or(def("Literal"), def("Identifier")))
-    .field("value", def("Expression"));
-
-def("SequenceExpression")
-    .bases("Expression")
-    .build("expressions")
-    .field("expressions", [def("Expression")]);
-
-var UnaryOperator = or(
-    "-", "+", "!", "~",
-    "typeof", "void", "delete");
-
-def("UnaryExpression")
-    .bases("Expression")
-    .build("operator", "argument", "prefix")
-    .field("operator", UnaryOperator)
-    .field("argument", def("Expression"))
-    // Esprima doesn't bother with this field, presumably because it's
-    // always true for unary operators.
-    .field("prefix", Boolean, defaults["true"]);
-
-var BinaryOperator = or(
-    "==", "!=", "===", "!==",
-    "<", "<=", ">", ">=",
-    "<<", ">>", ">>>",
-    "+", "-", "*", "/", "%",
-    "&", // TODO Missing from the Parser API.
-    "|", "^", "in",
-    "instanceof", "..");
-
-def("BinaryExpression")
-    .bases("Expression")
-    .build("operator", "left", "right")
-    .field("operator", BinaryOperator)
-    .field("left", def("Expression"))
-    .field("right", def("Expression"));
-
-var AssignmentOperator = or(
-    "=", "+=", "-=", "*=", "/=", "%=",
-    "<<=", ">>=", ">>>=",
-    "|=", "^=", "&=");
-
-def("AssignmentExpression")
-    .bases("Expression")
-    .build("operator", "left", "right")
-    .field("operator", AssignmentOperator)
-    .field("left", def("Pattern"))
-    .field("right", def("Expression"));
-
-var UpdateOperator = or("++", "--");
-
-def("UpdateExpression")
-    .bases("Expression")
-    .build("operator", "argument", "prefix")
-    .field("operator", UpdateOperator)
-    .field("argument", def("Expression"))
-    .field("prefix", Boolean);
-
-var LogicalOperator = or("||", "&&");
-
-def("LogicalExpression")
-    .bases("Expression")
-    .build("operator", "left", "right")
-    .field("operator", LogicalOperator)
-    .field("left", def("Expression"))
-    .field("right", def("Expression"));
-
-def("ConditionalExpression")
-    .bases("Expression")
-    .build("test", "consequent", "alternate")
-    .field("test", def("Expression"))
-    .field("consequent", def("Expression"))
-    .field("alternate", def("Expression"));
-
-def("NewExpression")
-    .bases("Expression")
-    .build("callee", "arguments")
-    .field("callee", def("Expression"))
-    // The Mozilla Parser API gives this type as [or(def("Expression"),
-    // null)], but null values don't really make sense at the call site.
-    // TODO Report this nonsense.
-    .field("arguments", [def("Expression")]);
-
-def("CallExpression")
-    .bases("Expression")
-    .build("callee", "arguments")
-    .field("callee", def("Expression"))
-    // See comment for NewExpression above.
-    .field("arguments", [def("Expression")]);
-
-def("MemberExpression")
-    .bases("Expression")
-    .build("object", "property", "computed")
-    .field("object", def("Expression"))
-    .field("property", or(def("Identifier"), def("Expression")))
-    .field("computed", Boolean, function(){
-        var type = this.property.type;
-        if (type === 'Literal' ||
-            type === 'MemberExpression' ||
-            type === 'BinaryExpression') {
-            return true;
-        }
-        return false;
-    });
-
-def("Pattern").bases("Node");
-
-def("SwitchCase")
-    .bases("Node")
-    .build("test", "consequent")
-    .field("test", or(def("Expression"), null))
-    .field("consequent", [def("Statement")]);
-
-def("Identifier")
-    // But aren't Expressions and Patterns already Nodes? TODO Report this.
-    .bases("Node", "Expression", "Pattern")
-    .build("name")
-    .field("name", String);
-
-def("Literal")
-    // But aren't Expressions already Nodes? TODO Report this.
-    .bases("Node", "Expression")
-    .build("value")
-    .field("value", or(String, Boolean, null, Number, RegExp))
-    .field("regex", or({
-        pattern: String,
-        flags: String
-    }, null), function() {
-        if (this.value instanceof RegExp) {
-            var flags = "";
-
-            if (this.value.ignoreCase) flags += "i";
-            if (this.value.multiline) flags += "m";
-            if (this.value.global) flags += "g";
-
-            return {
-                pattern: this.value.source,
-                flags: flags
-            };
-        }
-
-        return null;
-    });
-
-// Abstract (non-buildable) comment supertype. Not a Node.
-def("Comment")
-    .bases("Printable")
-    .field("value", String)
-    // A .leading comment comes before the node, whereas a .trailing
-    // comment comes after it. These two fields should not both be true,
-    // but they might both be false when the comment falls inside a node
-    // and the node has no children for the comment to lead or trail,
-    // e.g. { /*dangling*/ }.
-    .field("leading", Boolean, defaults["true"])
-    .field("trailing", Boolean, defaults["false"]);
+    def("EmptyStatement").bases("Statement").build();
+
+    def("BlockStatement")
+        .bases("Statement")
+        .build("body")
+        .field("body", [def("Statement")]);
+
+    // TODO Figure out how to silently coerce Expressions to
+    // ExpressionStatements where a Statement was expected.
+    def("ExpressionStatement")
+        .bases("Statement")
+        .build("expression")
+        .field("expression", def("Expression"));
+
+    def("IfStatement")
+        .bases("Statement")
+        .build("test", "consequent", "alternate")
+        .field("test", def("Expression"))
+        .field("consequent", def("Statement"))
+        .field("alternate", or(def("Statement"), null), defaults["null"]);
+
+    def("LabeledStatement")
+        .bases("Statement")
+        .build("label", "body")
+        .field("label", def("Identifier"))
+        .field("body", def("Statement"));
+
+    def("BreakStatement")
+        .bases("Statement")
+        .build("label")
+        .field("label", or(def("Identifier"), null), defaults["null"]);
+
+    def("ContinueStatement")
+        .bases("Statement")
+        .build("label")
+        .field("label", or(def("Identifier"), null), defaults["null"]);
+
+    def("WithStatement")
+        .bases("Statement")
+        .build("object", "body")
+        .field("object", def("Expression"))
+        .field("body", def("Statement"));
+
+    def("SwitchStatement")
+        .bases("Statement")
+        .build("discriminant", "cases", "lexical")
+        .field("discriminant", def("Expression"))
+        .field("cases", [def("SwitchCase")])
+        .field("lexical", Boolean, defaults["false"]);
+
+    def("ReturnStatement")
+        .bases("Statement")
+        .build("argument")
+        .field("argument", or(def("Expression"), null));
+
+    def("ThrowStatement")
+        .bases("Statement")
+        .build("argument")
+        .field("argument", def("Expression"));
+
+    def("TryStatement")
+        .bases("Statement")
+        .build("block", "handler", "finalizer")
+        .field("block", def("BlockStatement"))
+        .field("handler", or(def("CatchClause"), null), function () {
+            return this.handlers && this.handlers[0] || null;
+        })
+        .field("handlers", [def("CatchClause")], function () {
+            return this.handler ? [this.handler] : [];
+        }, true) // Indicates this field is hidden from eachField iteration.
+        .field("guardedHandlers", [def("CatchClause")], defaults.emptyArray)
+        .field("finalizer", or(def("BlockStatement"), null), defaults["null"]);
+
+    def("CatchClause")
+        .bases("Node")
+        .build("param", "guard", "body")
+        .field("param", def("Pattern"))
+        .field("guard", or(def("Expression"), null), defaults["null"])
+        .field("body", def("BlockStatement"));
+
+    def("WhileStatement")
+        .bases("Statement")
+        .build("test", "body")
+        .field("test", def("Expression"))
+        .field("body", def("Statement"));
+
+    def("DoWhileStatement")
+        .bases("Statement")
+        .build("body", "test")
+        .field("body", def("Statement"))
+        .field("test", def("Expression"));
+
+    def("ForStatement")
+        .bases("Statement")
+        .build("init", "test", "update", "body")
+        .field("init", or(
+            def("VariableDeclaration"),
+            def("Expression"),
+            null))
+        .field("test", or(def("Expression"), null))
+        .field("update", or(def("Expression"), null))
+        .field("body", def("Statement"));
+
+    def("ForInStatement")
+        .bases("Statement")
+        .build("left", "right", "body")
+        .field("left", or(
+            def("VariableDeclaration"),
+            def("Expression")))
+        .field("right", def("Expression"))
+        .field("body", def("Statement"));
+
+    def("DebuggerStatement").bases("Statement").build();
+
+    def("Declaration").bases("Statement");
+
+    def("FunctionDeclaration")
+        .bases("Function", "Declaration")
+        .build("id", "params", "body")
+        .field("id", def("Identifier"));
+
+    def("FunctionExpression")
+        .bases("Function", "Expression")
+        .build("id", "params", "body");
+
+    def("VariableDeclaration")
+        .bases("Declaration")
+        .build("kind", "declarations")
+        .field("kind", or("var", "let", "const"))
+        .field("declarations", [def("VariableDeclarator")]);
+
+    def("VariableDeclarator")
+        .bases("Node")
+        .build("id", "init")
+        .field("id", def("Pattern"))
+        .field("init", or(def("Expression"), null));
+
+    // TODO Are all Expressions really Patterns?
+    def("Expression").bases("Node", "Pattern");
+
+    def("ThisExpression").bases("Expression").build();
+
+    def("ArrayExpression")
+        .bases("Expression")
+        .build("elements")
+        .field("elements", [or(def("Expression"), null)]);
+
+    def("ObjectExpression")
+        .bases("Expression")
+        .build("properties")
+        .field("properties", [def("Property")]);
+
+    // TODO Not in the Mozilla Parser API, but used by Esprima.
+    def("Property")
+        .bases("Node") // Want to be able to visit Property Nodes.
+        .build("kind", "key", "value")
+        .field("kind", or("init", "get", "set"))
+        .field("key", or(def("Literal"), def("Identifier")))
+        .field("value", def("Expression"));
+
+    def("SequenceExpression")
+        .bases("Expression")
+        .build("expressions")
+        .field("expressions", [def("Expression")]);
+
+    var UnaryOperator = or(
+        "-", "+", "!", "~",
+        "typeof", "void", "delete");
+
+    def("UnaryExpression")
+        .bases("Expression")
+        .build("operator", "argument", "prefix")
+        .field("operator", UnaryOperator)
+        .field("argument", def("Expression"))
+        // Esprima doesn't bother with this field, presumably because it's
+        // always true for unary operators.
+        .field("prefix", Boolean, defaults["true"]);
+
+    var BinaryOperator = or(
+        "==", "!=", "===", "!==",
+        "<", "<=", ">", ">=",
+        "<<", ">>", ">>>",
+        "+", "-", "*", "/", "%",
+        "&", // TODO Missing from the Parser API.
+        "|", "^", "in",
+        "instanceof", "..");
+
+    def("BinaryExpression")
+        .bases("Expression")
+        .build("operator", "left", "right")
+        .field("operator", BinaryOperator)
+        .field("left", def("Expression"))
+        .field("right", def("Expression"));
+
+    var AssignmentOperator = or(
+        "=", "+=", "-=", "*=", "/=", "%=",
+        "<<=", ">>=", ">>>=",
+        "|=", "^=", "&=");
+
+    def("AssignmentExpression")
+        .bases("Expression")
+        .build("operator", "left", "right")
+        .field("operator", AssignmentOperator)
+        .field("left", def("Pattern"))
+        .field("right", def("Expression"));
+
+    var UpdateOperator = or("++", "--");
+
+    def("UpdateExpression")
+        .bases("Expression")
+        .build("operator", "argument", "prefix")
+        .field("operator", UpdateOperator)
+        .field("argument", def("Expression"))
+        .field("prefix", Boolean);
+
+    var LogicalOperator = or("||", "&&");
+
+    def("LogicalExpression")
+        .bases("Expression")
+        .build("operator", "left", "right")
+        .field("operator", LogicalOperator)
+        .field("left", def("Expression"))
+        .field("right", def("Expression"));
+
+    def("ConditionalExpression")
+        .bases("Expression")
+        .build("test", "consequent", "alternate")
+        .field("test", def("Expression"))
+        .field("consequent", def("Expression"))
+        .field("alternate", def("Expression"));
+
+    def("NewExpression")
+        .bases("Expression")
+        .build("callee", "arguments")
+        .field("callee", def("Expression"))
+        // The Mozilla Parser API gives this type as [or(def("Expression"),
+        // null)], but null values don't really make sense at the call site.
+        // TODO Report this nonsense.
+        .field("arguments", [def("Expression")]);
+
+    def("CallExpression")
+        .bases("Expression")
+        .build("callee", "arguments")
+        .field("callee", def("Expression"))
+        // See comment for NewExpression above.
+        .field("arguments", [def("Expression")]);
+
+    def("MemberExpression")
+        .bases("Expression")
+        .build("object", "property", "computed")
+        .field("object", def("Expression"))
+        .field("property", or(def("Identifier"), def("Expression")))
+        .field("computed", Boolean, function () {
+            var type = this.property.type;
+            if (type === 'Literal' ||
+                type === 'MemberExpression' ||
+                type === 'BinaryExpression') {
+                return true;
+            }
+            return false;
+        });
+
+    def("Pattern").bases("Node");
+
+    def("SwitchCase")
+        .bases("Node")
+        .build("test", "consequent")
+        .field("test", or(def("Expression"), null))
+        .field("consequent", [def("Statement")]);
+
+    def("Identifier")
+        // But aren't Expressions and Patterns already Nodes? TODO Report this.
+        .bases("Node", "Expression", "Pattern")
+        .build("name")
+        .field("name", String);
+
+    def("Literal")
+        // But aren't Expressions already Nodes? TODO Report this.
+        .bases("Node", "Expression")
+        .build("value")
+        .field("value", or(String, Boolean, null, Number, RegExp))
+        .field("regex", or({
+            pattern: String,
+            flags: String
+        }, null), function () {
+            if (this.value instanceof RegExp) {
+                var flags = "";
+
+                if (this.value.ignoreCase) flags += "i";
+                if (this.value.multiline) flags += "m";
+                if (this.value.global) flags += "g";
+
+                return {
+                    pattern: this.value.source,
+                    flags: flags
+                };
+            }
+
+            return null;
+        });
+
+    // Abstract (non-buildable) comment supertype. Not a Node.
+    def("Comment")
+        .bases("Printable")
+        .field("value", String)
+        // A .leading comment comes before the node, whereas a .trailing
+        // comment comes after it. These two fields should not both be true,
+        // but they might both be false when the comment falls inside a node
+        // and the node has no children for the comment to lead or trail,
+        // e.g. { /*dangling*/ }.
+        .field("leading", Boolean, defaults["true"])
+        .field("trailing", Boolean, defaults["false"]);
+};
\ No newline at end of file
diff --git a/def/e4x.js b/def/e4x.js
index 22ac0ff..59403eb 100644
--- a/def/e4x.js
+++ b/def/e4x.js
@@ -1,84 +1,86 @@
-require("./core");
-var types = require("../lib/types");
-var def = types.Type.def;
-var or = types.Type.or;
-
-// Note that none of these types are buildable because the Mozilla Parser
-// API doesn't specify any builder functions, and nobody uses E4X anymore.
-
-def("XMLDefaultDeclaration")
-    .bases("Declaration")
-    .field("namespace", def("Expression"));
-
-def("XMLAnyName").bases("Expression");
-
-def("XMLQualifiedIdentifier")
-    .bases("Expression")
-    .field("left", or(def("Identifier"), def("XMLAnyName")))
-    .field("right", or(def("Identifier"), def("Expression")))
-    .field("computed", Boolean);
-
-def("XMLFunctionQualifiedIdentifier")
-    .bases("Expression")
-    .field("right", or(def("Identifier"), def("Expression")))
-    .field("computed", Boolean);
-
-def("XMLAttributeSelector")
-    .bases("Expression")
-    .field("attribute", def("Expression"));
-
-def("XMLFilterExpression")
-    .bases("Expression")
-    .field("left", def("Expression"))
-    .field("right", def("Expression"));
-
-def("XMLElement")
-    .bases("XML", "Expression")
-    .field("contents", [def("XML")]);
-
-def("XMLList")
-    .bases("XML", "Expression")
-    .field("contents", [def("XML")]);
-
-def("XML").bases("Node");
-
-def("XMLEscape")
-    .bases("XML")
-    .field("expression", def("Expression"));
-
-def("XMLText")
-    .bases("XML")
-    .field("text", String);
-
-def("XMLStartTag")
-    .bases("XML")
-    .field("contents", [def("XML")]);
-
-def("XMLEndTag")
-    .bases("XML")
-    .field("contents", [def("XML")]);
-
-def("XMLPointTag")
-    .bases("XML")
-    .field("contents", [def("XML")]);
-
-def("XMLName")
-    .bases("XML")
-    .field("contents", or(String, [def("XML")]));
-
-def("XMLAttribute")
-    .bases("XML")
-    .field("value", String);
-
-def("XMLCdata")
-    .bases("XML")
-    .field("contents", String);
-
-def("XMLComment")
-    .bases("XML")
-    .field("contents", String);
-
-def("XMLProcessingInstruction")
-    .bases("XML")
-    .field("target", String)
-    .field("contents", or(String, null));
+module.exports = function (fork) {
+    fork.use(require("./core"));
+    var types = fork.use(require("../lib/types"));
+    var def = types.Type.def;
+    var or = types.Type.or;
+
+    // Note that none of these types are buildable because the Mozilla Parser
+    // API doesn't specify any builder functions, and nobody uses E4X anymore.
+
+    def("XMLDefaultDeclaration")
+        .bases("Declaration")
+        .field("namespace", def("Expression"));
+
+    def("XMLAnyName").bases("Expression");
+
+    def("XMLQualifiedIdentifier")
+        .bases("Expression")
+        .field("left", or(def("Identifier"), def("XMLAnyName")))
+        .field("right", or(def("Identifier"), def("Expression")))
+        .field("computed", Boolean);
+
+    def("XMLFunctionQualifiedIdentifier")
+        .bases("Expression")
+        .field("right", or(def("Identifier"), def("Expression")))
+        .field("computed", Boolean);
+
+    def("XMLAttributeSelector")
+        .bases("Expression")
+        .field("attribute", def("Expression"));
+
+    def("XMLFilterExpression")
+        .bases("Expression")
+        .field("left", def("Expression"))
+        .field("right", def("Expression"));
+
+    def("XMLElement")
+        .bases("XML", "Expression")
+        .field("contents", [def("XML")]);
+
+    def("XMLList")
+        .bases("XML", "Expression")
+        .field("contents", [def("XML")]);
+
+    def("XML").bases("Node");
+
+    def("XMLEscape")
+        .bases("XML")
+        .field("expression", def("Expression"));
+
+    def("XMLText")
+        .bases("XML")
+        .field("text", String);
+
+    def("XMLStartTag")
+        .bases("XML")
+        .field("contents", [def("XML")]);
+
+    def("XMLEndTag")
+        .bases("XML")
+        .field("contents", [def("XML")]);
+
+    def("XMLPointTag")
+        .bases("XML")
+        .field("contents", [def("XML")]);
+
+    def("XMLName")
+        .bases("XML")
+        .field("contents", or(String, [def("XML")]));
+
+    def("XMLAttribute")
+        .bases("XML")
+        .field("value", String);
+
+    def("XMLCdata")
+        .bases("XML")
+        .field("contents", String);
+
+    def("XMLComment")
+        .bases("XML")
+        .field("contents", String);
+
+    def("XMLProcessingInstruction")
+        .bases("XML")
+        .field("target", String)
+        .field("contents", or(String, null));
+};
\ No newline at end of file
diff --git a/def/es6.js b/def/es6.js
index b73bc07..284fa54 100644
--- a/def/es6.js
+++ b/def/es6.js
@@ -1,217 +1,219 @@
-require("./core");
-var types = require("../lib/types");
-var def = types.Type.def;
-var or = types.Type.or;
-var defaults = require("../lib/shared").defaults;
-
-def("Function")
-    .field("generator", Boolean, defaults["false"])
-    .field("expression", Boolean, defaults["false"])
-    .field("defaults", [or(def("Expression"), null)], defaults.emptyArray)
-    // TODO This could be represented as a RestElement in .params.
-    .field("rest", or(def("Identifier"), null), defaults["null"]);
-
-// The ESTree way of representing a ...rest parameter.
-def("RestElement")
-    .bases("Pattern")
-    .build("argument")
-    .field("argument", def("Pattern"));
-
-def("SpreadElementPattern")
-    .bases("Pattern")
-    .build("argument")
-    .field("argument", def("Pattern"));
-
-def("FunctionDeclaration")
-    .build("id", "params", "body", "generator", "expression");
-
-def("FunctionExpression")
-    .build("id", "params", "body", "generator", "expression");
-
-// The Parser API calls this ArrowExpression, but Esprima and all other
-// actual parsers use ArrowFunctionExpression.
-def("ArrowFunctionExpression")
-    .bases("Function", "Expression")
-    .build("params", "body", "expression")
-    // The forced null value here is compatible with the overridden
-    // definition of the "id" field in the Function interface.
-    .field("id", null, defaults["null"])
-    // Arrow function bodies are allowed to be expressions.
-    .field("body", or(def("BlockStatement"), def("Expression")))
-    // The current spec forbids arrow generators, so I have taken the
-    // liberty of enforcing that. TODO Report this.
-    .field("generator", false, defaults["false"]);
-
-def("YieldExpression")
-    .bases("Expression")
-    .build("argument", "delegate")
-    .field("argument", or(def("Expression"), null))
-    .field("delegate", Boolean, defaults["false"]);
-
-def("GeneratorExpression")
-    .bases("Expression")
-    .build("body", "blocks", "filter")
-    .field("body", def("Expression"))
-    .field("blocks", [def("ComprehensionBlock")])
-    .field("filter", or(def("Expression"), null));
-
-def("ComprehensionExpression")
-    .bases("Expression")
-    .build("body", "blocks", "filter")
-    .field("body", def("Expression"))
-    .field("blocks", [def("ComprehensionBlock")])
-    .field("filter", or(def("Expression"), null));
-
-def("ComprehensionBlock")
-    .bases("Node")
-    .build("left", "right", "each")
-    .field("left", def("Pattern"))
-    .field("right", def("Expression"))
-    .field("each", Boolean);
-
-def("Property")
-    .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
-    .field("value", or(def("Expression"), def("Pattern")))
-    .field("method", Boolean, defaults["false"])
-    .field("shorthand", Boolean, defaults["false"])
-    .field("computed", Boolean, defaults["false"]);
-
-def("PropertyPattern")
-    .bases("Pattern")
-    .build("key", "pattern")
-    .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
-    .field("pattern", def("Pattern"))
-    .field("computed", Boolean, defaults["false"]);
-
-def("ObjectPattern")
-    .bases("Pattern")
-    .build("properties")
-    .field("properties", [or(def("PropertyPattern"), def("Property"))]);
-
-def("ArrayPattern")
-    .bases("Pattern")
-    .build("elements")
-    .field("elements", [or(def("Pattern"), null)]);
-
-def("MethodDefinition")
-    .bases("Declaration")
-    .build("kind", "key", "value", "static")
-    .field("kind", or("constructor", "method", "get", "set"))
-    .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
-    .field("value", def("Function"))
-    .field("computed", Boolean, defaults["false"])
-    .field("static", Boolean, defaults["false"]);
-
-def("SpreadElement")
-    .bases("Node")
-    .build("argument")
-    .field("argument", def("Expression"));
-
-def("ArrayExpression")
-    .field("elements", [or(
+module.exports = function (fork) {
+    fork.use(require("./core"));
+    var types = fork.use(require("../lib/types"));
+    var def = types.Type.def;
+    var or = types.Type.or;
+    var defaults = fork.use(require("../lib/shared")).defaults;
+
+    def("Function")
+      .field("generator", Boolean, defaults["false"])
+      .field("expression", Boolean, defaults["false"])
+      .field("defaults", [or(def("Expression"), null)], defaults.emptyArray)
+      // TODO This could be represented as a RestElement in .params.
+      .field("rest", or(def("Identifier"), null), defaults["null"]);
+
+    // The ESTree way of representing a ...rest parameter.
+    def("RestElement")
+      .bases("Pattern")
+      .build("argument")
+      .field("argument", def("Pattern"));
+
+    def("SpreadElementPattern")
+      .bases("Pattern")
+      .build("argument")
+      .field("argument", def("Pattern"));
+
+    def("FunctionDeclaration")
+      .build("id", "params", "body", "generator", "expression");
+
+    def("FunctionExpression")
+      .build("id", "params", "body", "generator", "expression");
+
+    // The Parser API calls this ArrowExpression, but Esprima and all other
+    // actual parsers use ArrowFunctionExpression.
+    def("ArrowFunctionExpression")
+      .bases("Function", "Expression")
+      .build("params", "body", "expression")
+      // The forced null value here is compatible with the overridden
+      // definition of the "id" field in the Function interface.
+      .field("id", null, defaults["null"])
+      // Arrow function bodies are allowed to be expressions.
+      .field("body", or(def("BlockStatement"), def("Expression")))
+      // The current spec forbids arrow generators, so I have taken the
+      // liberty of enforcing that. TODO Report this.
+      .field("generator", false, defaults["false"]);
+
+    def("YieldExpression")
+      .bases("Expression")
+      .build("argument", "delegate")
+      .field("argument", or(def("Expression"), null))
+      .field("delegate", Boolean, defaults["false"]);
+
+    def("GeneratorExpression")
+      .bases("Expression")
+      .build("body", "blocks", "filter")
+      .field("body", def("Expression"))
+      .field("blocks", [def("ComprehensionBlock")])
+      .field("filter", or(def("Expression"), null));
+
+    def("ComprehensionExpression")
+      .bases("Expression")
+      .build("body", "blocks", "filter")
+      .field("body", def("Expression"))
+      .field("blocks", [def("ComprehensionBlock")])
+      .field("filter", or(def("Expression"), null));
+
+    def("ComprehensionBlock")
+      .bases("Node")
+      .build("left", "right", "each")
+      .field("left", def("Pattern"))
+      .field("right", def("Expression"))
+      .field("each", Boolean);
+
+    def("Property")
+      .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
+      .field("value", or(def("Expression"), def("Pattern")))
+      .field("method", Boolean, defaults["false"])
+      .field("shorthand", Boolean, defaults["false"])
+      .field("computed", Boolean, defaults["false"]);
+
+    def("PropertyPattern")
+      .bases("Pattern")
+      .build("key", "pattern")
+      .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
+      .field("pattern", def("Pattern"))
+      .field("computed", Boolean, defaults["false"]);
+
+    def("ObjectPattern")
+      .bases("Pattern")
+      .build("properties")
+      .field("properties", [or(def("PropertyPattern"), def("Property"))]);
+
+    def("ArrayPattern")
+      .bases("Pattern")
+      .build("elements")
+      .field("elements", [or(def("Pattern"), null)]);
+
+    def("MethodDefinition")
+      .bases("Declaration")
+      .build("kind", "key", "value", "static")
+      .field("kind", or("constructor", "method", "get", "set"))
+      .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
+      .field("value", def("Function"))
+      .field("computed", Boolean, defaults["false"])
+      .field("static", Boolean, defaults["false"]);
+
+    def("SpreadElement")
+      .bases("Node")
+      .build("argument")
+      .field("argument", def("Expression"));
+
+    def("ArrayExpression")
+      .field("elements", [or(
         def("Expression"),
         def("SpreadElement"),
         def("RestElement"),
         null
-    )]);
-
-def("NewExpression")
-    .field("arguments", [or(def("Expression"), def("SpreadElement"))]);
-
-def("CallExpression")
-    .field("arguments", [or(def("Expression"), def("SpreadElement"))]);
-
-// Note: this node type is *not* an AssignmentExpression with a Pattern on
-// the left-hand side! The existing AssignmentExpression type already
-// supports destructuring assignments. AssignmentPattern nodes may appear
-// wherever a Pattern is allowed, and the right-hand side represents a
-// default value to be destructured against the left-hand side, if no
-// value is otherwise provided. For example: default parameter values.
-def("AssignmentPattern")
-    .bases("Pattern")
-    .build("left", "right")
-    .field("left", def("Pattern"))
-    .field("right", def("Expression"));
-
-var ClassBodyElement = or(
-    def("MethodDefinition"),
-    def("VariableDeclarator"),
-    def("ClassPropertyDefinition"),
+      )]);
+
+    def("NewExpression")
+      .field("arguments", [or(def("Expression"), def("SpreadElement"))]);
+
+    def("CallExpression")
+      .field("arguments", [or(def("Expression"), def("SpreadElement"))]);
+
+    // Note: this node type is *not* an AssignmentExpression with a Pattern on
+    // the left-hand side! The existing AssignmentExpression type already
+    // supports destructuring assignments. AssignmentPattern nodes may appear
+    // wherever a Pattern is allowed, and the right-hand side represents a
+    // default value to be destructured against the left-hand side, if no
+    // value is otherwise provided. For example: default parameter values.
+    def("AssignmentPattern")
+      .bases("Pattern")
+      .build("left", "right")
+      .field("left", def("Pattern"))
+      .field("right", def("Expression"));
+
+    var ClassBodyElement = or(
+      def("MethodDefinition"),
+      def("VariableDeclarator"),
+      def("ClassPropertyDefinition"),
+      def("ClassProperty")
+    );
+
     def("ClassProperty")
-);
-
-def("ClassProperty")
-  .bases("Declaration")
-  .build("key")
-  .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
-  .field("computed", Boolean, defaults["false"]);
-
-def("ClassPropertyDefinition") // static property
-    .bases("Declaration")
-    .build("definition")
-    // Yes, Virginia, circular definitions are permitted.
-    .field("definition", ClassBodyElement);
-
-def("ClassBody")
-    .bases("Declaration")
-    .build("body")
-    .field("body", [ClassBodyElement]);
-
-def("ClassDeclaration")
-    .bases("Declaration")
-    .build("id", "body", "superClass")
-    .field("id", or(def("Identifier"), null))
-    .field("body", def("ClassBody"))
-    .field("superClass", or(def("Expression"), null), defaults["null"]);
-
-def("ClassExpression")
-    .bases("Expression")
-    .build("id", "body", "superClass")
-    .field("id", or(def("Identifier"), null), defaults["null"])
-    .field("body", def("ClassBody"))
-    .field("superClass", or(def("Expression"), null), defaults["null"])
-    .field("implements", [def("ClassImplements")], defaults.emptyArray);
-
-def("ClassImplements")
-    .bases("Node")
-    .build("id")
-    .field("id", def("Identifier"))
-    .field("superClass", or(def("Expression"), null), defaults["null"]);
-
-// Specifier and ModuleSpecifier are abstract non-standard types
-// introduced for definitional convenience.
-def("Specifier").bases("Node");
-
-// This supertype is shared/abused by both def/babel.js and
-// def/esprima.js. In the future, it will be possible to load only one set
-// of definitions appropriate for a given parser, but until then we must
-// rely on default functions to reconcile the conflicting AST formats.
-def("ModuleSpecifier")
-    .bases("Specifier")
-    // This local field is used by Babel/Acorn. It should not technically
-    // be optional in the Babel/Acorn AST format, but it must be optional
-    // in the Esprima AST format.
-    .field("local", or(def("Identifier"), null), defaults["null"])
-    // The id and name fields are used by Esprima. The id field should not
-    // technically be optional in the Esprima AST format, but it must be
-    // optional in the Babel/Acorn AST format.
-    .field("id", or(def("Identifier"), null), defaults["null"])
-    .field("name", or(def("Identifier"), null), defaults["null"]);
-
-def("TaggedTemplateExpression")
-    .bases("Expression")
-    .build("tag", "quasi")
-    .field("tag", def("Expression"))
-    .field("quasi", def("TemplateLiteral"));
-
-def("TemplateLiteral")
-    .bases("Expression")
-    .build("quasis", "expressions")
-    .field("quasis", [def("TemplateElement")])
-    .field("expressions", [def("Expression")]);
-
-def("TemplateElement")
-    .bases("Node")
-    .build("value", "tail")
-    .field("value", {"cooked": String, "raw": String})
-    .field("tail", Boolean);
+      .bases("Declaration")
+      .build("key")
+      .field("key", or(def("Literal"), def("Identifier"), def("Expression")))
+      .field("computed", Boolean, defaults["false"]);
+
+    def("ClassPropertyDefinition") // static property
+      .bases("Declaration")
+      .build("definition")
+      // Yes, Virginia, circular definitions are permitted.
+      .field("definition", ClassBodyElement);
+
+    def("ClassBody")
+      .bases("Declaration")
+      .build("body")
+      .field("body", [ClassBodyElement]);
+
+    def("ClassDeclaration")
+      .bases("Declaration")
+      .build("id", "body", "superClass")
+      .field("id", or(def("Identifier"), null))
+      .field("body", def("ClassBody"))
+      .field("superClass", or(def("Expression"), null), defaults["null"]);
+
+    def("ClassExpression")
+      .bases("Expression")
+      .build("id", "body", "superClass")
+      .field("id", or(def("Identifier"), null), defaults["null"])
+      .field("body", def("ClassBody"))
+      .field("superClass", or(def("Expression"), null), defaults["null"])
+      .field("implements", [def("ClassImplements")], defaults.emptyArray);
+
+    def("ClassImplements")
+      .bases("Node")
+      .build("id")
+      .field("id", def("Identifier"))
+      .field("superClass", or(def("Expression"), null), defaults["null"]);
+
+    // Specifier and ModuleSpecifier are abstract non-standard types
+    // introduced for definitional convenience.
+    def("Specifier").bases("Node");
+
+    // This supertype is shared/abused by both def/babel.js and
+    // def/esprima.js. In the future, it will be possible to load only one set
+    // of definitions appropriate for a given parser, but until then we must
+    // rely on default functions to reconcile the conflicting AST formats.
+    def("ModuleSpecifier")
+      .bases("Specifier")
+      // This local field is used by Babel/Acorn. It should not technically
+      // be optional in the Babel/Acorn AST format, but it must be optional
+      // in the Esprima AST format.
+      .field("local", or(def("Identifier"), null), defaults["null"])
+      // The id and name fields are used by Esprima. The id field should not
+      // technically be optional in the Esprima AST format, but it must be
+      // optional in the Babel/Acorn AST format.
+      .field("id", or(def("Identifier"), null), defaults["null"])
+      .field("name", or(def("Identifier"), null), defaults["null"]);
+
+    def("TaggedTemplateExpression")
+      .bases("Expression")
+      .build("tag", "quasi")
+      .field("tag", def("Expression"))
+      .field("quasi", def("TemplateLiteral"));
+
+    def("TemplateLiteral")
+      .bases("Expression")
+      .build("quasis", "expressions")
+      .field("quasis", [def("TemplateElement")])
+      .field("expressions", [def("Expression")]);
+
+    def("TemplateElement")
+      .bases("Node")
+      .build("value", "tail")
+      .field("value", {"cooked": String, "raw": String})
+      .field("tail", Boolean);
+};
diff --git a/def/es7.js b/def/es7.js
index e9c50bd..7760df6 100644
--- a/def/es7.js
+++ b/def/es7.js
@@ -1,36 +1,38 @@
-require("./es6");
+module.exports = function (fork) {
+    fork.use(require('./es6'));
 
-var types = require("../lib/types");
-var def = types.Type.def;
-var or = types.Type.or;
-var builtin = types.builtInTypes;
-var defaults = require("../lib/shared").defaults;
+    var types = fork.use(require("../lib/types"));
+    var def = types.Type.def;
+    var or = types.Type.or;
+    var builtin = types.builtInTypes;
+    var defaults = fork.use(require("../lib/shared")).defaults;
 
-def("Function")
-    .field("async", Boolean, defaults["false"]);
+    def("Function")
+      .field("async", Boolean, defaults["false"]);
 
-def("SpreadProperty")
-    .bases("Node")
-    .build("argument")
-    .field("argument", def("Expression"));
+    def("SpreadProperty")
+      .bases("Node")
+      .build("argument")
+      .field("argument", def("Expression"));
 
-def("ObjectExpression")
-    .field("properties", [or(def("Property"), def("SpreadProperty"))]);
+    def("ObjectExpression")
+      .field("properties", [or(def("Property"), def("SpreadProperty"))]);
 
-def("SpreadPropertyPattern")
-    .bases("Pattern")
-    .build("argument")
-    .field("argument", def("Pattern"));
+    def("SpreadPropertyPattern")
+      .bases("Pattern")
+      .build("argument")
+      .field("argument", def("Pattern"));
 
-def("ObjectPattern")
-    .field("properties", [or(
+    def("ObjectPattern")
+      .field("properties", [or(
         def("Property"),
         def("PropertyPattern"),
         def("SpreadPropertyPattern")
-    )]);
+      )]);
 
-def("AwaitExpression")
-    .bases("Expression")
-    .build("argument", "all")
-    .field("argument", or(def("Expression"), null))
-    .field("all", Boolean, defaults["false"]);
+    def("AwaitExpression")
+      .bases("Expression")
+      .build("argument", "all")
+      .field("argument", or(def("Expression"), null))
+      .field("all", Boolean, defaults["false"]);
+};
\ No newline at end of file
diff --git a/def/esprima.js b/def/esprima.js
index a27e38f..c0bc3e6 100644
--- a/def/esprima.js
+++ b/def/esprima.js
@@ -1,96 +1,98 @@
-require("./es7");
+module.exports = function (fork) {
+    fork.use(require("./es7"));
 
-var types = require("../lib/types");
-var defaults = require("../lib/shared").defaults;
-var def = types.Type.def;
-var or = types.Type.or;
+    var types = fork.use(require("../lib/types"));
+    var defaults = fork.use(require("../lib/shared")).defaults;
+    var def = types.Type.def;
+    var or = types.Type.or;
 
-def("VariableDeclaration")
-    .field("declarations", [or(
+    def("VariableDeclaration")
+      .field("declarations", [or(
         def("VariableDeclarator"),
         def("Identifier") // Esprima deviation.
-    )]);
+      )]);
 
-def("Property")
-    .field("value", or(
+    def("Property")
+      .field("value", or(
         def("Expression"),
         def("Pattern") // Esprima deviation.
-    ));
+      ));
 
-def("ArrayPattern")
-    .field("elements", [or(
+    def("ArrayPattern")
+      .field("elements", [or(
         def("Pattern"),
         def("SpreadElement"),
         null
-    )]);
+      )]);
 
-def("ObjectPattern")
-    .field("properties", [or(
+    def("ObjectPattern")
+      .field("properties", [or(
         def("Property"),
         def("PropertyPattern"),
         def("SpreadPropertyPattern"),
         def("SpreadProperty") // Used by Esprima.
-    )]);
+      )]);
 
 // Like ModuleSpecifier, except type:"ExportSpecifier" and buildable.
 // export {<id [as name]>} [from ...];
-def("ExportSpecifier")
-    .bases("ModuleSpecifier")
-    .build("id", "name");
+    def("ExportSpecifier")
+      .bases("ModuleSpecifier")
+      .build("id", "name");
 
 // export <*> from ...;
-def("ExportBatchSpecifier")
-    .bases("Specifier")
-    .build();
+    def("ExportBatchSpecifier")
+      .bases("Specifier")
+      .build();
 
 // Like ModuleSpecifier, except type:"ImportSpecifier" and buildable.
 // import {<id [as name]>} from ...;
-def("ImportSpecifier")
-    .bases("ModuleSpecifier")
-    .build("id", "name");
+    def("ImportSpecifier")
+      .bases("ModuleSpecifier")
+      .build("id", "name");
 
 // import <* as id> from ...;
-def("ImportNamespaceSpecifier")
-    .bases("ModuleSpecifier")
-    .build("id");
+    def("ImportNamespaceSpecifier")
+      .bases("ModuleSpecifier")
+      .build("id");
 
 // import <id> from ...;
-def("ImportDefaultSpecifier")
-    .bases("ModuleSpecifier")
-    .build("id");
+    def("ImportDefaultSpecifier")
+      .bases("ModuleSpecifier")
+      .build("id");
 
-def("ExportDeclaration")
-    .bases("Declaration")
-    .build("default", "declaration", "specifiers", "source")
-    .field("default", Boolean)
-    .field("declaration", or(
+    def("ExportDeclaration")
+      .bases("Declaration")
+      .build("default", "declaration", "specifiers", "source")
+      .field("default", Boolean)
+      .field("declaration", or(
         def("Declaration"),
         def("Expression"), // Implies default.
         null
-    ))
-    .field("specifiers", [or(
+      ))
+      .field("specifiers", [or(
         def("ExportSpecifier"),
         def("ExportBatchSpecifier")
-    )], defaults.emptyArray)
-    .field("source", or(
+      )], defaults.emptyArray)
+      .field("source", or(
         def("Literal"),
         null
-    ), defaults["null"]);
+      ), defaults["null"]);
 
-def("ImportDeclaration")
-    .bases("Declaration")
-    .build("specifiers", "source")
-    .field("specifiers", [or(
+    def("ImportDeclaration")
+      .bases("Declaration")
+      .build("specifiers", "source")
+      .field("specifiers", [or(
         def("ImportSpecifier"),
         def("ImportNamespaceSpecifier"),
         def("ImportDefaultSpecifier")
-    )], defaults.emptyArray)
-    .field("source", def("Literal"));
+      )], defaults.emptyArray)
+      .field("source", def("Literal"));
 
-def("Block")
-    .bases("Comment")
-    .build("value", /*optional:*/ "leading", "trailing");
+    def("Block")
+      .bases("Comment")
+      .build("value", /*optional:*/ "leading", "trailing");
 
-def("Line")
-    .bases("Comment")
-    .build("value", /*optional:*/ "leading", "trailing");
+    def("Line")
+      .bases("Comment")
+      .build("value", /*optional:*/ "leading", "trailing");
+};
\ No newline at end of file
diff --git a/def/flow.js b/def/flow.js
index b7774a6..513322f 100644
--- a/def/flow.js
+++ b/def/flow.js
@@ -1,269 +1,290 @@
-require("./es7");
-
-var types = require("../lib/types");
-var def = types.Type.def;
-var or = types.Type.or;
-var defaults = require("../lib/shared").defaults;
-
-// Type Annotations
-def("Type").bases("Node");
-
-def("AnyTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("MixedTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("VoidTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("NumberTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("NumberLiteralTypeAnnotation")
-  .bases("Type")
-  .build("value", "raw")
-  .field("value", Number)
-  .field("raw", String);
-
-def("StringTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("StringLiteralTypeAnnotation")
-  .bases("Type")
-  .build("value", "raw")
-  .field("value", String)
-  .field("raw", String);
-
-def("BooleanTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("BooleanLiteralTypeAnnotation")
-  .bases("Type")
-  .build("value", "raw")
-  .field("value", Boolean)
-  .field("raw", String);
-
-def("TypeAnnotation")
-  .bases("Node")
-  .build("typeAnnotation")
-  .field("typeAnnotation", def("Type"));
-
-def("NullableTypeAnnotation")
-  .bases("Type")
-  .build("typeAnnotation")
-  .field("typeAnnotation", def("Type"));
-
-def("NullLiteralTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("ThisTypeAnnotation")
-  .bases("Type")
-  .build();
-
-def("FunctionTypeAnnotation")
-  .bases("Type")
-  .build("params", "returnType", "rest", "typeParameters")
-  .field("params", [def("FunctionTypeParam")])
-  .field("returnType", def("Type"))
-  .field("rest", or(def("FunctionTypeParam"), null))
-  .field("typeParameters", or(def("TypeParameterDeclaration"), null));
-
-def("FunctionTypeParam")
-  .bases("Node")
-  .build("name", "typeAnnotation", "optional")
-  .field("name", def("Identifier"))
-  .field("typeAnnotation", def("Type"))
-  .field("optional", Boolean);
-
-def("ArrayTypeAnnotation")
-  .bases("Type")
-  .build("elementType")
-  .field("elementType", def("Type"));
-
-def("ObjectTypeAnnotation")
-  .bases("Type")
-  .build("properties")
-  .field("properties", [def("ObjectTypeProperty")])
-  .field("indexers", [def("ObjectTypeIndexer")], defaults.emptyArray)
-  .field("callProperties",
-         [def("ObjectTypeCallProperty")],
-         defaults.emptyArray);
-
-def("ObjectTypeProperty")
-  .bases("Node")
-  .build("key", "value", "optional")
-  .field("key", or(def("Literal"), def("Identifier")))
-  .field("value", def("Type"))
-  .field("optional", Boolean);
-
-def("ObjectTypeIndexer")
-  .bases("Node")
-  .build("id", "key", "value")
-  .field("id", def("Identifier"))
-  .field("key", def("Type"))
-  .field("value", def("Type"));
-
-def("ObjectTypeCallProperty")
-  .bases("Node")
-  .build("value")
-  .field("value", def("FunctionTypeAnnotation"))
-  .field("static", Boolean, defaults["false"]);
-
-def("QualifiedTypeIdentifier")
-  .bases("Node")
-  .build("qualification", "id")
-  .field("qualification",
-         or(def("Identifier"),
-            def("QualifiedTypeIdentifier")))
-  .field("id", def("Identifier"));
-
-def("GenericTypeAnnotation")
-  .bases("Type")
-  .build("id", "typeParameters")
-  .field("id", or(def("Identifier"), def("QualifiedTypeIdentifier")))
-  .field("typeParameters", or(def("TypeParameterInstantiation"), null));
-
-def("MemberTypeAnnotation")
-  .bases("Type")
-  .build("object", "property")
-  .field("object", def("Identifier"))
-  .field("property",
-         or(def("MemberTypeAnnotation"),
-            def("GenericTypeAnnotation")));
-
-def("UnionTypeAnnotation")
-  .bases("Type")
-  .build("types")
-  .field("types", [def("Type")]);
-
-def("IntersectionTypeAnnotation")
-  .bases("Type")
-  .build("types")
-  .field("types", [def("Type")]);
-
-def("TypeofTypeAnnotation")
-  .bases("Type")
-  .build("argument")
-  .field("argument", def("Type"));
-
-def("Identifier")
-  .field("typeAnnotation", or(def("TypeAnnotation"), null), defaults["null"]);
-
-def("TypeParameterDeclaration")
-  .bases("Node")
-  .build("params")
-  .field("params", [def("Identifier")]);
-
-def("TypeParameterInstantiation")
-  .bases("Node")
-  .build("params")
-  .field("params", [def("Type")]);
-
-def("Function")
-  .field("returnType",
-         or(def("TypeAnnotation"), null),
-         defaults["null"])
-  .field("typeParameters",
-         or(def("TypeParameterDeclaration"), null),
-         defaults["null"]);
-
-def("ClassProperty")
-  .build("key", "value", "typeAnnotation", "static")
-  .field("value", or(def("Expression"), null))
-  .field("typeAnnotation", or(def("TypeAnnotation"), null))
-  .field("static", Boolean, defaults["false"]);
-
-def("ClassImplements")
-  .field("typeParameters",
-         or(def("TypeParameterInstantiation"), null),
-         defaults["null"]);
-
-def("InterfaceDeclaration")
-  .bases("Declaration")
-  .build("id", "body", "extends")
-  .field("id", def("Identifier"))
-  .field("typeParameters",
-         or(def("TypeParameterDeclaration"), null),
-         defaults["null"])
-  .field("body", def("ObjectTypeAnnotation"))
-  .field("extends", [def("InterfaceExtends")]);
-
-def("DeclareInterface")
-  .bases("InterfaceDeclaration")
-  .build("id", "body", "extends");
-
-def("InterfaceExtends")
-  .bases("Node")
-  .build("id")
-  .field("id", def("Identifier"))
-  .field("typeParameters", or(def("TypeParameterInstantiation"), null));
-
-def("TypeAlias")
-  .bases("Declaration")
-  .build("id", "typeParameters", "right")
-  .field("id", def("Identifier"))
-  .field("typeParameters", or(def("TypeParameterDeclaration"), null))
-  .field("right", def("Type"));
-
-def("DeclareTypeAlias")
-  .bases("TypeAlias")
-  .build("id", "typeParameters", "right");
-
-def("TypeCastExpression")
-  .bases("Expression")
-  .build("expression", "typeAnnotation")
-  .field("expression", def("Expression"))
-  .field("typeAnnotation", def("TypeAnnotation"));
-
-def("TupleTypeAnnotation")
-  .bases("Type")
-  .build("types")
-  .field("types", [def("Type")]);
-
-def("DeclareVariable")
-  .bases("Statement")
-  .build("id")
-  .field("id", def("Identifier"));
-
-def("DeclareFunction")
-  .bases("Statement")
-  .build("id")
-  .field("id", def("Identifier"));
-
-def("DeclareClass")
-  .bases("InterfaceDeclaration")
-  .build("id");
-
-def("DeclareModule")
-  .bases("Statement")
-  .build("id", "body")
-  .field("id", or(def("Identifier"), def("Literal")))
-  .field("body", def("BlockStatement"));
-
-def("DeclareExportDeclaration")
-    .bases("Declaration")
-    .build("default", "declaration", "specifiers", "source")
-    .field("default", Boolean)
-    .field("declaration", or(
+module.exports = function (fork) {
+    fork.use(require("./es7"));
+
+    var types = fork.use(require("../lib/types"));
+    var def = types.Type.def;
+    var or = types.Type.or;
+    var defaults = fork.use(require("../lib/shared")).defaults;
+
+    // Type Annotations
+    def("Type").bases("Node");
+
+    def("AnyTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("MixedTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("VoidTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("NumberTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("NumberLiteralTypeAnnotation")
+      .bases("Type")
+      .build("value", "raw")
+      .field("value", Number)
+      .field("raw", String);
+
+    def("StringTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("StringLiteralTypeAnnotation")
+      .bases("Type")
+      .build("value", "raw")
+      .field("value", String)
+      .field("raw", String);
+
+    def("BooleanTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("BooleanLiteralTypeAnnotation")
+      .bases("Type")
+      .build("value", "raw")
+      .field("value", Boolean)
+      .field("raw", String);
+
+    def("TypeAnnotation")
+      .bases("Node")
+      .build("typeAnnotation")
+      .field("typeAnnotation", def("Type"));
+
+    def("NullableTypeAnnotation")
+      .bases("Type")
+      .build("typeAnnotation")
+      .field("typeAnnotation", def("Type"));
+
+    def("NullLiteralTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("NullTypeAnnotation")
+      .bases("Type")
+      .build();
+    
+    def("ThisTypeAnnotation")
+      .bases("Type")
+      .build();
+
+    def("ExistsTypeAnnotation")
+      .bases("Type")
+      .build();
+    
+    def("FunctionTypeAnnotation")
+      .bases("Type")
+      .build("params", "returnType", "rest", "typeParameters")
+      .field("params", [def("FunctionTypeParam")])
+      .field("returnType", def("Type"))
+      .field("rest", or(def("FunctionTypeParam"), null))
+      .field("typeParameters", or(def("TypeParameterDeclaration"), null));
+
+    def("FunctionTypeParam")
+      .bases("Node")
+      .build("name", "typeAnnotation", "optional")
+      .field("name", def("Identifier"))
+      .field("typeAnnotation", def("Type"))
+      .field("optional", Boolean);
+
+    def("ArrayTypeAnnotation")
+      .bases("Type")
+      .build("elementType")
+      .field("elementType", def("Type"));
+
+    def("ObjectTypeAnnotation")
+      .bases("Type")
+      .build("properties", "indexers", "callProperties")
+      .field("properties", [def("ObjectTypeProperty")])
+      .field("indexers", [def("ObjectTypeIndexer")], defaults.emptyArray)
+      .field("callProperties",
+        [def("ObjectTypeCallProperty")],
+        defaults.emptyArray);
+
+    def("ObjectTypeProperty")
+      .bases("Node")
+      .build("key", "value", "optional")
+      .field("key", or(def("Literal"), def("Identifier")))
+      .field("value", def("Type"))
+      .field("optional", Boolean);
+
+    def("ObjectTypeIndexer")
+      .bases("Node")
+      .build("id", "key", "value")
+      .field("id", def("Identifier"))
+      .field("key", def("Type"))
+      .field("value", def("Type"));
+
+    def("ObjectTypeCallProperty")
+      .bases("Node")
+      .build("value")
+      .field("value", def("FunctionTypeAnnotation"))
+      .field("static", Boolean, defaults["false"]);
+
+    def("QualifiedTypeIdentifier")
+      .bases("Node")
+      .build("qualification", "id")
+      .field("qualification",
+        or(def("Identifier"),
+          def("QualifiedTypeIdentifier")))
+      .field("id", def("Identifier"));
+
+    def("GenericTypeAnnotation")
+      .bases("Type")
+      .build("id", "typeParameters")
+      .field("id", or(def("Identifier"), def("QualifiedTypeIdentifier")))
+      .field("typeParameters", or(def("TypeParameterInstantiation"), null));
+
+    def("MemberTypeAnnotation")
+      .bases("Type")
+      .build("object", "property")
+      .field("object", def("Identifier"))
+      .field("property",
+        or(def("MemberTypeAnnotation"),
+          def("GenericTypeAnnotation")));
+
+    def("UnionTypeAnnotation")
+      .bases("Type")
+      .build("types")
+      .field("types", [def("Type")]);
+
+    def("IntersectionTypeAnnotation")
+      .bases("Type")
+      .build("types")
+      .field("types", [def("Type")]);
+
+    def("TypeofTypeAnnotation")
+      .bases("Type")
+      .build("argument")
+      .field("argument", def("Type"));
+
+    def("Identifier")
+      .field("typeAnnotation", or(def("TypeAnnotation"), null), defaults["null"]);
+
+    def("TypeParameterDeclaration")
+      .bases("Node")
+      .build("params")
+      .field("params", [def("TypeParameter")]);
+
+    def("TypeParameterInstantiation")
+      .bases("Node")
+      .build("params")
+      .field("params", [def("Type")]);
+
+    def("TypeParameter")
+      .bases("Type")
+      .build("name", "variance", "bound")
+      .field("name", String)
+      .field("variance",
+        or("plus", "minus", null),
+        defaults["null"])
+      .field("bound",
+        or(def("TypeAnnotation"), null),
+        defaults["null"]);
+    
+    def("Function")
+      .field("returnType",
+        or(def("TypeAnnotation"), null),
+        defaults["null"])
+      .field("typeParameters",
+        or(def("TypeParameterDeclaration"), null),
+        defaults["null"]);
+
+    def("ClassProperty")
+      .build("key", "value", "typeAnnotation", "static")
+      .field("value", or(def("Expression"), null))
+      .field("typeAnnotation", or(def("TypeAnnotation"), null))
+      .field("static", Boolean, defaults["false"]);
+
+    def("ClassImplements")
+      .field("typeParameters",
+        or(def("TypeParameterInstantiation"), null),
+        defaults["null"]);
+
+    def("InterfaceDeclaration")
+      .bases("Declaration")
+      .build("id", "body", "extends")
+      .field("id", def("Identifier"))
+      .field("typeParameters",
+        or(def("TypeParameterDeclaration"), null),
+        defaults["null"])
+      .field("body", def("ObjectTypeAnnotation"))
+      .field("extends", [def("InterfaceExtends")]);
+
+    def("DeclareInterface")
+      .bases("InterfaceDeclaration")
+      .build("id", "body", "extends");
+
+    def("InterfaceExtends")
+      .bases("Node")
+      .build("id")
+      .field("id", def("Identifier"))
+      .field("typeParameters", or(def("TypeParameterInstantiation"), null));
+
+    def("TypeAlias")
+      .bases("Declaration")
+      .build("id", "typeParameters", "right")
+      .field("id", def("Identifier"))
+      .field("typeParameters", or(def("TypeParameterDeclaration"), null))
+      .field("right", def("Type"));
+
+    def("DeclareTypeAlias")
+      .bases("TypeAlias")
+      .build("id", "typeParameters", "right");
+
+    def("TypeCastExpression")
+      .bases("Expression")
+      .build("expression", "typeAnnotation")
+      .field("expression", def("Expression"))
+      .field("typeAnnotation", def("TypeAnnotation"));
+
+    def("TupleTypeAnnotation")
+      .bases("Type")
+      .build("types")
+      .field("types", [def("Type")]);
+
+    def("DeclareVariable")
+      .bases("Statement")
+      .build("id")
+      .field("id", def("Identifier"));
+
+    def("DeclareFunction")
+      .bases("Statement")
+      .build("id")
+      .field("id", def("Identifier"));
+
+    def("DeclareClass")
+      .bases("InterfaceDeclaration")
+      .build("id");
+
+    def("DeclareModule")
+      .bases("Statement")
+      .build("id", "body")
+      .field("id", or(def("Identifier"), def("Literal")))
+      .field("body", def("BlockStatement"));
+
+    def("DeclareExportDeclaration")
+      .bases("Declaration")
+      .build("default", "declaration", "specifiers", "source")
+      .field("default", Boolean)
+      .field("declaration", or(
         def("DeclareVariable"),
         def("DeclareFunction"),
         def("DeclareClass"),
         def("Type"), // Implies default.
         null
-    ))
-    .field("specifiers", [or(
+      ))
+      .field("specifiers", [or(
         def("ExportSpecifier"),
         def("ExportBatchSpecifier")
-    )], defaults.emptyArray)
-    .field("source", or(
+      )], defaults.emptyArray)
+      .field("source", or(
         def("Literal"),
         null
-    ), defaults["null"]);
+      ), defaults["null"]);
+};
\ No newline at end of file
diff --git a/def/jsx.js b/def/jsx.js
index 18fa4ea..52656ce 100644
--- a/def/jsx.js
+++ b/def/jsx.js
@@ -1,100 +1,103 @@
-require("./es7");
+module.exports = function (fork) {
+    fork.use(require("./es7"));
 
-var types = require("../lib/types");
-var def = types.Type.def;
-var or = types.Type.or;
-var defaults = require("../lib/shared").defaults;
+    var types = fork.use(require("../lib/types"));
+    var def = types.Type.def;
+    var or = types.Type.or;
+    var defaults = fork.use(require("../lib/shared")).defaults;
 
-def("JSXAttribute")
-    .bases("Node")
-    .build("name", "value")
-    .field("name", or(def("JSXIdentifier"), def("JSXNamespacedName")))
-    .field("value", or(
+    def("JSXAttribute")
+      .bases("Node")
+      .build("name", "value")
+      .field("name", or(def("JSXIdentifier"), def("JSXNamespacedName")))
+      .field("value", or(
         def("Literal"), // attr="value"
         def("JSXExpressionContainer"), // attr={value}
         null // attr= or just attr
-    ), defaults["null"]);
+      ), defaults["null"]);
 
-def("JSXIdentifier")
-    .bases("Identifier")
-    .build("name")
-    .field("name", String);
+    def("JSXIdentifier")
+      .bases("Identifier")
+      .build("name")
+      .field("name", String);
 
-def("JSXNamespacedName")
-    .bases("Node")
-    .build("namespace", "name")
-    .field("namespace", def("JSXIdentifier"))
-    .field("name", def("JSXIdentifier"));
+    def("JSXNamespacedName")
+      .bases("Node")
+      .build("namespace", "name")
+      .field("namespace", def("JSXIdentifier"))
+      .field("name", def("JSXIdentifier"));
 
-def("JSXMemberExpression")
-    .bases("MemberExpression")
-    .build("object", "property")
-    .field("object", or(def("JSXIdentifier"), def("JSXMemberExpression")))
-    .field("property", def("JSXIdentifier"))
-    .field("computed", Boolean, defaults.false);
-
-var JSXElementName = or(
-    def("JSXIdentifier"),
-    def("JSXNamespacedName"),
     def("JSXMemberExpression")
-);
+      .bases("MemberExpression")
+      .build("object", "property")
+      .field("object", or(def("JSXIdentifier"), def("JSXMemberExpression")))
+      .field("property", def("JSXIdentifier"))
+      .field("computed", Boolean, defaults.false);
 
-def("JSXSpreadAttribute")
-    .bases("Node")
-    .build("argument")
-    .field("argument", def("Expression"));
+    var JSXElementName = or(
+      def("JSXIdentifier"),
+      def("JSXNamespacedName"),
+      def("JSXMemberExpression")
+    );
 
-var JSXAttributes = [or(
-    def("JSXAttribute"),
     def("JSXSpreadAttribute")
-)];
+      .bases("Node")
+      .build("argument")
+      .field("argument", def("Expression"));
+
+    var JSXAttributes = [or(
+      def("JSXAttribute"),
+      def("JSXSpreadAttribute")
+    )];
 
-def("JSXExpressionContainer")
-    .bases("Expression")
-    .build("expression")
-    .field("expression", def("Expression"));
+    def("JSXExpressionContainer")
+      .bases("Expression")
+      .build("expression")
+      .field("expression", def("Expression"));
 
-def("JSXElement")
-    .bases("Expression")
-    .build("openingElement", "closingElement", "children")
-    .field("openingElement", def("JSXOpeningElement"))
-    .field("closingElement", or(def("JSXClosingElement"), null), defaults["null"])
-    .field("children", [or(
+    def("JSXElement")
+      .bases("Expression")
+      .build("openingElement", "closingElement", "children")
+      .field("openingElement", def("JSXOpeningElement"))
+      .field("closingElement", or(def("JSXClosingElement"), null), defaults["null"])
+      .field("children", [or(
         def("JSXElement"),
         def("JSXExpressionContainer"),
         def("JSXText"),
         def("Literal") // TODO Esprima should return JSXText instead.
-    )], defaults.emptyArray)
-    .field("name", JSXElementName, function() {
-        // Little-known fact: the `this` object inside a default function
-        // is none other than the partially-built object itself, and any
-        // fields initialized directly from builder function arguments
-        // (like openingElement, closingElement, and children) are
-        // guaranteed to be available.
-        return this.openingElement.name;
-    }, true) // hidden from traversal
-    .field("selfClosing", Boolean, function() {
-        return this.openingElement.selfClosing;
-    }, true) // hidden from traversal
-    .field("attributes", JSXAttributes, function() {
-        return this.openingElement.attributes;
-    }, true); // hidden from traversal
+      )], defaults.emptyArray)
+      .field("name", JSXElementName, function () {
+          // Little-known fact: the `this` object inside a default function
+          // is none other than the partially-built object itself, and any
+          // fields initialized directly from builder function arguments
+          // (like openingElement, closingElement, and children) are
+          // guaranteed to be available.
+          return this.openingElement.name;
+      }, true) // hidden from traversal
+      .field("selfClosing", Boolean, function () {
+          return this.openingElement.selfClosing;
+      }, true) // hidden from traversal
+      .field("attributes", JSXAttributes, function () {
+          return this.openingElement.attributes;
+      }, true); // hidden from traversal
+
+    def("JSXOpeningElement")
+      .bases("Node") // TODO Does this make sense? Can't really be an JSXElement.
+      .build("name", "attributes", "selfClosing")
+      .field("name", JSXElementName)
+      .field("attributes", JSXAttributes, defaults.emptyArray)
+      .field("selfClosing", Boolean, defaults["false"]);
 
-def("JSXOpeningElement")
-    .bases("Node") // TODO Does this make sense? Can't really be an JSXElement.
-    .build("name", "attributes", "selfClosing")
-    .field("name", JSXElementName)
-    .field("attributes", JSXAttributes, defaults.emptyArray)
-    .field("selfClosing", Boolean, defaults["false"]);
+    def("JSXClosingElement")
+      .bases("Node") // TODO Same concern.
+      .build("name")
+      .field("name", JSXElementName);
 
-def("JSXClosingElement")
-    .bases("Node") // TODO Same concern.
-    .build("name")
-    .field("name", JSXElementName);
+    def("JSXText")
+      .bases("Literal")
+      .build("value")
+      .field("value", String);
 
-def("JSXText")
-    .bases("Literal")
-    .build("value")
-    .field("value", String);
+    def("JSXEmptyExpression").bases("Expression").build();
 
-def("JSXEmptyExpression").bases("Expression").build();
+};
\ No newline at end of file
diff --git a/def/mozilla.js b/def/mozilla.js
index 2861ae9..7636152 100644
--- a/def/mozilla.js
+++ b/def/mozilla.js
@@ -1,49 +1,51 @@
-require("./core");
-var types = require("../lib/types");
-var def = types.Type.def;
-var or = types.Type.or;
-var shared = require("../lib/shared");
-var geq = shared.geq;
-var defaults = shared.defaults;
+module.exports = function (fork) {
+    fork.use(require("./core"));
+    var types = fork.use(require("../lib/types"));
+    var def = types.Type.def;
+    var or = types.Type.or;
+    var shared = fork.use(require("../lib/shared"));
+    var geq = shared.geq;
+    var defaults = shared.defaults;
 
-def("Function")
-    // SpiderMonkey allows expression closures: function(x) x+1
-    .field("body", or(def("BlockStatement"), def("Expression")));
+    def("Function")
+        // SpiderMonkey allows expression closures: function(x) x+1
+        .field("body", or(def("BlockStatement"), def("Expression")));
 
-def("ForInStatement")
-    .build("left", "right", "body", "each")
-    .field("each", Boolean, defaults["false"]);
+    def("ForInStatement")
+        .build("left", "right", "body", "each")
+        .field("each", Boolean, defaults["false"]);
 
-def("ForOfStatement")
-    .bases("Statement")
-    .build("left", "right", "body")
-    .field("left", or(
-        def("VariableDeclaration"),
-        def("Expression")))
-    .field("right", def("Expression"))
-    .field("body", def("Statement"));
+    def("ForOfStatement")
+        .bases("Statement")
+        .build("left", "right", "body")
+        .field("left", or(
+            def("VariableDeclaration"),
+            def("Expression")))
+        .field("right", def("Expression"))
+        .field("body", def("Statement"));
 
-def("LetStatement")
-    .bases("Statement")
-    .build("head", "body")
-    // TODO Deviating from the spec by reusing VariableDeclarator here.
-    .field("head", [def("VariableDeclarator")])
-    .field("body", def("Statement"));
+    def("LetStatement")
+        .bases("Statement")
+        .build("head", "body")
+        // TODO Deviating from the spec by reusing VariableDeclarator here.
+        .field("head", [def("VariableDeclarator")])
+        .field("body", def("Statement"));
 
-def("LetExpression")
-    .bases("Expression")
-    .build("head", "body")
-    // TODO Deviating from the spec by reusing VariableDeclarator here.
-    .field("head", [def("VariableDeclarator")])
-    .field("body", def("Expression"));
+    def("LetExpression")
+        .bases("Expression")
+        .build("head", "body")
+        // TODO Deviating from the spec by reusing VariableDeclarator here.
+        .field("head", [def("VariableDeclarator")])
+        .field("body", def("Expression"));
 
-def("GraphExpression")
-    .bases("Expression")
-    .build("index", "expression")
-    .field("index", geq(0))
-    .field("expression", def("Literal"));
+    def("GraphExpression")
+        .bases("Expression")
+        .build("index", "expression")
+        .field("index", geq(0))
+        .field("expression", def("Literal"));
 
-def("GraphIndexExpression")
-    .bases("Expression")
-    .build("index")
-    .field("index", geq(0));
+    def("GraphIndexExpression")
+        .bases("Expression")
+        .build("index")
+        .field("index", geq(0));
+};
\ No newline at end of file
diff --git a/fork.js b/fork.js
new file mode 100644
index 0000000..e94f5cc
--- /dev/null
+++ b/fork.js
@@ -0,0 +1,46 @@
+module.exports = function (defs) {
+    var used = [];
+    var usedResult = [];
+    var fork = {};
+
+    function use(plugin) {
+        var idx = used.indexOf(plugin);
+        if (idx === -1) {
+            idx = used.length;
+            used.push(plugin);
+            usedResult[idx] = plugin(fork);
+        }
+        return usedResult[idx];
+    }
+
+    fork.use = use;
+
+    var types = use(require('./lib/types'));
+
+    defs.forEach(use);
+
+    types.finalize();
+
+    var exports = {
+        Type: types.Type,
+        builtInTypes: types.builtInTypes,
+        namedTypes: types.namedTypes,
+        builders: types.builders,
+        defineMethod: types.defineMethod,
+        getFieldNames: types.getFieldNames,
+        getFieldValue: types.getFieldValue,
+        eachField: types.eachField,
+        someField: types.someField,
+        getSupertypeNames: types.getSupertypeNames,
+        astNodesAreEquivalent: use(require("./lib/equiv")),
+        finalize: types.finalize,
+        Path: use(require('./lib/path')),
+        NodePath: use(require("./lib/node-path")),
+        PathVisitor: use(require("./lib/path-visitor")),
+        use: use
+    };
+
+    exports.visit = exports.PathVisitor.visit;
+
+    return exports;
+};
\ No newline at end of file
diff --git a/lib/equiv.js b/lib/equiv.js
index 6167577..230ac1f 100644
--- a/lib/equiv.js
+++ b/lib/equiv.js
@@ -1,146 +1,102 @@
-var types = require("../main");
-var getFieldNames = types.getFieldNames;
-var getFieldValue = types.getFieldValue;
-var isArray = types.builtInTypes.array;
-var isObject = types.builtInTypes.object;
-var isDate = types.builtInTypes.Date;
-var isRegExp = types.builtInTypes.RegExp;
-var hasOwn = Object.prototype.hasOwnProperty;
-
-function astNodesAreEquivalent(a, b, problemPath) {
-    if (isArray.check(problemPath)) {
-        problemPath.length = 0;
-    } else {
-        problemPath = null;
-    }
-
-    return areEquivalent(a, b, problemPath);
-}
-
-astNodesAreEquivalent.assert = function(a, b) {
-    var problemPath = [];
-    if (!astNodesAreEquivalent(a, b, problemPath)) {
-        if (problemPath.length === 0) {
-            if (a !== b) {
-                throw new Error("Nodes must be equal");
-            }
+module.exports = function (fork) {
+    var types = fork.use(require('../lib/types'));
+    var getFieldNames = types.getFieldNames;
+    var getFieldValue = types.getFieldValue;
+    var isArray = types.builtInTypes.array;
+    var isObject = types.builtInTypes.object;
+    var isDate = types.builtInTypes.Date;
+    var isRegExp = types.builtInTypes.RegExp;
+    var hasOwn = Object.prototype.hasOwnProperty;
+
+    function astNodesAreEquivalent(a, b, problemPath) {
+        if (isArray.check(problemPath)) {
+            problemPath.length = 0;
         } else {
-            throw new Error(
-                "Nodes differ in the following path: " +
-                    problemPath.map(subscriptForProperty).join("")
-            );
+            problemPath = null;
         }
-    }
-};
 
-function subscriptForProperty(property) {
-    if (/[_$a-z][_$a-z0-9]*/i.test(property)) {
-        return "." + property;
+        return areEquivalent(a, b, problemPath);
     }
-    return "[" + JSON.stringify(property) + "]";
-}
 
-function areEquivalent(a, b, problemPath) {
-    if (a === b) {
-        return true;
-    }
-
-    if (isArray.check(a)) {
-        return arraysAreEquivalent(a, b, problemPath);
-    }
-
-    if (isObject.check(a)) {
-        return objectsAreEquivalent(a, b, problemPath);
-    }
-
-    if (isDate.check(a)) {
-        return isDate.check(b) && (+a === +b);
-    }
-
-    if (isRegExp.check(a)) {
-        return isRegExp.check(b) && (
-            a.source === b.source &&
-            a.global === b.global &&
-            a.multiline === b.multiline &&
-            a.ignoreCase === b.ignoreCase
-        );
-    }
-
-    return a == b;
-}
-
-function arraysAreEquivalent(a, b, problemPath) {
-    isArray.assert(a);
-    var aLength = a.length;
+    astNodesAreEquivalent.assert = function (a, b) {
+        var problemPath = [];
+        if (!astNodesAreEquivalent(a, b, problemPath)) {
+            if (problemPath.length === 0) {
+                if (a !== b) {
+                    throw new Error("Nodes must be equal");
+                }
+            } else {
+                throw new Error(
+                  "Nodes differ in the following path: " +
+                  problemPath.map(subscriptForProperty).join("")
+                );
+            }
+        }
+    };
 
-    if (!isArray.check(b) || b.length !== aLength) {
-        if (problemPath) {
-            problemPath.push("length");
+    function subscriptForProperty(property) {
+        if (/[_$a-z][_$a-z0-9]*/i.test(property)) {
+            return "." + property;
         }
-        return false;
+        return "[" + JSON.stringify(property) + "]";
     }
 
-    for (var i = 0; i < aLength; ++i) {
-        if (problemPath) {
-            problemPath.push(i);
+    function areEquivalent(a, b, problemPath) {
+        if (a === b) {
+            return true;
         }
 
-        if (i in a !== i in b) {
-            return false;
+        if (isArray.check(a)) {
+            return arraysAreEquivalent(a, b, problemPath);
         }
 
-        if (!areEquivalent(a[i], b[i], problemPath)) {
-            return false;
+        if (isObject.check(a)) {
+            return objectsAreEquivalent(a, b, problemPath);
         }
 
-        if (problemPath) {
-            var problemPathTail = problemPath.pop();
-            if (problemPathTail !== i) {
-                throw new Error("" + problemPathTail);
-            }
+        if (isDate.check(a)) {
+            return isDate.check(b) && (+a === +b);
         }
-    }
-
-    return true;
-}
 
-function objectsAreEquivalent(a, b, problemPath) {
-    isObject.assert(a);
-    if (!isObject.check(b)) {
-        return false;
-    }
-
-    // Fast path for a common property of AST nodes.
-    if (a.type !== b.type) {
-        if (problemPath) {
-            problemPath.push("type");
+        if (isRegExp.check(a)) {
+            return isRegExp.check(b) && (
+                a.source === b.source &&
+                a.global === b.global &&
+                a.multiline === b.multiline &&
+                a.ignoreCase === b.ignoreCase
+              );
         }
-        return false;
-    }
 
-    var aNames = getFieldNames(a);
-    var aNameCount = aNames.length;
+        return a == b;
+    }
 
-    var bNames = getFieldNames(b);
-    var bNameCount = bNames.length;
+    function arraysAreEquivalent(a, b, problemPath) {
+        isArray.assert(a);
+        var aLength = a.length;
 
-    if (aNameCount === bNameCount) {
-        for (var i = 0; i < aNameCount; ++i) {
-            var name = aNames[i];
-            var aChild = getFieldValue(a, name);
-            var bChild = getFieldValue(b, name);
+        if (!isArray.check(b) || b.length !== aLength) {
+            if (problemPath) {
+                problemPath.push("length");
+            }
+            return false;
+        }
 
+        for (var i = 0; i < aLength; ++i) {
             if (problemPath) {
-                problemPath.push(name);
+                problemPath.push(i);
             }
 
-            if (!areEquivalent(aChild, bChild, problemPath)) {
+            if (i in a !== i in b) {
+                return false;
+            }
+
+            if (!areEquivalent(a[i], b[i], problemPath)) {
                 return false;
             }
 
             if (problemPath) {
                 var problemPathTail = problemPath.pop();
-                if (problemPathTail !== name) {
+                if (problemPathTail !== i) {
                     throw new Error("" + problemPathTail);
                 }
             }
@@ -149,36 +105,82 @@ function objectsAreEquivalent(a, b, problemPath) {
         return true;
     }
 
-    if (!problemPath) {
-        return false;
-    }
+    function objectsAreEquivalent(a, b, problemPath) {
+        isObject.assert(a);
+        if (!isObject.check(b)) {
+            return false;
+        }
 
-    // Since aNameCount !== bNameCount, we need to find some name that's
-    // missing in aNames but present in bNames, or vice-versa.
+        // Fast path for a common property of AST nodes.
+        if (a.type !== b.type) {
+            if (problemPath) {
+                problemPath.push("type");
+            }
+            return false;
+        }
 
-    var seenNames = Object.create(null);
+        var aNames = getFieldNames(a);
+        var aNameCount = aNames.length;
 
-    for (i = 0; i < aNameCount; ++i) {
-        seenNames[aNames[i]] = true;
-    }
+        var bNames = getFieldNames(b);
+        var bNameCount = bNames.length;
 
-    for (i = 0; i < bNameCount; ++i) {
-        name = bNames[i];
+        if (aNameCount === bNameCount) {
+            for (var i = 0; i < aNameCount; ++i) {
+                var name = aNames[i];
+                var aChild = getFieldValue(a, name);
+                var bChild = getFieldValue(b, name);
 
-        if (!hasOwn.call(seenNames, name)) {
-            problemPath.push(name);
+                if (problemPath) {
+                    problemPath.push(name);
+                }
+
+                if (!areEquivalent(aChild, bChild, problemPath)) {
+                    return false;
+                }
+
+                if (problemPath) {
+                    var problemPathTail = problemPath.pop();
+                    if (problemPathTail !== name) {
+                        throw new Error("" + problemPathTail);
+                    }
+                }
+            }
+
+            return true;
+        }
+
+        if (!problemPath) {
             return false;
         }
 
-        delete seenNames[name];
-    }
+        // Since aNameCount !== bNameCount, we need to find some name that's
+        // missing in aNames but present in bNames, or vice-versa.
 
-    for (name in seenNames) {
-        problemPath.push(name);
-        break;
-    }
+        var seenNames = Object.create(null);
 
-    return false;
-}
+        for (i = 0; i < aNameCount; ++i) {
+            seenNames[aNames[i]] = true;
+        }
+
+        for (i = 0; i < bNameCount; ++i) {
+            name = bNames[i];
 
-module.exports = astNodesAreEquivalent;
+            if (!hasOwn.call(seenNames, name)) {
+                problemPath.push(name);
+                return false;
+            }
+
+            delete seenNames[name];
+        }
+
+        for (name in seenNames) {
+            problemPath.push(name);
+            break;
+        }
+
+        return false;
+    }
+    
+    return astNodesAreEquivalent;
+};
diff --git a/lib/node-path.js b/lib/node-path.js
index fbb79ea..3270734 100644
--- a/lib/node-path.js
+++ b/lib/node-path.js
@@ -1,474 +1,476 @@
-var types = require("./types");
-var n = types.namedTypes;
-var b = types.builders;
-var isNumber = types.builtInTypes.number;
-var isArray = types.builtInTypes.array;
-var Path = require("./path");
-var Scope = require("./scope");
-
-function NodePath(value, parentPath, name) {
-    if (!(this instanceof NodePath)) {
-        throw new Error("NodePath constructor cannot be invoked without 'new'");
-    }
-    Path.call(this, value, parentPath, name);
-}
-
-var NPp = NodePath.prototype = Object.create(Path.prototype, {
-    constructor: {
-        value: NodePath,
-        enumerable: false,
-        writable: true,
-        configurable: true
-    }
-});
-
-Object.defineProperties(NPp, {
-    node: {
-        get: function() {
-            Object.defineProperty(this, "node", {
-                configurable: true, // Enable deletion.
-                value: this._computeNode()
-            });
-
-            return this.node;
+module.exports = function (fork) {
+    var types = fork.use(require("./types"));
+    var n = types.namedTypes;
+    var b = types.builders;
+    var isNumber = types.builtInTypes.number;
+    var isArray = types.builtInTypes.array;
+    var Path = fork.use(require("./path"));
+    var Scope = fork.use(require("./scope"));
+
+    function NodePath(value, parentPath, name) {
+        if (!(this instanceof NodePath)) {
+            throw new Error("NodePath constructor cannot be invoked without 'new'");
         }
-    },
-
-    parent: {
-        get: function() {
-            Object.defineProperty(this, "parent", {
-                configurable: true, // Enable deletion.
-                value: this._computeParent()
-            });
+        Path.call(this, value, parentPath, name);
+    }
 
-            return this.parent;
+    var NPp = NodePath.prototype = Object.create(Path.prototype, {
+        constructor: {
+            value: NodePath,
+            enumerable: false,
+            writable: true,
+            configurable: true
         }
-    },
+    });
 
-    scope: {
-        get: function() {
-            Object.defineProperty(this, "scope", {
-                configurable: true, // Enable deletion.
-                value: this._computeScope()
-            });
+    Object.defineProperties(NPp, {
+        node: {
+            get: function () {
+                Object.defineProperty(this, "node", {
+                    configurable: true, // Enable deletion.
+                    value: this._computeNode()
+                });
 
-            return this.scope;
-        }
-    }
-});
+                return this.node;
+            }
+        },
 
-NPp.replace = function() {
-    delete this.node;
-    delete this.parent;
-    delete this.scope;
-    return Path.prototype.replace.apply(this, arguments);
-};
+        parent: {
+            get: function () {
+                Object.defineProperty(this, "parent", {
+                    configurable: true, // Enable deletion.
+                    value: this._computeParent()
+                });
 
-NPp.prune = function() {
-    var remainingNodePath = this.parent;
+                return this.parent;
+            }
+        },
 
-    this.replace();
+        scope: {
+            get: function () {
+                Object.defineProperty(this, "scope", {
+                    configurable: true, // Enable deletion.
+                    value: this._computeScope()
+                });
 
-    return cleanUpNodesAfterPrune(remainingNodePath);
-};
+                return this.scope;
+            }
+        }
+    });
 
-// The value of the first ancestor Path whose value is a Node.
-NPp._computeNode = function() {
-    var value = this.value;
-    if (n.Node.check(value)) {
-        return value;
-    }
+    NPp.replace = function () {
+        delete this.node;
+        delete this.parent;
+        delete this.scope;
+        return Path.prototype.replace.apply(this, arguments);
+    };
 
-    var pp = this.parentPath;
-    return pp && pp.node || null;
-};
+    NPp.prune = function () {
+        var remainingNodePath = this.parent;
 
-// The first ancestor Path whose value is a Node distinct from this.node.
-NPp._computeParent = function() {
-    var value = this.value;
-    var pp = this.parentPath;
+        this.replace();
 
-    if (!n.Node.check(value)) {
-        while (pp && !n.Node.check(pp.value)) {
-            pp = pp.parentPath;
-        }
+        return cleanUpNodesAfterPrune(remainingNodePath);
+    };
 
-        if (pp) {
-            pp = pp.parentPath;
+    // The value of the first ancestor Path whose value is a Node.
+    NPp._computeNode = function () {
+        var value = this.value;
+        if (n.Node.check(value)) {
+            return value;
         }
-    }
 
-    while (pp && !n.Node.check(pp.value)) {
-        pp = pp.parentPath;
-    }
-
-    return pp || null;
-};
-
-// The closest enclosing scope that governs this node.
-NPp._computeScope = function() {
-    var value = this.value;
-    var pp = this.parentPath;
-    var scope = pp && pp.scope;
+        var pp = this.parentPath;
+        return pp && pp.node || null;
+    };
 
-    if (n.Node.check(value) &&
-        Scope.isEstablishedBy(value)) {
-        scope = new Scope(this, scope);
-    }
+    // The first ancestor Path whose value is a Node distinct from this.node.
+    NPp._computeParent = function () {
+        var value = this.value;
+        var pp = this.parentPath;
 
-    return scope || null;
-};
+        if (!n.Node.check(value)) {
+            while (pp && !n.Node.check(pp.value)) {
+                pp = pp.parentPath;
+            }
 
-NPp.getValueProperty = function(name) {
-    return types.getFieldValue(this.value, name);
-};
+            if (pp) {
+                pp = pp.parentPath;
+            }
+        }
 
-/**
- * Determine whether this.node needs to be wrapped in parentheses in order
- * for a parser to reproduce the same local AST structure.
- *
- * For instance, in the expression `(1 + 2) * 3`, the BinaryExpression
- * whose operator is "+" needs parentheses, because `1 + 2 * 3` would
- * parse differently.
- *
- * If assumeExpressionContext === true, we don't worry about edge cases
- * like an anonymous FunctionExpression appearing lexically first in its
- * enclosing statement and thus needing parentheses to avoid being parsed
- * as a FunctionDeclaration with a missing name.
- */
-NPp.needsParens = function(assumeExpressionContext) {
-    var pp = this.parentPath;
-    if (!pp) {
-        return false;
-    }
+        while (pp && !n.Node.check(pp.value)) {
+            pp = pp.parentPath;
+        }
 
-    var node = this.value;
+        return pp || null;
+    };
 
-    // Only expressions need parentheses.
-    if (!n.Expression.check(node)) {
-        return false;
-    }
+    // The closest enclosing scope that governs this node.
+    NPp._computeScope = function () {
+        var value = this.value;
+        var pp = this.parentPath;
+        var scope = pp && pp.scope;
 
-    // Identifiers never need parentheses.
-    if (node.type === "Identifier") {
-        return false;
-    }
+        if (n.Node.check(value) &&
+          Scope.isEstablishedBy(value)) {
+            scope = new Scope(this, scope);
+        }
 
-    while (!n.Node.check(pp.value)) {
-        pp = pp.parentPath;
+        return scope || null;
+    };
+
+    NPp.getValueProperty = function (name) {
+        return types.getFieldValue(this.value, name);
+    };
+
+    /**
+     * Determine whether this.node needs to be wrapped in parentheses in order
+     * for a parser to reproduce the same local AST structure.
+     *
+     * For instance, in the expression `(1 + 2) * 3`, the BinaryExpression
+     * whose operator is "+" needs parentheses, because `1 + 2 * 3` would
+     * parse differently.
+     *
+     * If assumeExpressionContext === true, we don't worry about edge cases
+     * like an anonymous FunctionExpression appearing lexically first in its
+     * enclosing statement and thus needing parentheses to avoid being parsed
+     * as a FunctionDeclaration with a missing name.
+     */
+    NPp.needsParens = function (assumeExpressionContext) {
+        var pp = this.parentPath;
         if (!pp) {
             return false;
         }
-    }
 
-    var parent = pp.value;
-
-    switch (node.type) {
-    case "UnaryExpression":
-    case "SpreadElement":
-    case "SpreadProperty":
-        return parent.type === "MemberExpression"
-            && this.name === "object"
-            && parent.object === node;
-
-    case "BinaryExpression":
-    case "LogicalExpression":
-        switch (parent.type) {
-        case "CallExpression":
-            return this.name === "callee"
-                && parent.callee === node;
-
-        case "UnaryExpression":
-        case "SpreadElement":
-        case "SpreadProperty":
-            return true;
-
-        case "MemberExpression":
-            return this.name === "object"
-                && parent.object === node;
+        var node = this.value;
 
-        case "BinaryExpression":
-        case "LogicalExpression":
-            var po = parent.operator;
-            var pp = PRECEDENCE[po];
-            var no = node.operator;
-            var np = PRECEDENCE[no];
-
-            if (pp > np) {
-                return true;
-            }
-
-            if (pp === np && this.name === "right") {
-                if (parent.right !== node) {
-                    throw new Error("Nodes must be equal");
-                }
-                return true;
-            }
-
-        default:
+        // Only expressions need parentheses.
+        if (!n.Expression.check(node)) {
             return false;
         }
 
-    case "SequenceExpression":
-        switch (parent.type) {
-        case "ForStatement":
-            // Although parentheses wouldn't hurt around sequence
-            // expressions in the head of for loops, traditional style
-            // dictates that e.g. i++, j++ should not be wrapped with
-            // parentheses.
+        // Identifiers never need parentheses.
+        if (node.type === "Identifier") {
             return false;
-
-        case "ExpressionStatement":
-            return this.name !== "expression";
-
-        default:
-            // Otherwise err on the side of overparenthesization, adding
-            // explicit exceptions above if this proves overzealous.
-            return true;
         }
 
-    case "YieldExpression":
-        switch (parent.type) {
-        case "BinaryExpression":
-        case "LogicalExpression":
-        case "UnaryExpression":
-        case "SpreadElement":
-        case "SpreadProperty":
-        case "CallExpression":
-        case "MemberExpression":
-        case "NewExpression":
-        case "ConditionalExpression":
-        case "YieldExpression":
-            return true;
-
-        default:
-            return false;
+        while (!n.Node.check(pp.value)) {
+            pp = pp.parentPath;
+            if (!pp) {
+                return false;
+            }
         }
 
-    case "Literal":
-        return parent.type === "MemberExpression"
-            && isNumber.check(node.value)
-            && this.name === "object"
-            && parent.object === node;
-
-    case "AssignmentExpression":
-    case "ConditionalExpression":
-        switch (parent.type) {
-        case "UnaryExpression":
-        case "SpreadElement":
-        case "SpreadProperty":
-        case "BinaryExpression":
-        case "LogicalExpression":
-            return true;
-
-        case "CallExpression":
-            return this.name === "callee"
-                && parent.callee === node;
+        var parent = pp.value;
+
+        switch (node.type) {
+            case "UnaryExpression":
+            case "SpreadElement":
+            case "SpreadProperty":
+                return parent.type === "MemberExpression"
+                  && this.name === "object"
+                  && parent.object === node;
+
+            case "BinaryExpression":
+            case "LogicalExpression":
+                switch (parent.type) {
+                    case "CallExpression":
+                        return this.name === "callee"
+                          && parent.callee === node;
+
+                    case "UnaryExpression":
+                    case "SpreadElement":
+                    case "SpreadProperty":
+                        return true;
+
+                    case "MemberExpression":
+                        return this.name === "object"
+                          && parent.object === node;
+
+                    case "BinaryExpression":
+                    case "LogicalExpression":
+                        var po = parent.operator;
+                        var pp = PRECEDENCE[po];
+                        var no = node.operator;
+                        var np = PRECEDENCE[no];
+
+                        if (pp > np) {
+                            return true;
+                        }
+
+                        if (pp === np && this.name === "right") {
+                            if (parent.right !== node) {
+                                throw new Error("Nodes must be equal");
+                            }
+                            return true;
+                        }
+
+                    default:
+                        return false;
+                }
 
-        case "ConditionalExpression":
-            return this.name === "test"
-                && parent.test === node;
+            case "SequenceExpression":
+                switch (parent.type) {
+                    case "ForStatement":
+                        // Although parentheses wouldn't hurt around sequence
+                        // expressions in the head of for loops, traditional style
+                        // dictates that e.g. i++, j++ should not be wrapped with
+                        // parentheses.
+                        return false;
+
+                    case "ExpressionStatement":
+                        return this.name !== "expression";
+
+                    default:
+                        // Otherwise err on the side of overparenthesization, adding
+                        // explicit exceptions above if this proves overzealous.
+                        return true;
+                }
 
-        case "MemberExpression":
-            return this.name === "object"
-                && parent.object === node;
+            case "YieldExpression":
+                switch (parent.type) {
+                    case "BinaryExpression":
+                    case "LogicalExpression":
+                    case "UnaryExpression":
+                    case "SpreadElement":
+                    case "SpreadProperty":
+                    case "CallExpression":
+                    case "MemberExpression":
+                    case "NewExpression":
+                    case "ConditionalExpression":
+                    case "YieldExpression":
+                        return true;
+
+                    default:
+                        return false;
+                }
 
-        default:
-            return false;
-        }
+            case "Literal":
+                return parent.type === "MemberExpression"
+                  && isNumber.check(node.value)
+                  && this.name === "object"
+                  && parent.object === node;
+
+            case "AssignmentExpression":
+            case "ConditionalExpression":
+                switch (parent.type) {
+                    case "UnaryExpression":
+                    case "SpreadElement":
+                    case "SpreadProperty":
+                    case "BinaryExpression":
+                    case "LogicalExpression":
+                        return true;
+
+                    case "CallExpression":
+                        return this.name === "callee"
+                          && parent.callee === node;
+
+                    case "ConditionalExpression":
+                        return this.name === "test"
+                          && parent.test === node;
+
+                    case "MemberExpression":
+                        return this.name === "object"
+                          && parent.object === node;
+
+                    default:
+                        return false;
+                }
 
-    default:
-        if (parent.type === "NewExpression" &&
-            this.name === "callee" &&
-            parent.callee === node) {
-            return containsCallExpression(node);
+            default:
+                if (parent.type === "NewExpression" &&
+                  this.name === "callee" &&
+                  parent.callee === node) {
+                    return containsCallExpression(node);
+                }
         }
-    }
 
-    if (assumeExpressionContext !== true &&
-        !this.canBeFirstInStatement() &&
-        this.firstInStatement())
-        return true;
-
-    return false;
-};
+        if (assumeExpressionContext !== true &&
+          !this.canBeFirstInStatement() &&
+          this.firstInStatement())
+            return true;
 
-function isBinary(node) {
-    return n.BinaryExpression.check(node)
-        || n.LogicalExpression.check(node);
-}
-
-function isUnaryLike(node) {
-    return n.UnaryExpression.check(node)
-        // I considered making SpreadElement and SpreadProperty subtypes
-        // of UnaryExpression, but they're not really Expression nodes.
-        || (n.SpreadElement && n.SpreadElement.check(node))
-        || (n.SpreadProperty && n.SpreadProperty.check(node));
-}
-
-var PRECEDENCE = {};
-[["||"],
- ["&&"],
- ["|"],
- ["^"],
- ["&"],
- ["==", "===", "!=", "!=="],
- ["<", ">", "<=", ">=", "in", "instanceof"],
- [">>", "<<", ">>>"],
- ["+", "-"],
- ["*", "/", "%"]
-].forEach(function(tier, i) {
-    tier.forEach(function(op) {
-        PRECEDENCE[op] = i;
-    });
-});
+        return false;
+    };
 
-function containsCallExpression(node) {
-    if (n.CallExpression.check(node)) {
-        return true;
+    function isBinary(node) {
+        return n.BinaryExpression.check(node)
+          || n.LogicalExpression.check(node);
     }
 
-    if (isArray.check(node)) {
-        return node.some(containsCallExpression);
+    function isUnaryLike(node) {
+        return n.UnaryExpression.check(node)
+          // I considered making SpreadElement and SpreadProperty subtypes
+          // of UnaryExpression, but they're not really Expression nodes.
+          || (n.SpreadElement && n.SpreadElement.check(node))
+          || (n.SpreadProperty && n.SpreadProperty.check(node));
     }
 
-    if (n.Node.check(node)) {
-        return types.someField(node, function(name, child) {
-            return containsCallExpression(child);
+    var PRECEDENCE = {};
+    [["||"],
+        ["&&"],
+        ["|"],
+        ["^"],
+        ["&"],
+        ["==", "===", "!=", "!=="],
+        ["<", ">", "<=", ">=", "in", "instanceof"],
+        [">>", "<<", ">>>"],
+        ["+", "-"],
+        ["*", "/", "%"]
+    ].forEach(function (tier, i) {
+        tier.forEach(function (op) {
+            PRECEDENCE[op] = i;
         });
-    }
+    });
 
-    return false;
-}
+    function containsCallExpression(node) {
+        if (n.CallExpression.check(node)) {
+            return true;
+        }
 
-NPp.canBeFirstInStatement = function() {
-    var node = this.node;
-    return !n.FunctionExpression.check(node)
-        && !n.ObjectExpression.check(node);
-};
+        if (isArray.check(node)) {
+            return node.some(containsCallExpression);
+        }
 
-NPp.firstInStatement = function() {
-    return firstInStatement(this);
-};
+        if (n.Node.check(node)) {
+            return types.someField(node, function (name, child) {
+                return containsCallExpression(child);
+            });
+        }
 
-function firstInStatement(path) {
-    for (var node, parent; path.parent; path = path.parent) {
-        node = path.node;
-        parent = path.parent.node;
+        return false;
+    }
 
-        if (n.BlockStatement.check(parent) &&
-            path.parent.name === "body" &&
-            path.name === 0) {
-            if (parent.body[0] !== node) {
-                throw new Error("Nodes must be equal");
+    NPp.canBeFirstInStatement = function () {
+        var node = this.node;
+        return !n.FunctionExpression.check(node)
+          && !n.ObjectExpression.check(node);
+    };
+
+    NPp.firstInStatement = function () {
+        return firstInStatement(this);
+    };
+
+    function firstInStatement(path) {
+        for (var node, parent; path.parent; path = path.parent) {
+            node = path.node;
+            parent = path.parent.node;
+
+            if (n.BlockStatement.check(parent) &&
+              path.parent.name === "body" &&
+              path.name === 0) {
+                if (parent.body[0] !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                return true;
             }
-            return true;
-        }
 
-        if (n.ExpressionStatement.check(parent) &&
-            path.name === "expression") {
-            if (parent.expression !== node) {
-                throw new Error("Nodes must be equal");
+            if (n.ExpressionStatement.check(parent) &&
+              path.name === "expression") {
+                if (parent.expression !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                return true;
             }
-            return true;
-        }
 
-        if (n.SequenceExpression.check(parent) &&
-            path.parent.name === "expressions" &&
-            path.name === 0) {
-            if (parent.expressions[0] !== node) {
-                throw new Error("Nodes must be equal");
+            if (n.SequenceExpression.check(parent) &&
+              path.parent.name === "expressions" &&
+              path.name === 0) {
+                if (parent.expressions[0] !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                continue;
             }
-            continue;
-        }
 
-        if (n.CallExpression.check(parent) &&
-            path.name === "callee") {
-            if (parent.callee !== node) {
-                throw new Error("Nodes must be equal");
+            if (n.CallExpression.check(parent) &&
+              path.name === "callee") {
+                if (parent.callee !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                continue;
             }
-            continue;
-        }
 
-        if (n.MemberExpression.check(parent) &&
-            path.name === "object") {
-            if (parent.object !== node) {
-                throw new Error("Nodes must be equal");
+            if (n.MemberExpression.check(parent) &&
+              path.name === "object") {
+                if (parent.object !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                continue;
             }
-            continue;
-        }
 
-        if (n.ConditionalExpression.check(parent) &&
-            path.name === "test") {
-            if (parent.test !== node) {
-                throw new Error("Nodes must be equal");
+            if (n.ConditionalExpression.check(parent) &&
+              path.name === "test") {
+                if (parent.test !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                continue;
             }
-            continue;
-        }
 
-        if (isBinary(parent) &&
-            path.name === "left") {
-            if (parent.left !== node) {
-                throw new Error("Nodes must be equal");
+            if (isBinary(parent) &&
+              path.name === "left") {
+                if (parent.left !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                continue;
             }
-            continue;
-        }
 
-        if (n.UnaryExpression.check(parent) &&
-            !parent.prefix &&
-            path.name === "argument") {
-            if (parent.argument !== node) {
-                throw new Error("Nodes must be equal");
+            if (n.UnaryExpression.check(parent) &&
+              !parent.prefix &&
+              path.name === "argument") {
+                if (parent.argument !== node) {
+                    throw new Error("Nodes must be equal");
+                }
+                continue;
             }
-            continue;
+
+            return false;
         }
 
-        return false;
+        return true;
     }
 
-    return true;
-}
-
-/**
- * Pruning certain nodes will result in empty or incomplete nodes, here we clean those nodes up.
- */
-function cleanUpNodesAfterPrune(remainingNodePath) {
-    if (n.VariableDeclaration.check(remainingNodePath.node)) {
-        var declarations = remainingNodePath.get('declarations').value;
-        if (!declarations || declarations.length === 0) {
-            return remainingNodePath.prune();
-        }
-    } else if (n.ExpressionStatement.check(remainingNodePath.node)) {
-        if (!remainingNodePath.get('expression').value) {
-            return remainingNodePath.prune();
+    /**
+     * Pruning certain nodes will result in empty or incomplete nodes, here we clean those nodes up.
+     */
+    function cleanUpNodesAfterPrune(remainingNodePath) {
+        if (n.VariableDeclaration.check(remainingNodePath.node)) {
+            var declarations = remainingNodePath.get('declarations').value;
+            if (!declarations || declarations.length === 0) {
+                return remainingNodePath.prune();
+            }
+        } else if (n.ExpressionStatement.check(remainingNodePath.node)) {
+            if (!remainingNodePath.get('expression').value) {
+                return remainingNodePath.prune();
+            }
+        } else if (n.IfStatement.check(remainingNodePath.node)) {
+            cleanUpIfStatementAfterPrune(remainingNodePath);
         }
-    } else if (n.IfStatement.check(remainingNodePath.node)) {
-        cleanUpIfStatementAfterPrune(remainingNodePath);
+
+        return remainingNodePath;
     }
 
-    return remainingNodePath;
-}
+    function cleanUpIfStatementAfterPrune(ifStatement) {
+        var testExpression = ifStatement.get('test').value;
+        var alternate = ifStatement.get('alternate').value;
+        var consequent = ifStatement.get('consequent').value;
 
-function cleanUpIfStatementAfterPrune(ifStatement) {
-    var testExpression = ifStatement.get('test').value;
-    var alternate = ifStatement.get('alternate').value;
-    var consequent = ifStatement.get('consequent').value;
+        if (!consequent && !alternate) {
+            var testExpressionStatement = b.expressionStatement(testExpression);
 
-    if (!consequent && !alternate) {
-        var testExpressionStatement = b.expressionStatement(testExpression);
+            ifStatement.replace(testExpressionStatement);
+        } else if (!consequent && alternate) {
+            var negatedTestExpression = b.unaryExpression('!', testExpression, true);
 
-        ifStatement.replace(testExpressionStatement);
-    } else if (!consequent && alternate) {
-        var negatedTestExpression = b.unaryExpression('!', testExpression, true);
+            if (n.UnaryExpression.check(testExpression) && testExpression.operator === '!') {
+                negatedTestExpression = testExpression.argument;
+            }
 
-        if (n.UnaryExpression.check(testExpression) && testExpression.operator === '!') {
-            negatedTestExpression = testExpression.argument;
+            ifStatement.get("test").replace(negatedTestExpression);
+            ifStatement.get("consequent").replace(alternate);
+            ifStatement.get("alternate").replace();
         }
-
-        ifStatement.get("test").replace(negatedTestExpression);
-        ifStatement.get("consequent").replace(alternate);
-        ifStatement.get("alternate").replace();
     }
-}
 
-module.exports = NodePath;
+    return NodePath;
+};
diff --git a/lib/path-visitor.js b/lib/path-visitor.js
index cfe1441..d6b50fa 100644
--- a/lib/path-visitor.js
+++ b/lib/path-visitor.js
@@ -1,419 +1,422 @@
-var types = require("./types");
-var NodePath = require("./node-path");
-var Printable = types.namedTypes.Printable;
-var isArray = types.builtInTypes.array;
-var isObject = types.builtInTypes.object;
-var isFunction = types.builtInTypes.function;
 var hasOwn = Object.prototype.hasOwnProperty;
-var undefined;
 
-function PathVisitor() {
-    if (!(this instanceof PathVisitor)) {
-        throw new Error(
-            "PathVisitor constructor cannot be invoked without 'new'"
-        );
-    }
-
-    // Permanent state.
-    this._reusableContextStack = [];
+module.exports = function (fork) {
+    var types = fork.use(require("./types"));
+    var NodePath = fork.use(require("./node-path"));
+    var Printable = types.namedTypes.Printable;
+    var isArray = types.builtInTypes.array;
+    var isObject = types.builtInTypes.object;
+    var isFunction = types.builtInTypes.function;
+    var undefined;
 
-    this._methodNameTable = computeMethodNameTable(this);
-    this._shouldVisitComments =
-        hasOwn.call(this._methodNameTable, "Block") ||
-        hasOwn.call(this._methodNameTable, "Line");
+    function PathVisitor() {
+        if (!(this instanceof PathVisitor)) {
+            throw new Error(
+              "PathVisitor constructor cannot be invoked without 'new'"
+            );
+        }
 
-    this.Context = makeContextConstructor(this);
+        // Permanent state.
+        this._reusableContextStack = [];
 
-    // State reset every time PathVisitor.prototype.visit is called.
-    this._visiting = false;
-    this._changeReported = false;
-}
+        this._methodNameTable = computeMethodNameTable(this);
+        this._shouldVisitComments =
+          hasOwn.call(this._methodNameTable, "Block") ||
+          hasOwn.call(this._methodNameTable, "Line");
 
-function computeMethodNameTable(visitor) {
-    var typeNames = Object.create(null);
+        this.Context = makeContextConstructor(this);
 
-    for (var methodName in visitor) {
-        if (/^visit[A-Z]/.test(methodName)) {
-            typeNames[methodName.slice("visit".length)] = true;
-        }
+        // State reset every time PathVisitor.prototype.visit is called.
+        this._visiting = false;
+        this._changeReported = false;
     }
 
-    var supertypeTable = types.computeSupertypeLookupTable(typeNames);
-    var methodNameTable = Object.create(null);
+    function computeMethodNameTable(visitor) {
+        var typeNames = Object.create(null);
 
-    var typeNames = Object.keys(supertypeTable);
-    var typeNameCount = typeNames.length;
-    for (var i = 0; i < typeNameCount; ++i) {
-        var typeName = typeNames[i];
-        methodName = "visit" + supertypeTable[typeName];
-        if (isFunction.check(visitor[methodName])) {
-            methodNameTable[typeName] = methodName;
+        for (var methodName in visitor) {
+            if (/^visit[A-Z]/.test(methodName)) {
+                typeNames[methodName.slice("visit".length)] = true;
+            }
         }
-    }
 
-    return methodNameTable;
-}
+        var supertypeTable = types.computeSupertypeLookupTable(typeNames);
+        var methodNameTable = Object.create(null);
 
-PathVisitor.fromMethodsObject = function fromMethodsObject(methods) {
-    if (methods instanceof PathVisitor) {
-        return methods;
-    }
+        var typeNames = Object.keys(supertypeTable);
+        var typeNameCount = typeNames.length;
+        for (var i = 0; i < typeNameCount; ++i) {
+            var typeName = typeNames[i];
+            methodName = "visit" + supertypeTable[typeName];
+            if (isFunction.check(visitor[methodName])) {
+                methodNameTable[typeName] = methodName;
+            }
+        }
 
-    if (!isObject.check(methods)) {
-        // An empty visitor?
-        return new PathVisitor;
+        return methodNameTable;
     }
 
-    function Visitor() {
-        if (!(this instanceof Visitor)) {
-            throw new Error(
-                "Visitor constructor cannot be invoked without 'new'"
-            );
+    PathVisitor.fromMethodsObject = function fromMethodsObject(methods) {
+        if (methods instanceof PathVisitor) {
+            return methods;
         }
-        PathVisitor.call(this);
-    }
 
-    var Vp = Visitor.prototype = Object.create(PVp);
-    Vp.constructor = Visitor;
-
-    extend(Vp, methods);
-    extend(Visitor, PathVisitor);
-
-    isFunction.assert(Visitor.fromMethodsObject);
-    isFunction.assert(Visitor.visit);
-
-    return new Visitor;
-};
-
-function extend(target, source) {
-    for (var property in source) {
-        if (hasOwn.call(source, property)) {
-            target[property] = source[property];
+        if (!isObject.check(methods)) {
+            // An empty visitor?
+            return new PathVisitor;
         }
-    }
-
-    return target;
-}
-
-PathVisitor.visit = function visit(node, methods) {
-    return PathVisitor.fromMethodsObject(methods).visit(node);
-};
-
-var PVp = PathVisitor.prototype;
-
-PVp.visit = function() {
-    if (this._visiting) {
-        throw new Error(
-            "Recursively calling visitor.visit(path) resets visitor state. " +
-                "Try this.visit(path) or this.traverse(path) instead."
-        );
-    }
 
-    // Private state that needs to be reset before every traversal.
-    this._visiting = true;
-    this._changeReported = false;
-    this._abortRequested = false;
+        function Visitor() {
+            if (!(this instanceof Visitor)) {
+                throw new Error(
+                  "Visitor constructor cannot be invoked without 'new'"
+                );
+            }
+            PathVisitor.call(this);
+        }
 
-    var argc = arguments.length;
-    var args = new Array(argc)
-    for (var i = 0; i < argc; ++i) {
-        args[i] = arguments[i];
-    }
+        var Vp = Visitor.prototype = Object.create(PVp);
+        Vp.constructor = Visitor;
 
-    if (!(args[0] instanceof NodePath)) {
-        args[0] = new NodePath({ root: args[0] }).get("root");
-    }
+        extend(Vp, methods);
+        extend(Visitor, PathVisitor);
 
-    // Called with the same arguments as .visit.
-    this.reset.apply(this, args);
+        isFunction.assert(Visitor.fromMethodsObject);
+        isFunction.assert(Visitor.visit);
 
-    try {
-        var root = this.visitWithoutReset(args[0]);
-        var didNotThrow = true;
-    } finally {
-        this._visiting = false;
+        return new Visitor;
+    };
 
-        if (!didNotThrow && this._abortRequested) {
-            // If this.visitWithoutReset threw an exception and
-            // this._abortRequested was set to true, return the root of
-            // the AST instead of letting the exception propagate, so that
-            // client code does not have to provide a try-catch block to
-            // intercept the AbortRequest exception.  Other kinds of
-            // exceptions will propagate without being intercepted and
-            // rethrown by a catch block, so their stacks will accurately
-            // reflect the original throwing context.
-            return args[0].value;
+    function extend(target, source) {
+        for (var property in source) {
+            if (hasOwn.call(source, property)) {
+                target[property] = source[property];
+            }
         }
-    }
 
-    return root;
-};
+        return target;
+    }
 
-PVp.AbortRequest = function AbortRequest() {};
-PVp.abort = function() {
-    var visitor = this;
-    visitor._abortRequested = true;
-    var request = new visitor.AbortRequest();
-
-    // If you decide to catch this exception and stop it from propagating,
-    // make sure to call its cancel method to avoid silencing other
-    // exceptions that might be thrown later in the traversal.
-    request.cancel = function() {
-        visitor._abortRequested = false;
+    PathVisitor.visit = function visit(node, methods) {
+        return PathVisitor.fromMethodsObject(methods).visit(node);
     };
 
-    throw request;
-};
+    var PVp = PathVisitor.prototype;
 
-PVp.reset = function(path/*, additional arguments */) {
-    // Empty stub; may be reassigned or overridden by subclasses.
-};
+    PVp.visit = function () {
+        if (this._visiting) {
+            throw new Error(
+              "Recursively calling visitor.visit(path) resets visitor state. " +
+              "Try this.visit(path) or this.traverse(path) instead."
+            );
+        }
 
-PVp.visitWithoutReset = function(path) {
-    if (this instanceof this.Context) {
-        // Since this.Context.prototype === this, there's a chance we
-        // might accidentally call context.visitWithoutReset. If that
-        // happens, re-invoke the method against context.visitor.
-        return this.visitor.visitWithoutReset(path);
-    }
+        // Private state that needs to be reset before every traversal.
+        this._visiting = true;
+        this._changeReported = false;
+        this._abortRequested = false;
 
-    if (!(path instanceof NodePath)) {
-        throw new Error("");
-    }
+        var argc = arguments.length;
+        var args = new Array(argc)
+        for (var i = 0; i < argc; ++i) {
+            args[i] = arguments[i];
+        }
 
-    var value = path.value;
+        if (!(args[0] instanceof NodePath)) {
+            args[0] = new NodePath({root: args[0]}).get("root");
+        }
 
-    var methodName = value &&
-        typeof value === "object" &&
-        typeof value.type === "string" &&
-        this._methodNameTable[value.type];
+        // Called with the same arguments as .visit.
+        this.reset.apply(this, args);
 
-    if (methodName) {
-        var context = this.acquireContext(path);
         try {
-            return context.invokeVisitorMethod(methodName);
+            var root = this.visitWithoutReset(args[0]);
+            var didNotThrow = true;
         } finally {
-            this.releaseContext(context);
+            this._visiting = false;
+
+            if (!didNotThrow && this._abortRequested) {
+                // If this.visitWithoutReset threw an exception and
+                // this._abortRequested was set to true, return the root of
+                // the AST instead of letting the exception propagate, so that
+                // client code does not have to provide a try-catch block to
+                // intercept the AbortRequest exception.  Other kinds of
+                // exceptions will propagate without being intercepted and
+                // rethrown by a catch block, so their stacks will accurately
+                // reflect the original throwing context.
+                return args[0].value;
+            }
         }
 
-    } else {
-        // If there was no visitor method to call, visit the children of
-        // this node generically.
-        return visitChildren(path, this);
-    }
-};
+        return root;
+    };
 
-function visitChildren(path, visitor) {
-    if (!(path instanceof NodePath)) {
-        throw new Error("");
-    }
-    if (!(visitor instanceof PathVisitor)) {
-        throw new Error("");
-    }
+    PVp.AbortRequest = function AbortRequest() {};
+    PVp.abort = function () {
+        var visitor = this;
+        visitor._abortRequested = true;
+        var request = new visitor.AbortRequest();
 
-    var value = path.value;
-
-    if (isArray.check(value)) {
-        path.each(visitor.visitWithoutReset, visitor);
-    } else if (!isObject.check(value)) {
-        // No children to visit.
-    } else {
-        var childNames = types.getFieldNames(value);
-
-        // The .comments field of the Node type is hidden, so we only
-        // visit it if the visitor defines visitBlock or visitLine, and
-        // value.comments is defined.
-        if (visitor._shouldVisitComments &&
-            value.comments &&
-            childNames.indexOf("comments") < 0) {
-            childNames.push("comments");
-        }
+        // If you decide to catch this exception and stop it from propagating,
+        // make sure to call its cancel method to avoid silencing other
+        // exceptions that might be thrown later in the traversal.
+        request.cancel = function () {
+            visitor._abortRequested = false;
+        };
 
-        var childCount = childNames.length;
-        var childPaths = [];
+        throw request;
+    };
 
-        for (var i = 0; i < childCount; ++i) {
-            var childName = childNames[i];
-            if (!hasOwn.call(value, childName)) {
-                value[childName] = types.getFieldValue(value, childName);
-            }
-            childPaths.push(path.get(childName));
-        }
+    PVp.reset = function (path/*, additional arguments */) {
+        // Empty stub; may be reassigned or overridden by subclasses.
+    };
 
-        for (var i = 0; i < childCount; ++i) {
-            visitor.visitWithoutReset(childPaths[i]);
+    PVp.visitWithoutReset = function (path) {
+        if (this instanceof this.Context) {
+            // Since this.Context.prototype === this, there's a chance we
+            // might accidentally call context.visitWithoutReset. If that
+            // happens, re-invoke the method against context.visitor.
+            return this.visitor.visitWithoutReset(path);
         }
-    }
-
-    return path.value;
-}
 
-PVp.acquireContext = function(path) {
-    if (this._reusableContextStack.length === 0) {
-        return new this.Context(path);
-    }
-    return this._reusableContextStack.pop().reset(path);
-};
+        if (!(path instanceof NodePath)) {
+            throw new Error("");
+        }
 
-PVp.releaseContext = function(context) {
-    if (!(context instanceof this.Context)) {
-        throw new Error("");
-    }
-    this._reusableContextStack.push(context);
-    context.currentPath = null;
-};
+        var value = path.value;
 
-PVp.reportChanged = function() {
-    this._changeReported = true;
-};
+        var methodName = value &&
+          typeof value === "object" &&
+          typeof value.type === "string" &&
+          this._methodNameTable[value.type];
 
-PVp.wasChangeReported = function() {
-    return this._changeReported;
-};
+        if (methodName) {
+            var context = this.acquireContext(path);
+            try {
+                return context.invokeVisitorMethod(methodName);
+            } finally {
+                this.releaseContext(context);
+            }
 
-function makeContextConstructor(visitor) {
-    function Context(path) {
-        if (!(this instanceof Context)) {
-            throw new Error("");
+        } else {
+            // If there was no visitor method to call, visit the children of
+            // this node generically.
+            return visitChildren(path, this);
         }
-        if (!(this instanceof PathVisitor)) {
+    };
+
+    function visitChildren(path, visitor) {
+        if (!(path instanceof NodePath)) {
             throw new Error("");
         }
-        if (!(path instanceof NodePath)) {
+        if (!(visitor instanceof PathVisitor)) {
             throw new Error("");
         }
 
-        Object.defineProperty(this, "visitor", {
-            value: visitor,
-            writable: false,
-            enumerable: true,
-            configurable: false
-        });
-
-        this.currentPath = path;
-        this.needToCallTraverse = true;
-
-        Object.seal(this);
-    }
-
-    if (!(visitor instanceof PathVisitor)) {
-        throw new Error("");
-    }
-
-    // Note that the visitor object is the prototype of Context.prototype,
-    // so all visitor methods are inherited by context objects.
-    var Cp = Context.prototype = Object.create(visitor);
+        var value = path.value;
+
+        if (isArray.check(value)) {
+            path.each(visitor.visitWithoutReset, visitor);
+        } else if (!isObject.check(value)) {
+            // No children to visit.
+        } else {
+            var childNames = types.getFieldNames(value);
+
+            // The .comments field of the Node type is hidden, so we only
+            // visit it if the visitor defines visitBlock or visitLine, and
+            // value.comments is defined.
+            if (visitor._shouldVisitComments &&
+              value.comments &&
+              childNames.indexOf("comments") < 0) {
+                childNames.push("comments");
+            }
 
-    Cp.constructor = Context;
-    extend(Cp, sharedContextProtoMethods);
+            var childCount = childNames.length;
+            var childPaths = [];
 
-    return Context;
-}
+            for (var i = 0; i < childCount; ++i) {
+                var childName = childNames[i];
+                if (!hasOwn.call(value, childName)) {
+                    value[childName] = types.getFieldValue(value, childName);
+                }
+                childPaths.push(path.get(childName));
+            }
 
-// Every PathVisitor has a different this.Context constructor and
-// this.Context.prototype object, but those prototypes can all use the
-// same reset, invokeVisitorMethod, and traverse function objects.
-var sharedContextProtoMethods = Object.create(null);
+            for (var i = 0; i < childCount; ++i) {
+                visitor.visitWithoutReset(childPaths[i]);
+            }
+        }
 
-sharedContextProtoMethods.reset =
-function reset(path) {
-    if (!(this instanceof this.Context)) {
-        throw new Error("");
-    }
-    if (!(path instanceof NodePath)) {
-        throw new Error("");
+        return path.value;
     }
 
-    this.currentPath = path;
-    this.needToCallTraverse = true;
-
-    return this;
-};
+    PVp.acquireContext = function (path) {
+        if (this._reusableContextStack.length === 0) {
+            return new this.Context(path);
+        }
+        return this._reusableContextStack.pop().reset(path);
+    };
 
-sharedContextProtoMethods.invokeVisitorMethod =
-function invokeVisitorMethod(methodName) {
-    if (!(this instanceof this.Context)) {
-        throw new Error("");
-    }
-    if (!(this.currentPath instanceof NodePath)) {
-        throw new Error("");
-    }
+    PVp.releaseContext = function (context) {
+        if (!(context instanceof this.Context)) {
+            throw new Error("");
+        }
+        this._reusableContextStack.push(context);
+        context.currentPath = null;
+    };
 
-    var result = this.visitor[methodName].call(this, this.currentPath);
+    PVp.reportChanged = function () {
+        this._changeReported = true;
+    };
 
-    if (result === false) {
-        // Visitor methods return false to indicate that they have handled
-        // their own traversal needs, and we should not complain if
-        // this.needToCallTraverse is still true.
-        this.needToCallTraverse = false;
+    PVp.wasChangeReported = function () {
+        return this._changeReported;
+    };
 
-    } else if (result !== undefined) {
-        // Any other non-undefined value returned from the visitor method
-        // is interpreted as a replacement value.
-        this.currentPath = this.currentPath.replace(result)[0];
+    function makeContextConstructor(visitor) {
+        function Context(path) {
+            if (!(this instanceof Context)) {
+                throw new Error("");
+            }
+            if (!(this instanceof PathVisitor)) {
+                throw new Error("");
+            }
+            if (!(path instanceof NodePath)) {
+                throw new Error("");
+            }
 
-        if (this.needToCallTraverse) {
-            // If this.traverse still hasn't been called, visit the
-            // children of the replacement node.
-            this.traverse(this.currentPath);
-        }
-    }
+            Object.defineProperty(this, "visitor", {
+                value: visitor,
+                writable: false,
+                enumerable: true,
+                configurable: false
+            });
 
-    if (this.needToCallTraverse !== false) {
-        throw new Error(
-            "Must either call this.traverse or return false in " + methodName
-        );
-    }
+            this.currentPath = path;
+            this.needToCallTraverse = true;
 
-    var path = this.currentPath;
-    return path && path.value;
-};
+            Object.seal(this);
+        }
 
-sharedContextProtoMethods.traverse =
-function traverse(path, newVisitor) {
-    if (!(this instanceof this.Context)) {
-        throw new Error("");
-    }
-    if (!(path instanceof NodePath)) {
-        throw new Error("");
-    }
-    if (!(this.currentPath instanceof NodePath)) {
-        throw new Error("");
-    }
+        if (!(visitor instanceof PathVisitor)) {
+            throw new Error("");
+        }
 
-    this.needToCallTraverse = false;
+        // Note that the visitor object is the prototype of Context.prototype,
+        // so all visitor methods are inherited by context objects.
+        var Cp = Context.prototype = Object.create(visitor);
 
-    return visitChildren(path, PathVisitor.fromMethodsObject(
-        newVisitor || this.visitor
-    ));
-};
+        Cp.constructor = Context;
+        extend(Cp, sharedContextProtoMethods);
 
-sharedContextProtoMethods.visit =
-function visit(path, newVisitor) {
-    if (!(this instanceof this.Context)) {
-        throw new Error("");
-    }
-    if (!(path instanceof NodePath)) {
-        throw new Error("");
-    }
-    if (!(this.currentPath instanceof NodePath)) {
-        throw new Error("");
+        return Context;
     }
 
-    this.needToCallTraverse = false;
-
-    return PathVisitor.fromMethodsObject(
-        newVisitor || this.visitor
-    ).visitWithoutReset(path);
-};
+// Every PathVisitor has a different this.Context constructor and
+// this.Context.prototype object, but those prototypes can all use the
+// same reset, invokeVisitorMethod, and traverse function objects.
+    var sharedContextProtoMethods = Object.create(null);
+
+    sharedContextProtoMethods.reset =
+      function reset(path) {
+          if (!(this instanceof this.Context)) {
+              throw new Error("");
+          }
+          if (!(path instanceof NodePath)) {
+              throw new Error("");
+          }
+
+          this.currentPath = path;
+          this.needToCallTraverse = true;
+
+          return this;
+      };
+
+    sharedContextProtoMethods.invokeVisitorMethod =
+      function invokeVisitorMethod(methodName) {
+          if (!(this instanceof this.Context)) {
+              throw new Error("");
+          }
+          if (!(this.currentPath instanceof NodePath)) {
+              throw new Error("");
+          }
+
+          var result = this.visitor[methodName].call(this, this.currentPath);
+
+          if (result === false) {
+              // Visitor methods return false to indicate that they have handled
+              // their own traversal needs, and we should not complain if
+              // this.needToCallTraverse is still true.
+              this.needToCallTraverse = false;
+
+          } else if (result !== undefined) {
+              // Any other non-undefined value returned from the visitor method
+              // is interpreted as a replacement value.
+              this.currentPath = this.currentPath.replace(result)[0];
+
+              if (this.needToCallTraverse) {
+                  // If this.traverse still hasn't been called, visit the
+                  // children of the replacement node.
+                  this.traverse(this.currentPath);
+              }
+          }
+
+          if (this.needToCallTraverse !== false) {
+              throw new Error(
+                "Must either call this.traverse or return false in " + methodName
+              );
+          }
+
+          var path = this.currentPath;
+          return path && path.value;
+      };
+
+    sharedContextProtoMethods.traverse =
+      function traverse(path, newVisitor) {
+          if (!(this instanceof this.Context)) {
+              throw new Error("");
+          }
+          if (!(path instanceof NodePath)) {
+              throw new Error("");
+          }
+          if (!(this.currentPath instanceof NodePath)) {
+              throw new Error("");
+          }
+
+          this.needToCallTraverse = false;
+
+          return visitChildren(path, PathVisitor.fromMethodsObject(
+            newVisitor || this.visitor
+          ));
+      };
+
+    sharedContextProtoMethods.visit =
+      function visit(path, newVisitor) {
+          if (!(this instanceof this.Context)) {
+              throw new Error("");
+          }
+          if (!(path instanceof NodePath)) {
+              throw new Error("");
+          }
+          if (!(this.currentPath instanceof NodePath)) {
+              throw new Error("");
+          }
+
+          this.needToCallTraverse = false;
+
+          return PathVisitor.fromMethodsObject(
+            newVisitor || this.visitor
+          ).visitWithoutReset(path);
+      };
+
+    sharedContextProtoMethods.reportChanged = function reportChanged() {
+        this.visitor.reportChanged();
+    };
 
-sharedContextProtoMethods.reportChanged = function reportChanged() {
-    this.visitor.reportChanged();
-};
+    sharedContextProtoMethods.abort = function abort() {
+        this.needToCallTraverse = false;
+        this.visitor.abort();
+    };
 
-sharedContextProtoMethods.abort = function abort() {
-    this.needToCallTraverse = false;
-    this.visitor.abort();
+    return PathVisitor;
 };
-
-module.exports = PathVisitor;
diff --git a/lib/path.js b/lib/path.js
index 9601e88..6dbb099 100644
--- a/lib/path.js
+++ b/lib/path.js
@@ -1,366 +1,369 @@
-var Op = Object.prototype;
-var hasOwn = Op.hasOwnProperty;
-var types = require("./types");
-var isArray = types.builtInTypes.array;
-var isNumber = types.builtInTypes.number;
 var Ap = Array.prototype;
 var slice = Ap.slice;
 var map = Ap.map;
+var Op = Object.prototype;
+var hasOwn = Op.hasOwnProperty;
 
-function Path(value, parentPath, name) {
-    if (!(this instanceof Path)) {
-        throw new Error("Path constructor cannot be invoked without 'new'");
-    }
+module.exports = function (fork) {
+    var types = fork.use(require("./types"));
+    var isArray = types.builtInTypes.array;
+    var isNumber = types.builtInTypes.number;
 
-    if (parentPath) {
-        if (!(parentPath instanceof Path)) {
-            throw new Error("");
+    function Path(value, parentPath, name) {
+        if (!(this instanceof Path)) {
+            throw new Error("Path constructor cannot be invoked without 'new'");
         }
-    } else {
-        parentPath = null;
-        name = null;
-    }
 
-    // The value encapsulated by this Path, generally equal to
-    // parentPath.value[name] if we have a parentPath.
-    this.value = value;
-
-    // The immediate parent Path of this Path.
-    this.parentPath = parentPath;
-
-    // The name of the property of parentPath.value through which this
-    // Path's value was reached.
-    this.name = name;
-
-    // Calling path.get("child") multiple times always returns the same
-    // child Path object, for both performance and consistency reasons.
-    this.__childCache = null;
-}
-
-var Pp = Path.prototype;
-
-function getChildCache(path) {
-    // Lazily create the child cache. This also cheapens cache
-    // invalidation, since you can just reset path.__childCache to null.
-    return path.__childCache || (path.__childCache = Object.create(null));
-}
-
-function getChildPath(path, name) {
-    var cache = getChildCache(path);
-    var actualChildValue = path.getValueProperty(name);
-    var childPath = cache[name];
-    if (!hasOwn.call(cache, name) ||
-        // Ensure consistency between cache and reality.
-        childPath.value !== actualChildValue) {
-        childPath = cache[name] = new path.constructor(
-            actualChildValue, path, name
-        );
-    }
-    return childPath;
-}
+        if (parentPath) {
+            if (!(parentPath instanceof Path)) {
+                throw new Error("");
+            }
+        } else {
+            parentPath = null;
+            name = null;
+        }
 
-// This method is designed to be overridden by subclasses that need to
-// handle missing properties, etc.
-Pp.getValueProperty = function getValueProperty(name) {
-    return this.value[name];
-};
+        // The value encapsulated by this Path, generally equal to
+        // parentPath.value[name] if we have a parentPath.
+        this.value = value;
 
-Pp.get = function get(name) {
-    var path = this;
-    var names = arguments;
-    var count = names.length;
+        // The immediate parent Path of this Path.
+        this.parentPath = parentPath;
 
-    for (var i = 0; i < count; ++i) {
-        path = getChildPath(path, names[i]);
-    }
+        // The name of the property of parentPath.value through which this
+        // Path's value was reached.
+        this.name = name;
 
-    return path;
-};
+        // Calling path.get("child") multiple times always returns the same
+        // child Path object, for both performance and consistency reasons.
+        this.__childCache = null;
+    }
 
-Pp.each = function each(callback, context) {
-    var childPaths = [];
-    var len = this.value.length;
-    var i = 0;
+    var Pp = Path.prototype;
 
-    // Collect all the original child paths before invoking the callback.
-    for (var i = 0; i < len; ++i) {
-        if (hasOwn.call(this.value, i)) {
-            childPaths[i] = this.get(i);
-        }
+    function getChildCache(path) {
+        // Lazily create the child cache. This also cheapens cache
+        // invalidation, since you can just reset path.__childCache to null.
+        return path.__childCache || (path.__childCache = Object.create(null));
     }
 
-    // Invoke the callback on just the original child paths, regardless of
-    // any modifications made to the array by the callback. I chose these
-    // semantics over cleverly invoking the callback on new elements because
-    // this way is much easier to reason about.
-    context = context || this;
-    for (i = 0; i < len; ++i) {
-        if (hasOwn.call(childPaths, i)) {
-            callback.call(context, childPaths[i]);
+    function getChildPath(path, name) {
+        var cache = getChildCache(path);
+        var actualChildValue = path.getValueProperty(name);
+        var childPath = cache[name];
+        if (!hasOwn.call(cache, name) ||
+          // Ensure consistency between cache and reality.
+          childPath.value !== actualChildValue) {
+            childPath = cache[name] = new path.constructor(
+              actualChildValue, path, name
+            );
         }
+        return childPath;
     }
-};
-
-Pp.map = function map(callback, context) {
-    var result = [];
 
-    this.each(function(childPath) {
-        result.push(callback.call(this, childPath));
-    }, context);
-
-    return result;
-};
+// This method is designed to be overridden by subclasses that need to
+// handle missing properties, etc.
+    Pp.getValueProperty = function getValueProperty(name) {
+        return this.value[name];
+    };
 
-Pp.filter = function filter(callback, context) {
-    var result = [];
+    Pp.get = function get(name) {
+        var path = this;
+        var names = arguments;
+        var count = names.length;
 
-    this.each(function(childPath) {
-        if (callback.call(this, childPath)) {
-            result.push(childPath);
+        for (var i = 0; i < count; ++i) {
+            path = getChildPath(path, names[i]);
         }
-    }, context);
 
-    return result;
-};
-
-function emptyMoves() {}
-function getMoves(path, offset, start, end) {
-    isArray.assert(path.value);
+        return path;
+    };
 
-    if (offset === 0) {
-        return emptyMoves;
-    }
+    Pp.each = function each(callback, context) {
+        var childPaths = [];
+        var len = this.value.length;
+        var i = 0;
 
-    var length = path.value.length;
-    if (length < 1) {
-        return emptyMoves;
-    }
+        // Collect all the original child paths before invoking the callback.
+        for (var i = 0; i < len; ++i) {
+            if (hasOwn.call(this.value, i)) {
+                childPaths[i] = this.get(i);
+            }
+        }
 
-    var argc = arguments.length;
-    if (argc === 2) {
-        start = 0;
-        end = length;
-    } else if (argc === 3) {
-        start = Math.max(start, 0);
-        end = length;
-    } else {
-        start = Math.max(start, 0);
-        end = Math.min(end, length);
-    }
+        // Invoke the callback on just the original child paths, regardless of
+        // any modifications made to the array by the callback. I chose these
+        // semantics over cleverly invoking the callback on new elements because
+        // this way is much easier to reason about.
+        context = context || this;
+        for (i = 0; i < len; ++i) {
+            if (hasOwn.call(childPaths, i)) {
+                callback.call(context, childPaths[i]);
+            }
+        }
+    };
 
-    isNumber.assert(start);
-    isNumber.assert(end);
+    Pp.map = function map(callback, context) {
+        var result = [];
 
-    var moves = Object.create(null);
-    var cache = getChildCache(path);
+        this.each(function (childPath) {
+            result.push(callback.call(this, childPath));
+        }, context);
 
-    for (var i = start; i < end; ++i) {
-        if (hasOwn.call(path.value, i)) {
-            var childPath = path.get(i);
-            if (childPath.name !== i) {
-                throw new Error("");
-            }
-            var newIndex = i + offset;
-            childPath.name = newIndex;
-            moves[newIndex] = childPath;
-            delete cache[i];
-        }
-    }
+        return result;
+    };
 
-    delete cache.length;
+    Pp.filter = function filter(callback, context) {
+        var result = [];
 
-    return function() {
-        for (var newIndex in moves) {
-            var childPath = moves[newIndex];
-            if (childPath.name !== +newIndex) {
-                throw new Error("");
+        this.each(function (childPath) {
+            if (callback.call(this, childPath)) {
+                result.push(childPath);
             }
-            cache[newIndex] = childPath;
-            path.value[newIndex] = childPath.value;
-        }
+        }, context);
+
+        return result;
     };
-}
 
-Pp.shift = function shift() {
-    var move = getMoves(this, -1);
-    var result = this.value.shift();
-    move();
-    return result;
-};
+    function emptyMoves() {}
+    function getMoves(path, offset, start, end) {
+        isArray.assert(path.value);
 
-Pp.unshift = function unshift(node) {
-    var move = getMoves(this, arguments.length);
-    var result = this.value.unshift.apply(this.value, arguments);
-    move();
-    return result;
-};
+        if (offset === 0) {
+            return emptyMoves;
+        }
 
-Pp.push = function push(node) {
-    isArray.assert(this.value);
-    delete getChildCache(this).length
-    return this.value.push.apply(this.value, arguments);
-};
+        var length = path.value.length;
+        if (length < 1) {
+            return emptyMoves;
+        }
 
-Pp.pop = function pop() {
-    isArray.assert(this.value);
-    var cache = getChildCache(this);
-    delete cache[this.value.length - 1];
-    delete cache.length;
-    return this.value.pop();
-};
+        var argc = arguments.length;
+        if (argc === 2) {
+            start = 0;
+            end = length;
+        } else if (argc === 3) {
+            start = Math.max(start, 0);
+            end = length;
+        } else {
+            start = Math.max(start, 0);
+            end = Math.min(end, length);
+        }
 
-Pp.insertAt = function insertAt(index, node) {
-    var argc = arguments.length;
-    var move = getMoves(this, argc - 1, index);
-    if (move === emptyMoves) {
-        return this;
-    }
+        isNumber.assert(start);
+        isNumber.assert(end);
+
+        var moves = Object.create(null);
+        var cache = getChildCache(path);
+
+        for (var i = start; i < end; ++i) {
+            if (hasOwn.call(path.value, i)) {
+                var childPath = path.get(i);
+                if (childPath.name !== i) {
+                    throw new Error("");
+                }
+                var newIndex = i + offset;
+                childPath.name = newIndex;
+                moves[newIndex] = childPath;
+                delete cache[i];
+            }
+        }
 
-    index = Math.max(index, 0);
+        delete cache.length;
 
-    for (var i = 1; i < argc; ++i) {
-        this.value[index + i - 1] = arguments[i];
+        return function () {
+            for (var newIndex in moves) {
+                var childPath = moves[newIndex];
+                if (childPath.name !== +newIndex) {
+                    throw new Error("");
+                }
+                cache[newIndex] = childPath;
+                path.value[newIndex] = childPath.value;
+            }
+        };
     }
 
-    move();
+    Pp.shift = function shift() {
+        var move = getMoves(this, -1);
+        var result = this.value.shift();
+        move();
+        return result;
+    };
 
-    return this;
-};
+    Pp.unshift = function unshift(node) {
+        var move = getMoves(this, arguments.length);
+        var result = this.value.unshift.apply(this.value, arguments);
+        move();
+        return result;
+    };
 
-Pp.insertBefore = function insertBefore(node) {
-    var pp = this.parentPath;
-    var argc = arguments.length;
-    var insertAtArgs = [this.name];
-    for (var i = 0; i < argc; ++i) {
-        insertAtArgs.push(arguments[i]);
-    }
-    return pp.insertAt.apply(pp, insertAtArgs);
-};
+    Pp.push = function push(node) {
+        isArray.assert(this.value);
+        delete getChildCache(this).length
+        return this.value.push.apply(this.value, arguments);
+    };
 
-Pp.insertAfter = function insertAfter(node) {
-    var pp = this.parentPath;
-    var argc = arguments.length;
-    var insertAtArgs = [this.name + 1];
-    for (var i = 0; i < argc; ++i) {
-        insertAtArgs.push(arguments[i]);
-    }
-    return pp.insertAt.apply(pp, insertAtArgs);
-};
+    Pp.pop = function pop() {
+        isArray.assert(this.value);
+        var cache = getChildCache(this);
+        delete cache[this.value.length - 1];
+        delete cache.length;
+        return this.value.pop();
+    };
 
-function repairRelationshipWithParent(path) {
-    if (!(path instanceof Path)) {
-        throw new Error("");
-    }
+    Pp.insertAt = function insertAt(index, node) {
+        var argc = arguments.length;
+        var move = getMoves(this, argc - 1, index);
+        if (move === emptyMoves) {
+            return this;
+        }
 
-    var pp = path.parentPath;
-    if (!pp) {
-        // Orphan paths have no relationship to repair.
-        return path;
-    }
+        index = Math.max(index, 0);
 
-    var parentValue = pp.value;
-    var parentCache = getChildCache(pp);
-
-    // Make sure parentCache[path.name] is populated.
-    if (parentValue[path.name] === path.value) {
-        parentCache[path.name] = path;
-    } else if (isArray.check(parentValue)) {
-        // Something caused path.name to become out of date, so attempt to
-        // recover by searching for path.value in parentValue.
-        var i = parentValue.indexOf(path.value);
-        if (i >= 0) {
-            parentCache[path.name = i] = path;
+        for (var i = 1; i < argc; ++i) {
+            this.value[index + i - 1] = arguments[i];
         }
-    } else {
-        // If path.value disagrees with parentValue[path.name], and
-        // path.name is not an array index, let path.value become the new
-        // parentValue[path.name] and update parentCache accordingly.
-        parentValue[path.name] = path.value;
-        parentCache[path.name] = path;
-    }
 
-    if (parentValue[path.name] !== path.value) {
-        throw new Error("");
-    }
-    if (path.parentPath.get(path.name) !== path) {
-        throw new Error("");
-    }
+        move();
 
-    return path;
-}
+        return this;
+    };
 
-Pp.replace = function replace(replacement) {
-    var results = [];
-    var parentValue = this.parentPath.value;
-    var parentCache = getChildCache(this.parentPath);
-    var count = arguments.length;
+    Pp.insertBefore = function insertBefore(node) {
+        var pp = this.parentPath;
+        var argc = arguments.length;
+        var insertAtArgs = [this.name];
+        for (var i = 0; i < argc; ++i) {
+            insertAtArgs.push(arguments[i]);
+        }
+        return pp.insertAt.apply(pp, insertAtArgs);
+    };
 
-    repairRelationshipWithParent(this);
+    Pp.insertAfter = function insertAfter(node) {
+        var pp = this.parentPath;
+        var argc = arguments.length;
+        var insertAtArgs = [this.name + 1];
+        for (var i = 0; i < argc; ++i) {
+            insertAtArgs.push(arguments[i]);
+        }
+        return pp.insertAt.apply(pp, insertAtArgs);
+    };
 
-    if (isArray.check(parentValue)) {
-        var originalLength = parentValue.length;
-        var move = getMoves(this.parentPath, count - 1, this.name + 1);
+    function repairRelationshipWithParent(path) {
+        if (!(path instanceof Path)) {
+            throw new Error("");
+        }
 
-        var spliceArgs = [this.name, 1];
-        for (var i = 0; i < count; ++i) {
-            spliceArgs.push(arguments[i]);
+        var pp = path.parentPath;
+        if (!pp) {
+            // Orphan paths have no relationship to repair.
+            return path;
         }
 
-        var splicedOut = parentValue.splice.apply(parentValue, spliceArgs);
+        var parentValue = pp.value;
+        var parentCache = getChildCache(pp);
+
+        // Make sure parentCache[path.name] is populated.
+        if (parentValue[path.name] === path.value) {
+            parentCache[path.name] = path;
+        } else if (isArray.check(parentValue)) {
+            // Something caused path.name to become out of date, so attempt to
+            // recover by searching for path.value in parentValue.
+            var i = parentValue.indexOf(path.value);
+            if (i >= 0) {
+                parentCache[path.name = i] = path;
+            }
+        } else {
+            // If path.value disagrees with parentValue[path.name], and
+            // path.name is not an array index, let path.value become the new
+            // parentValue[path.name] and update parentCache accordingly.
+            parentValue[path.name] = path.value;
+            parentCache[path.name] = path;
+        }
 
-        if (splicedOut[0] !== this.value) {
+        if (parentValue[path.name] !== path.value) {
             throw new Error("");
         }
-        if (parentValue.length !== (originalLength - 1 + count)) {
+        if (path.parentPath.get(path.name) !== path) {
             throw new Error("");
         }
 
-        move();
+        return path;
+    }
 
-        if (count === 0) {
-            delete this.value;
-            delete parentCache[this.name];
-            this.__childCache = null;
+    Pp.replace = function replace(replacement) {
+        var results = [];
+        var parentValue = this.parentPath.value;
+        var parentCache = getChildCache(this.parentPath);
+        var count = arguments.length;
 
-        } else {
-            if (parentValue[this.name] !== replacement) {
+        repairRelationshipWithParent(this);
+
+        if (isArray.check(parentValue)) {
+            var originalLength = parentValue.length;
+            var move = getMoves(this.parentPath, count - 1, this.name + 1);
+
+            var spliceArgs = [this.name, 1];
+            for (var i = 0; i < count; ++i) {
+                spliceArgs.push(arguments[i]);
+            }
+
+            var splicedOut = parentValue.splice.apply(parentValue, spliceArgs);
+
+            if (splicedOut[0] !== this.value) {
+                throw new Error("");
+            }
+            if (parentValue.length !== (originalLength - 1 + count)) {
                 throw new Error("");
             }
 
-            if (this.value !== replacement) {
-                this.value = replacement;
+            move();
+
+            if (count === 0) {
+                delete this.value;
+                delete parentCache[this.name];
                 this.__childCache = null;
-            }
 
-            for (i = 0; i < count; ++i) {
-                results.push(this.parentPath.get(this.name + i));
+            } else {
+                if (parentValue[this.name] !== replacement) {
+                    throw new Error("");
+                }
+
+                if (this.value !== replacement) {
+                    this.value = replacement;
+                    this.__childCache = null;
+                }
+
+                for (i = 0; i < count; ++i) {
+                    results.push(this.parentPath.get(this.name + i));
+                }
+
+                if (results[0] !== this) {
+                    throw new Error("");
+                }
             }
 
-            if (results[0] !== this) {
-                throw new Error("");
+        } else if (count === 1) {
+            if (this.value !== replacement) {
+                this.__childCache = null;
             }
-        }
+            this.value = parentValue[this.name] = replacement;
+            results.push(this);
 
-    } else if (count === 1) {
-        if (this.value !== replacement) {
+        } else if (count === 0) {
+            delete parentValue[this.name];
+            delete this.value;
             this.__childCache = null;
-        }
-        this.value = parentValue[this.name] = replacement;
-        results.push(this);
 
-    } else if (count === 0) {
-        delete parentValue[this.name];
-        delete this.value;
-        this.__childCache = null;
+            // Leave this path cached as parentCache[this.name], even though
+            // it no longer has a value defined.
 
-        // Leave this path cached as parentCache[this.name], even though
-        // it no longer has a value defined.
+        } else {
+            throw new Error("Could not replace path");
+        }
 
-    } else {
-        throw new Error("Could not replace path");
-    }
+        return results;
+    };
 
-    return results;
+    return Path;
 };
-
-module.exports = Path;
diff --git a/lib/scope.js b/lib/scope.js
index f4c7b9d..1f98fb4 100644
--- a/lib/scope.js
+++ b/lib/scope.js
@@ -1,330 +1,350 @@
-var types = require("./types");
-var Type = types.Type;
-var namedTypes = types.namedTypes;
-var Node = namedTypes.Node;
-var Expression = namedTypes.Expression;
-var isArray = types.builtInTypes.array;
 var hasOwn = Object.prototype.hasOwnProperty;
-var b = types.builders;
 
-function Scope(path, parentScope) {
-    if (!(this instanceof Scope)) {
-        throw new Error("Scope constructor cannot be invoked without 'new'");
-    }
-    if (!(path instanceof require("./node-path"))) {
-        throw new Error("");
-    }
-    ScopeType.assert(path.value);
+module.exports = function (fork) {
+    var types = fork.use(require("./types"));
+    var Type = types.Type;
+    var namedTypes = types.namedTypes;
+    var Node = namedTypes.Node;
+    var Expression = namedTypes.Expression;
+    var isArray = types.builtInTypes.array;
+    var b = types.builders;
+
+    function Scope(path, parentScope) {
+        if (!(this instanceof Scope)) {
+            throw new Error("Scope constructor cannot be invoked without 'new'");
+        }
+        if (!(path instanceof fork.use(require("./node-path")))) {
+            throw new Error("");
+        }
+        ScopeType.assert(path.value);
 
-    var depth;
+        var depth;
 
-    if (parentScope) {
-        if (!(parentScope instanceof Scope)) {
-            throw new Error("");
+        if (parentScope) {
+            if (!(parentScope instanceof Scope)) {
+                throw new Error("");
+            }
+            depth = parentScope.depth + 1;
+        } else {
+            parentScope = null;
+            depth = 0;
         }
-        depth = parentScope.depth + 1;
-    } else {
-        parentScope = null;
-        depth = 0;
+
+        Object.defineProperties(this, {
+            path: { value: path },
+            node: { value: path.value },
+            isGlobal: { value: !parentScope, enumerable: true },
+            depth: { value: depth },
+            parent: { value: parentScope },
+            bindings: { value: {} },
+            types: { value: {} },
+        });
     }
 
-    Object.defineProperties(this, {
-        path: { value: path },
-        node: { value: path.value },
-        isGlobal: { value: !parentScope, enumerable: true },
-        depth: { value: depth },
-        parent: { value: parentScope },
-        bindings: { value: {} },
-        types: { value: {} },
-    });
-}
-
-var scopeTypes = [
-    // Program nodes introduce global scopes.
-    namedTypes.Program,
-
-    // Function is the supertype of FunctionExpression,
-    // FunctionDeclaration, ArrowExpression, etc.
-    namedTypes.Function,
-
-    // In case you didn't know, the caught parameter shadows any variable
-    // of the same name in an outer scope.
-    namedTypes.CatchClause
-];
-
-var ScopeType = Type.or.apply(Type, scopeTypes);
-
-Scope.isEstablishedBy = function(node) {
-    return ScopeType.check(node);
-};
+    var scopeTypes = [
+        // Program nodes introduce global scopes.
+        namedTypes.Program,
+
+        // Function is the supertype of FunctionExpression,
+        // FunctionDeclaration, ArrowExpression, etc.
+        namedTypes.Function,
+
+        // In case you didn't know, the caught parameter shadows any variable
+        // of the same name in an outer scope.
+        namedTypes.CatchClause
+    ];
 
-var Sp = Scope.prototype;
+    var ScopeType = Type.or.apply(Type, scopeTypes);
+
+    Scope.isEstablishedBy = function(node) {
+        return ScopeType.check(node);
+    };
+
+    var Sp = Scope.prototype;
 
 // Will be overridden after an instance lazily calls scanScope.
-Sp.didScan = false;
+    Sp.didScan = false;
 
-Sp.declares = function(name) {
-    this.scan();
-    return hasOwn.call(this.bindings, name);
-};
+    Sp.declares = function(name) {
+        this.scan();
+        return hasOwn.call(this.bindings, name);
+    };
 
-Sp.declaresType = function(name) {
-    this.scan();
-    return hasOwn.call(this.types, name);
-};
+    Sp.declaresType = function(name) {
+        this.scan();
+        return hasOwn.call(this.types, name);
+    };
 
-Sp.declareTemporary = function(prefix) {
-    if (prefix) {
-        if (!/^[a-z$_]/i.test(prefix)) {
-            throw new Error("");
+    Sp.declareTemporary = function(prefix) {
+        if (prefix) {
+            if (!/^[a-z$_]/i.test(prefix)) {
+                throw new Error("");
+            }
+        } else {
+            prefix = "t$";
         }
-    } else {
-        prefix = "t$";
-    }
 
-    // Include this.depth in the name to make sure the name does not
-    // collide with any variables in nested/enclosing scopes.
-    prefix += this.depth.toString(36) + "$";
+        // Include this.depth in the name to make sure the name does not
+        // collide with any variables in nested/enclosing scopes.
+        prefix += this.depth.toString(36) + "$";
 
-    this.scan();
+        this.scan();
 
-    var index = 0;
-    while (this.declares(prefix + index)) {
-        ++index;
-    }
+        var index = 0;
+        while (this.declares(prefix + index)) {
+            ++index;
+        }
 
-    var name = prefix + index;
-    return this.bindings[name] = types.builders.identifier(name);
-};
+        var name = prefix + index;
+        return this.bindings[name] = types.builders.identifier(name);
+    };
 
-Sp.injectTemporary = function(identifier, init) {
-    identifier || (identifier = this.declareTemporary());
+    Sp.injectTemporary = function(identifier, init) {
+        identifier || (identifier = this.declareTemporary());
 
-    var bodyPath = this.path.get("body");
-    if (namedTypes.BlockStatement.check(bodyPath.value)) {
-        bodyPath = bodyPath.get("body");
-    }
+        var bodyPath = this.path.get("body");
+        if (namedTypes.BlockStatement.check(bodyPath.value)) {
+            bodyPath = bodyPath.get("body");
+        }
 
-    bodyPath.unshift(
-        b.variableDeclaration(
+        bodyPath.unshift(
+          b.variableDeclaration(
             "var",
             [b.variableDeclarator(identifier, init || null)]
-        )
-    );
+          )
+        );
 
-    return identifier;
-};
+        return identifier;
+    };
 
-Sp.scan = function(force) {
-    if (force || !this.didScan) {
-        for (var name in this.bindings) {
-            // Empty out this.bindings, just in cases.
-            delete this.bindings[name];
+    Sp.scan = function(force) {
+        if (force || !this.didScan) {
+            for (var name in this.bindings) {
+                // Empty out this.bindings, just in cases.
+                delete this.bindings[name];
+            }
+            scanScope(this.path, this.bindings, this.types);
+            this.didScan = true;
         }
-        scanScope(this.path, this.bindings, this.types);
-        this.didScan = true;
-    }
-};
+    };
 
-Sp.getBindings = function () {
-    this.scan();
-    return this.bindings;
-};
+    Sp.getBindings = function () {
+        this.scan();
+        return this.bindings;
+    };
 
-Sp.getTypes = function () {
-    this.scan();
-    return this.types;
-};
+    Sp.getTypes = function () {
+        this.scan();
+        return this.types;
+    };
 
-function scanScope(path, bindings, scopeTypes) {
-    var node = path.value;
-    ScopeType.assert(node);
+    function scanScope(path, bindings, scopeTypes) {
+        var node = path.value;
+        ScopeType.assert(node);
 
-    if (namedTypes.CatchClause.check(node)) {
-        // A catch clause establishes a new scope but the only variable
-        // bound in that scope is the catch parameter. Any other
-        // declarations create bindings in the outer scope.
-        addPattern(path.get("param"), bindings);
+        if (namedTypes.CatchClause.check(node)) {
+            // A catch clause establishes a new scope but the only variable
+            // bound in that scope is the catch parameter. Any other
+            // declarations create bindings in the outer scope.
+            addPattern(path.get("param"), bindings);
 
-    } else {
-        recursiveScanScope(path, bindings, scopeTypes);
+        } else {
+            recursiveScanScope(path, bindings, scopeTypes);
+        }
     }
-}
 
-function recursiveScanScope(path, bindings, scopeTypes) {
-    var node = path.value;
+    function recursiveScanScope(path, bindings, scopeTypes) {
+        var node = path.value;
 
-    if (path.parent &&
-        namedTypes.FunctionExpression.check(path.parent.node) &&
-        path.parent.node.id) {
-        addPattern(path.parent.get("id"), bindings);
-    }
-
-    if (!node) {
-        // None of the remaining cases matter if node is falsy.
+        if (path.parent &&
+          namedTypes.FunctionExpression.check(path.parent.node) &&
+          path.parent.node.id) {
+            addPattern(path.parent.get("id"), bindings);
+        }
 
-    } else if (isArray.check(node)) {
-        path.each(function(childPath) {
-            recursiveScanChild(childPath, bindings, scopeTypes);
-        });
+        if (!node) {
+            // None of the remaining cases matter if node is falsy.
+
+        } else if (isArray.check(node)) {
+            path.each(function(childPath) {
+                recursiveScanChild(childPath, bindings, scopeTypes);
+            });
+
+        } else if (namedTypes.Function.check(node)) {
+            path.get("params").each(function(paramPath) {
+                addPattern(paramPath, bindings);
+            });
+
+            recursiveScanChild(path.get("body"), bindings, scopeTypes);
+
+        } else if (namedTypes.TypeAlias && namedTypes.TypeAlias.check(node)) {
+            addTypePattern(path.get("id"), scopeTypes);
+
+        } else if (namedTypes.VariableDeclarator.check(node)) {
+            addPattern(path.get("id"), bindings);
+            recursiveScanChild(path.get("init"), bindings, scopeTypes);
+
+        } else if (node.type === "ImportSpecifier" ||
+          node.type === "ImportNamespaceSpecifier" ||
+          node.type === "ImportDefaultSpecifier") {
+            addPattern(
+              // Esprima used to use the .name field to refer to the local
+              // binding identifier for ImportSpecifier nodes, but .id for
+              // ImportNamespaceSpecifier and ImportDefaultSpecifier nodes.
+              // ESTree/Acorn/ESpree use .local for all three node types.
+              path.get(node.local ? "local" :
+                node.name ? "name" : "id"),
+              bindings
+            );
+
+        } else if (Node.check(node) && !Expression.check(node)) {
+            types.eachField(node, function(name, child) {
+                var childPath = path.get(name);
+                if (!pathHasValue(childPath, child)) {
+                    throw new Error("");
+                }
+                recursiveScanChild(childPath, bindings, scopeTypes);
+            });
+        }
+    }
 
-    } else if (namedTypes.Function.check(node)) {
-        path.get("params").each(function(paramPath) {
-            addPattern(paramPath, bindings);
-        });
+    function pathHasValue(path, value) {
+        if (path.value === value) {
+            return true;
+        }
 
-        recursiveScanChild(path.get("body"), bindings, scopeTypes);
-
-    } else if (namedTypes.TypeAlias && namedTypes.TypeAlias.check(node)) {
-        addTypePattern(path.get("id"), scopeTypes);
-
-    } else if (namedTypes.VariableDeclarator.check(node)) {
-        addPattern(path.get("id"), bindings);
-        recursiveScanChild(path.get("init"), bindings, scopeTypes);
-
-    } else if (node.type === "ImportSpecifier" ||
-               node.type === "ImportNamespaceSpecifier" ||
-               node.type === "ImportDefaultSpecifier") {
-        addPattern(
-            // Esprima used to use the .name field to refer to the local
-            // binding identifier for ImportSpecifier nodes, but .id for
-            // ImportNamespaceSpecifier and ImportDefaultSpecifier nodes.
-            // ESTree/Acorn/ESpree use .local for all three node types.
-            path.get(node.local ? "local" :
-                     node.name ? "name" : "id"),
-            bindings
-        );
+        // Empty arrays are probably produced by defaults.emptyArray, in which
+        // case is makes sense to regard them as equivalent, if not ===.
+        if (Array.isArray(path.value) &&
+          path.value.length === 0 &&
+          Array.isArray(value) &&
+          value.length === 0) {
+            return true;
+        }
 
-    } else if (Node.check(node) && !Expression.check(node)) {
-        types.eachField(node, function(name, child) {
-            var childPath = path.get(name);
-            if (childPath.value !== child) {
-                throw new Error("");
-            }
-            recursiveScanChild(childPath, bindings, scopeTypes);
-        });
+        return false;
     }
-}
 
-function recursiveScanChild(path, bindings, scopeTypes) {
-    var node = path.value;
+    function recursiveScanChild(path, bindings, scopeTypes) {
+        var node = path.value;
 
-    if (!node || Expression.check(node)) {
-        // Ignore falsy values and Expressions.
+        if (!node || Expression.check(node)) {
+            // Ignore falsy values and Expressions.
 
-    } else if (namedTypes.FunctionDeclaration.check(node)) {
-        addPattern(path.get("id"), bindings);
+        } else if (namedTypes.FunctionDeclaration.check(node)) {
+            addPattern(path.get("id"), bindings);
 
-    } else if (namedTypes.ClassDeclaration &&
-               namedTypes.ClassDeclaration.check(node)) {
-        addPattern(path.get("id"), bindings);
+        } else if (namedTypes.ClassDeclaration &&
+          namedTypes.ClassDeclaration.check(node)) {
+            addPattern(path.get("id"), bindings);
 
-    } else if (ScopeType.check(node)) {
-        if (namedTypes.CatchClause.check(node)) {
-            var catchParamName = node.param.name;
-            var hadBinding = hasOwn.call(bindings, catchParamName);
-
-            // Any declarations that occur inside the catch body that do
-            // not have the same name as the catch parameter should count
-            // as bindings in the outer scope.
-            recursiveScanScope(path.get("body"), bindings, scopeTypes);
-
-            // If a new binding matching the catch parameter name was
-            // created while scanning the catch body, ignore it because it
-            // actually refers to the catch parameter and not the outer
-            // scope that we're currently scanning.
-            if (!hadBinding) {
-                delete bindings[catchParamName];
-            }
-        }
+        } else if (ScopeType.check(node)) {
+            if (namedTypes.CatchClause.check(node)) {
+                var catchParamName = node.param.name;
+                var hadBinding = hasOwn.call(bindings, catchParamName);
 
-    } else {
-        recursiveScanScope(path, bindings, scopeTypes);
-    }
-}
+                // Any declarations that occur inside the catch body that do
+                // not have the same name as the catch parameter should count
+                // as bindings in the outer scope.
+                recursiveScanScope(path.get("body"), bindings, scopeTypes);
 
-function addPattern(patternPath, bindings) {
-    var pattern = patternPath.value;
-    namedTypes.Pattern.assert(pattern);
+                // If a new binding matching the catch parameter name was
+                // created while scanning the catch body, ignore it because it
+                // actually refers to the catch parameter and not the outer
+                // scope that we're currently scanning.
+                if (!hadBinding) {
+                    delete bindings[catchParamName];
+                }
+            }
 
-    if (namedTypes.Identifier.check(pattern)) {
-        if (hasOwn.call(bindings, pattern.name)) {
-            bindings[pattern.name].push(patternPath);
         } else {
-            bindings[pattern.name] = [patternPath];
+            recursiveScanScope(path, bindings, scopeTypes);
         }
+    }
 
-    } else if (namedTypes.ObjectPattern &&
-               namedTypes.ObjectPattern.check(pattern)) {
-        patternPath.get('properties').each(function(propertyPath) {
-            var property = propertyPath.value;
-            if (namedTypes.Pattern.check(property)) {
-                addPattern(propertyPath, bindings);
-            } else  if (namedTypes.Property.check(property)) {
-                addPattern(propertyPath.get('value'), bindings);
-            } else if (namedTypes.SpreadProperty &&
-                       namedTypes.SpreadProperty.check(property)) {
-                addPattern(propertyPath.get('argument'), bindings);
-            }
-        });
+    function addPattern(patternPath, bindings) {
+        var pattern = patternPath.value;
+        namedTypes.Pattern.assert(pattern);
 
-    } else if (namedTypes.ArrayPattern &&
-               namedTypes.ArrayPattern.check(pattern)) {
-        patternPath.get('elements').each(function(elementPath) {
-            var element = elementPath.value;
-            if (namedTypes.Pattern.check(element)) {
-                addPattern(elementPath, bindings);
-            } else if (namedTypes.SpreadElement &&
-                       namedTypes.SpreadElement.check(element)) {
-                addPattern(elementPath.get("argument"), bindings);
+        if (namedTypes.Identifier.check(pattern)) {
+            if (hasOwn.call(bindings, pattern.name)) {
+                bindings[pattern.name].push(patternPath);
+            } else {
+                bindings[pattern.name] = [patternPath];
             }
-        });
-
-    } else if (namedTypes.PropertyPattern &&
-               namedTypes.PropertyPattern.check(pattern)) {
-        addPattern(patternPath.get('pattern'), bindings);
 
-    } else if ((namedTypes.SpreadElementPattern &&
-                namedTypes.SpreadElementPattern.check(pattern)) ||
-               (namedTypes.SpreadPropertyPattern &&
-                namedTypes.SpreadPropertyPattern.check(pattern))) {
-        addPattern(patternPath.get('argument'), bindings);
+        } else if (namedTypes.ObjectPattern &&
+          namedTypes.ObjectPattern.check(pattern)) {
+            patternPath.get('properties').each(function(propertyPath) {
+                var property = propertyPath.value;
+                if (namedTypes.Pattern.check(property)) {
+                    addPattern(propertyPath, bindings);
+                } else  if (namedTypes.Property.check(property)) {
+                    addPattern(propertyPath.get('value'), bindings);
+                } else if (namedTypes.SpreadProperty &&
+                  namedTypes.SpreadProperty.check(property)) {
+                    addPattern(propertyPath.get('argument'), bindings);
+                }
+            });
+
+        } else if (namedTypes.ArrayPattern &&
+          namedTypes.ArrayPattern.check(pattern)) {
+            patternPath.get('elements').each(function(elementPath) {
+                var element = elementPath.value;
+                if (namedTypes.Pattern.check(element)) {
+                    addPattern(elementPath, bindings);
+                } else if (namedTypes.SpreadElement &&
+                  namedTypes.SpreadElement.check(element)) {
+                    addPattern(elementPath.get("argument"), bindings);
+                }
+            });
+
+        } else if (namedTypes.PropertyPattern &&
+          namedTypes.PropertyPattern.check(pattern)) {
+            addPattern(patternPath.get('pattern'), bindings);
+
+        } else if ((namedTypes.SpreadElementPattern &&
+          namedTypes.SpreadElementPattern.check(pattern)) ||
+          (namedTypes.SpreadPropertyPattern &&
+          namedTypes.SpreadPropertyPattern.check(pattern))) {
+            addPattern(patternPath.get('argument'), bindings);
+        }
     }
-}
 
-function addTypePattern(patternPath, types) {
-    var pattern = patternPath.value;
-    namedTypes.Pattern.assert(pattern);
+    function addTypePattern(patternPath, types) {
+        var pattern = patternPath.value;
+        namedTypes.Pattern.assert(pattern);
 
-    if (namedTypes.Identifier.check(pattern)) {
-        if (hasOwn.call(types, pattern.name)) {
-            types[pattern.name].push(patternPath);
-        } else {
-            types[pattern.name] = [patternPath];
-        }
+        if (namedTypes.Identifier.check(pattern)) {
+            if (hasOwn.call(types, pattern.name)) {
+                types[pattern.name].push(patternPath);
+            } else {
+                types[pattern.name] = [patternPath];
+            }
 
+        }
     }
-}
-
-Sp.lookup = function(name) {
-    for (var scope = this; scope; scope = scope.parent)
-        if (scope.declares(name))
-            break;
-    return scope;
-};
-
-Sp.lookupType = function(name) {
-    for (var scope = this; scope; scope = scope.parent)
-        if (scope.declaresType(name))
-            break;
-    return scope;
-};
 
-Sp.getGlobalScope = function() {
-    var scope = this;
-    while (!scope.isGlobal)
-        scope = scope.parent;
-    return scope;
+    Sp.lookup = function(name) {
+        for (var scope = this; scope; scope = scope.parent)
+            if (scope.declares(name))
+                break;
+        return scope;
+    };
+
+    Sp.lookupType = function(name) {
+        for (var scope = this; scope; scope = scope.parent)
+            if (scope.declaresType(name))
+                break;
+        return scope;
+    };
+
+    Sp.getGlobalScope = function() {
+        var scope = this;
+        while (!scope.isGlobal)
+            scope = scope.parent;
+        return scope;
+    };
+    
+    return Scope;
 };
-
-module.exports = Scope;
diff --git a/lib/shared.js b/lib/shared.js
index 3e84086..a2df7a9 100644
--- a/lib/shared.js
+++ b/lib/shared.js
@@ -1,41 +1,46 @@
-var types = require("../lib/types");
-var Type = types.Type;
-var builtin = types.builtInTypes;
-var isNumber = builtin.number;
+module.exports = function (fork) {
+    var exports = {};
+    var types = fork.use(require("../lib/types"));
+    var Type = types.Type;
+    var builtin = types.builtInTypes;
+    var isNumber = builtin.number;
 
-// An example of constructing a new type with arbitrary constraints from
-// an existing type.
-exports.geq = function(than) {
-    return new Type(function(value) {
-        return isNumber.check(value) && value >= than;
-    }, isNumber + " >= " + than);
-};
+    // An example of constructing a new type with arbitrary constraints from
+    // an existing type.
+    exports.geq = function (than) {
+        return new Type(function (value) {
+            return isNumber.check(value) && value >= than;
+        }, isNumber + " >= " + than);
+    };
 
-// Default value-returning functions that may optionally be passed as a
-// third argument to Def.prototype.field.
-exports.defaults = {
-    // Functions were used because (among other reasons) that's the most
-    // elegant way to allow for the emptyArray one always to give a new
-    // array instance.
-    "null": function() { return null },
-    "emptyArray": function() { return [] },
-    "false": function() { return false },
-    "true": function() { return true },
-    "undefined": function() {}
-};
+    // Default value-returning functions that may optionally be passed as a
+    // third argument to Def.prototype.field.
+    exports.defaults = {
+        // Functions were used because (among other reasons) that's the most
+        // elegant way to allow for the emptyArray one always to give a new
+        // array instance.
+        "null": function () { return null },
+        "emptyArray": function () { return [] },
+        "false": function () { return false },
+        "true": function () { return true },
+        "undefined": function () {}
+    };
 
-var naiveIsPrimitive = Type.or(
-    builtin.string,
-    builtin.number,
-    builtin.boolean,
-    builtin.null,
-    builtin.undefined
-);
+    var naiveIsPrimitive = Type.or(
+      builtin.string,
+      builtin.number,
+      builtin.boolean,
+      builtin.null,
+      builtin.undefined
+    );
 
-exports.isPrimitive = new Type(function(value) {
-    if (value === null)
-        return true;
-    var type = typeof value;
-    return !(type === "object" ||
-             type === "function");
-}, naiveIsPrimitive.toString());
+    exports.isPrimitive = new Type(function (value) {
+        if (value === null)
+            return true;
+        var type = typeof value;
+        return !(type === "object" ||
+        type === "function");
+    }, naiveIsPrimitive.toString());
+
+    return exports;
+};
\ No newline at end of file
diff --git a/lib/types.js b/lib/types.js
index b4d4aee..927cba4 100644
--- a/lib/types.js
+++ b/lib/types.js
@@ -8,821 +8,828 @@ var funObjStr = objToStr.call(function(){});
 var strObjStr = objToStr.call("");
 var hasOwn = Op.hasOwnProperty;
 
-// A type is an object with a .check method that takes a value and returns
-// true or false according to whether the value matches the type.
+module.exports = function () {
 
-function Type(check, name) {
-    var self = this;
-    if (!(self instanceof Type)) {
-        throw new Error("Type constructor cannot be invoked without 'new'");
-    }
+    var exports = {};
 
-    // Unfortunately we can't elegantly reuse isFunction and isString,
-    // here, because this code is executed while defining those types.
-    if (objToStr.call(check) !== funObjStr) {
-        throw new Error(check + " is not a function");
-    }
+    // A type is an object with a .check method that takes a value and returns
+    // true or false according to whether the value matches the type.
+
+    function Type(check, name) {
+        var self = this;
+        if (!(self instanceof Type)) {
+            throw new Error("Type constructor cannot be invoked without 'new'");
+        }
+
+        // Unfortunately we can't elegantly reuse isFunction and isString,
+        // here, because this code is executed while defining those types.
+        if (objToStr.call(check) !== funObjStr) {
+            throw new Error(check + " is not a function");
+        }
 
-    // The `name` parameter can be either a function or a string.
-    var nameObjStr = objToStr.call(name);
-    if (!(nameObjStr === funObjStr ||
+        // The `name` parameter can be either a function or a string.
+        var nameObjStr = objToStr.call(name);
+        if (!(nameObjStr === funObjStr ||
           nameObjStr === strObjStr)) {
-        throw new Error(name + " is neither a function nor a string");
-    }
+            throw new Error(name + " is neither a function nor a string");
+        }
 
-    Object.defineProperties(self, {
-        name: { value: name },
-        check: {
-            value: function(value, deep) {
-                var result = check.call(self, value, deep);
-                if (!result && deep && objToStr.call(deep) === funObjStr)
-                    deep(self, value);
-                return result;
+        Object.defineProperties(self, {
+            name: {value: name},
+            check: {
+                value: function (value, deep) {
+                    var result = check.call(self, value, deep);
+                    if (!result && deep && objToStr.call(deep) === funObjStr)
+                        deep(self, value);
+                    return result;
+                }
             }
-        }
-    });
-}
+        });
+    }
 
-var Tp = Type.prototype;
+    var Tp = Type.prototype;
 
-// Throughout this file we use Object.defineProperty to prevent
-// redefinition of exported properties.
-exports.Type = Type;
+    // Throughout this file we use Object.defineProperty to prevent
+    // redefinition of exported properties.
+    exports.Type = Type;
 
-// Like .check, except that failure triggers an AssertionError.
-Tp.assert = function(value, deep) {
-    if (!this.check(value, deep)) {
-        var str = shallowStringify(value);
-        throw new Error(str + " does not match type " + this);
-    }
-    return true;
-};
+    // Like .check, except that failure triggers an AssertionError.
+    Tp.assert = function (value, deep) {
+        if (!this.check(value, deep)) {
+            var str = shallowStringify(value);
+            throw new Error(str + " does not match type " + this);
+        }
+        return true;
+    };
 
-function shallowStringify(value) {
-    if (isObject.check(value))
-        return "{" + Object.keys(value).map(function(key) {
-            return key + ": " + value[key];
-        }).join(", ") + "}";
+    function shallowStringify(value) {
+        if (isObject.check(value))
+            return "{" + Object.keys(value).map(function (key) {
+                  return key + ": " + value[key];
+              }).join(", ") + "}";
 
-    if (isArray.check(value))
-        return "[" + value.map(shallowStringify).join(", ") + "]";
+        if (isArray.check(value))
+            return "[" + value.map(shallowStringify).join(", ") + "]";
 
-    return JSON.stringify(value);
-}
+        return JSON.stringify(value);
+    }
 
-Tp.toString = function() {
-    var name = this.name;
+    Tp.toString = function () {
+        var name = this.name;
 
-    if (isString.check(name))
-        return name;
+        if (isString.check(name))
+            return name;
 
-    if (isFunction.check(name))
-        return name.call(this) + "";
+        if (isFunction.check(name))
+            return name.call(this) + "";
 
-    return name + " type";
-};
+        return name + " type";
+    };
 
-var builtInCtorFns = [];
-var builtInCtorTypes = [];
-var builtInTypes = {};
-exports.builtInTypes = builtInTypes;
+    var builtInCtorFns = [];
+    var builtInCtorTypes = [];
+    var builtInTypes = {};
+    exports.builtInTypes = builtInTypes;
 
-function defBuiltInType(example, name) {
-    var objStr = objToStr.call(example);
+    function defBuiltInType(example, name) {
+        var objStr = objToStr.call(example);
 
-    var type = new Type(function(value) {
-        return objToStr.call(value) === objStr;
-    }, name);
+        var type = new Type(function (value) {
+            return objToStr.call(value) === objStr;
+        }, name);
 
-    builtInTypes[name] = type;
+        builtInTypes[name] = type;
 
-    if (example && typeof example.constructor === "function") {
-        builtInCtorFns.push(example.constructor);
-        builtInCtorTypes.push(type);
+        if (example && typeof example.constructor === "function") {
+            builtInCtorFns.push(example.constructor);
+            builtInCtorTypes.push(type);
+        }
+
+        return type;
     }
 
-    return type;
-}
-
-// These types check the underlying [[Class]] attribute of the given
-// value, rather than using the problematic typeof operator. Note however
-// that no subtyping is considered; so, for instance, isObject.check
-// returns false for [], /./, new Date, and null.
-var isString = defBuiltInType("truthy", "string");
-var isFunction = defBuiltInType(function(){}, "function");
-var isArray = defBuiltInType([], "array");
-var isObject = defBuiltInType({}, "object");
-var isRegExp = defBuiltInType(/./, "RegExp");
-var isDate = defBuiltInType(new Date, "Date");
-var isNumber = defBuiltInType(3, "number");
-var isBoolean = defBuiltInType(true, "boolean");
-var isNull = defBuiltInType(null, "null");
-var isUndefined = defBuiltInType(void 0, "undefined");
-
-// There are a number of idiomatic ways of expressing types, so this
-// function serves to coerce them all to actual Type objects. Note that
-// providing the name argument is not necessary in most cases.
-function toType(from, name) {
-    // The toType function should of course be idempotent.
-    if (from instanceof Type)
-        return from;
-
-    // The Def type is used as a helper for constructing compound
-    // interface types for AST nodes.
-    if (from instanceof Def)
-        return from.type;
-
-    // Support [ElemType] syntax.
-    if (isArray.check(from))
-        return Type.fromArray(from);
-
-    // Support { someField: FieldType, ... } syntax.
-    if (isObject.check(from))
-        return Type.fromObject(from);
-
-    if (isFunction.check(from)) {
-        var bicfIndex = builtInCtorFns.indexOf(from);
-        if (bicfIndex >= 0) {
-            return builtInCtorTypes[bicfIndex];
+    // These types check the underlying [[Class]] attribute of the given
+    // value, rather than using the problematic typeof operator. Note however
+    // that no subtyping is considered; so, for instance, isObject.check
+    // returns false for [], /./, new Date, and null.
+    var isString = defBuiltInType("truthy", "string");
+    var isFunction = defBuiltInType(function () {}, "function");
+    var isArray = defBuiltInType([], "array");
+    var isObject = defBuiltInType({}, "object");
+    var isRegExp = defBuiltInType(/./, "RegExp");
+    var isDate = defBuiltInType(new Date, "Date");
+    var isNumber = defBuiltInType(3, "number");
+    var isBoolean = defBuiltInType(true, "boolean");
+    var isNull = defBuiltInType(null, "null");
+    var isUndefined = defBuiltInType(void 0, "undefined");
+
+    // There are a number of idiomatic ways of expressing types, so this
+    // function serves to coerce them all to actual Type objects. Note that
+    // providing the name argument is not necessary in most cases.
+    function toType(from, name) {
+        // The toType function should of course be idempotent.
+        if (from instanceof Type)
+            return from;
+
+        // The Def type is used as a helper for constructing compound
+        // interface types for AST nodes.
+        if (from instanceof Def)
+            return from.type;
+
+        // Support [ElemType] syntax.
+        if (isArray.check(from))
+            return Type.fromArray(from);
+
+        // Support { someField: FieldType, ... } syntax.
+        if (isObject.check(from))
+            return Type.fromObject(from);
+
+        if (isFunction.check(from)) {
+            var bicfIndex = builtInCtorFns.indexOf(from);
+            if (bicfIndex >= 0) {
+                return builtInCtorTypes[bicfIndex];
+            }
+
+            // If isFunction.check(from), and from is not a built-in
+            // constructor, assume from is a binary predicate function we can
+            // use to define the type.
+            return new Type(from, name);
         }
 
-        // If isFunction.check(from), and from is not a built-in
-        // constructor, assume from is a binary predicate function we can
-        // use to define the type.
-        return new Type(from, name);
+        // As a last resort, toType returns a type that matches any value that
+        // is === from. This is primarily useful for literal values like
+        // toType(null), but it has the additional advantage of allowing
+        // toType to be a total function.
+        return new Type(function (value) {
+            return value === from;
+        }, isUndefined.check(name) ? function () {
+            return from + "";
+        } : name);
     }
 
-    // As a last resort, toType returns a type that matches any value that
-    // is === from. This is primarily useful for literal values like
-    // toType(null), but it has the additional advantage of allowing
-    // toType to be a total function.
-    return new Type(function(value) {
-        return value === from;
-    }, isUndefined.check(name) ? function() {
-        return from + "";
-    } : name);
-}
-
-// Returns a type that matches the given value iff any of type1, type2,
-// etc. match the value.
-Type.or = function(/* type1, type2, ... */) {
-    var types = [];
-    var len = arguments.length;
-    for (var i = 0; i < len; ++i)
-        types.push(toType(arguments[i]));
-
-    return new Type(function(value, deep) {
+    // Returns a type that matches the given value iff any of type1, type2,
+    // etc. match the value.
+    Type.or = function (/* type1, type2, ... */) {
+        var types = [];
+        var len = arguments.length;
         for (var i = 0; i < len; ++i)
-            if (types[i].check(value, deep))
-                return true;
-        return false;
-    }, function() {
-        return types.join(" | ");
-    });
-};
+            types.push(toType(arguments[i]));
+
+        return new Type(function (value, deep) {
+            for (var i = 0; i < len; ++i)
+                if (types[i].check(value, deep))
+                    return true;
+            return false;
+        }, function () {
+            return types.join(" | ");
+        });
+    };
 
-Type.fromArray = function(arr) {
-    if (!isArray.check(arr)) {
-        throw new Error("");
-    }
-    if (arr.length !== 1) {
-        throw new Error("only one element type is permitted for typed arrays");
-    }
-    return toType(arr[0]).arrayOf();
-};
+    Type.fromArray = function (arr) {
+        if (!isArray.check(arr)) {
+            throw new Error("");
+        }
+        if (arr.length !== 1) {
+            throw new Error("only one element type is permitted for typed arrays");
+        }
+        return toType(arr[0]).arrayOf();
+    };
 
-Tp.arrayOf = function() {
-    var elemType = this;
-    return new Type(function(value, deep) {
-        return isArray.check(value) && value.every(function(elem) {
-            return elemType.check(elem, deep);
+    Tp.arrayOf = function () {
+        var elemType = this;
+        return new Type(function (value, deep) {
+            return isArray.check(value) && value.every(function (elem) {
+                  return elemType.check(elem, deep);
+              });
+        }, function () {
+            return "[" + elemType + "]";
         });
-    }, function() {
-        return "[" + elemType + "]";
-    });
-};
+    };
 
-Type.fromObject = function(obj) {
-    var fields = Object.keys(obj).map(function(name) {
-        return new Field(name, obj[name]);
-    });
+    Type.fromObject = function (obj) {
+        var fields = Object.keys(obj).map(function (name) {
+            return new Field(name, obj[name]);
+        });
 
-    return new Type(function(value, deep) {
-        return isObject.check(value) && fields.every(function(field) {
-            return field.type.check(value[field.name], deep);
+        return new Type(function (value, deep) {
+            return isObject.check(value) && fields.every(function (field) {
+                  return field.type.check(value[field.name], deep);
+              });
+        }, function () {
+            return "{ " + fields.join(", ") + " }";
         });
-    }, function() {
-        return "{ " + fields.join(", ") + " }";
-    });
-};
+    };
 
-function Field(name, type, defaultFn, hidden) {
-    var self = this;
+    function Field(name, type, defaultFn, hidden) {
+        var self = this;
 
-    if (!(self instanceof Field)) {
-        throw new Error("Field constructor cannot be invoked without 'new'");
-    }
-    isString.assert(name);
+        if (!(self instanceof Field)) {
+            throw new Error("Field constructor cannot be invoked without 'new'");
+        }
+        isString.assert(name);
 
-    type = toType(type);
+        type = toType(type);
 
-    var properties = {
-        name: { value: name },
-        type: { value: type },
-        hidden: { value: !!hidden }
-    };
+        var properties = {
+            name: {value: name},
+            type: {value: type},
+            hidden: {value: !!hidden}
+        };
+
+        if (isFunction.check(defaultFn)) {
+            properties.defaultFn = {value: defaultFn};
+        }
 
-    if (isFunction.check(defaultFn)) {
-        properties.defaultFn = { value: defaultFn };
+        Object.defineProperties(self, properties);
     }
 
-    Object.defineProperties(self, properties);
-}
+    var Fp = Field.prototype;
 
-var Fp = Field.prototype;
+    Fp.toString = function () {
+        return JSON.stringify(this.name) + ": " + this.type;
+    };
 
-Fp.toString = function() {
-    return JSON.stringify(this.name) + ": " + this.type;
-};
+    Fp.getValue = function (obj) {
+        var value = obj[this.name];
 
-Fp.getValue = function(obj) {
-    var value = obj[this.name];
+        if (!isUndefined.check(value))
+            return value;
 
-    if (!isUndefined.check(value))
-        return value;
+        if (this.defaultFn)
+            value = this.defaultFn.call(obj);
 
-    if (this.defaultFn)
-        value = this.defaultFn.call(obj);
+        return value;
+    };
 
-    return value;
-};
+    // Define a type whose name is registered in a namespace (the defCache) so
+    // that future definitions will return the same type given the same name.
+    // In particular, this system allows for circular and forward definitions.
+    // The Def object d returned from Type.def may be used to configure the
+    // type d.type by calling methods such as d.bases, d.build, and d.field.
+    Type.def = function (typeName) {
+        isString.assert(typeName);
+        return hasOwn.call(defCache, typeName)
+          ? defCache[typeName]
+          : defCache[typeName] = new Def(typeName);
+    };
 
-// Define a type whose name is registered in a namespace (the defCache) so
-// that future definitions will return the same type given the same name.
-// In particular, this system allows for circular and forward definitions.
-// The Def object d returned from Type.def may be used to configure the
-// type d.type by calling methods such as d.bases, d.build, and d.field.
-Type.def = function(typeName) {
-    isString.assert(typeName);
-    return hasOwn.call(defCache, typeName)
-        ? defCache[typeName]
-        : defCache[typeName] = new Def(typeName);
-};
+    // In order to return the same Def instance every time Type.def is called
+    // with a particular name, those instances need to be stored in a cache.
+    var defCache = Object.create(null);
 
-// In order to return the same Def instance every time Type.def is called
-// with a particular name, those instances need to be stored in a cache.
-var defCache = Object.create(null);
+    function Def(typeName) {
+        var self = this;
+        if (!(self instanceof Def)) {
+            throw new Error("Def constructor cannot be invoked without 'new'");
+        }
 
-function Def(typeName) {
-    var self = this;
-    if (!(self instanceof Def)) {
-        throw new Error("Def constructor cannot be invoked without 'new'");
+        Object.defineProperties(self, {
+            typeName: {value: typeName},
+            baseNames: {value: []},
+            ownFields: {value: Object.create(null)},
+
+            // These two are populated during finalization.
+            allSupertypes: {value: Object.create(null)}, // Includes own typeName.
+            supertypeList: {value: []}, // Linear inheritance hierarchy.
+            allFields: {value: Object.create(null)}, // Includes inherited fields.
+            fieldNames: {value: []}, // Non-hidden keys of allFields.
+
+            type: {
+                value: new Type(function (value, deep) {
+                    return self.check(value, deep);
+                }, typeName)
+            }
+        });
     }
 
-    Object.defineProperties(self, {
-        typeName: { value: typeName },
-        baseNames: { value: [] },
-        ownFields: { value: Object.create(null) },
-
-        // These two are populated during finalization.
-        allSupertypes: { value: Object.create(null) }, // Includes own typeName.
-        supertypeList: { value: [] }, // Linear inheritance hierarchy.
-        allFields: { value: Object.create(null) }, // Includes inherited fields.
-        fieldNames: { value: [] }, // Non-hidden keys of allFields.
-
-        type: {
-            value: new Type(function(value, deep) {
-                return self.check(value, deep);
-            }, typeName)
-        }
-    });
-}
-
-Def.fromValue = function(value) {
-    if (value && typeof value === "object") {
-        var type = value.type;
-        if (typeof type === "string" &&
-            hasOwn.call(defCache, type)) {
-            var d = defCache[type];
-            if (d.finalized) {
-                return d;
+    Def.fromValue = function (value) {
+        if (value && typeof value === "object") {
+            var type = value.type;
+            if (typeof type === "string" &&
+              hasOwn.call(defCache, type)) {
+                var d = defCache[type];
+                if (d.finalized) {
+                    return d;
+                }
             }
         }
-    }
 
-    return null;
-};
+        return null;
+    };
 
-var Dp = Def.prototype;
+    var Dp = Def.prototype;
 
-Dp.isSupertypeOf = function(that) {
-    if (that instanceof Def) {
-        if (this.finalized !== true ||
-            that.finalized !== true) {
-            throw new Error("");
+    Dp.isSupertypeOf = function (that) {
+        if (that instanceof Def) {
+            if (this.finalized !== true ||
+              that.finalized !== true) {
+                throw new Error("");
+            }
+            return hasOwn.call(that.allSupertypes, this.typeName);
+        } else {
+            throw new Error(that + " is not a Def");
         }
-        return hasOwn.call(that.allSupertypes, this.typeName);
-    } else {
-        throw new Error(that + " is not a Def");
-    }
-};
-
-// Note that the list returned by this function is a copy of the internal
-// supertypeList, *without* the typeName itself as the first element.
-exports.getSupertypeNames = function(typeName) {
-    if (!hasOwn.call(defCache, typeName)) {
-        throw new Error("");
-    }
-    var d = defCache[typeName];
-    if (d.finalized !== true) {
-        throw new Error("");
-    }
-    return d.supertypeList.slice(1);
-};
-
-// Returns an object mapping from every known type in the defCache to the
-// most specific supertype whose name is an own property of the candidates
-// object.
-exports.computeSupertypeLookupTable = function(candidates) {
-    var table = {};
-    var typeNames = Object.keys(defCache);
-    var typeNameCount = typeNames.length;
+    };
 
-    for (var i = 0; i < typeNameCount; ++i) {
-        var typeName = typeNames[i];
+    // Note that the list returned by this function is a copy of the internal
+    // supertypeList, *without* the typeName itself as the first element.
+    exports.getSupertypeNames = function (typeName) {
+        if (!hasOwn.call(defCache, typeName)) {
+            throw new Error("");
+        }
         var d = defCache[typeName];
         if (d.finalized !== true) {
-            throw new Error("" + typeName);
+            throw new Error("");
         }
-        for (var j = 0; j < d.supertypeList.length; ++j) {
-            var superTypeName = d.supertypeList[j];
-            if (hasOwn.call(candidates, superTypeName)) {
-                table[typeName] = superTypeName;
-                break;
+        return d.supertypeList.slice(1);
+    };
+
+    // Returns an object mapping from every known type in the defCache to the
+    // most specific supertype whose name is an own property of the candidates
+    // object.
+    exports.computeSupertypeLookupTable = function (candidates) {
+        var table = {};
+        var typeNames = Object.keys(defCache);
+        var typeNameCount = typeNames.length;
+
+        for (var i = 0; i < typeNameCount; ++i) {
+            var typeName = typeNames[i];
+            var d = defCache[typeName];
+            if (d.finalized !== true) {
+                throw new Error("" + typeName);
+            }
+            for (var j = 0; j < d.supertypeList.length; ++j) {
+                var superTypeName = d.supertypeList[j];
+                if (hasOwn.call(candidates, superTypeName)) {
+                    table[typeName] = superTypeName;
+                    break;
+                }
             }
         }
-    }
-
-    return table;
-};
 
-Dp.checkAllFields = function(value, deep) {
-    var allFields = this.allFields;
-    if (this.finalized !== true) {
-        throw new Error("" + this.typeName);
-    }
+        return table;
+    };
 
-    function checkFieldByName(name) {
-        var field = allFields[name];
-        var type = field.type;
-        var child = field.getValue(value);
-        return type.check(child, deep);
-    }
+    Dp.checkAllFields = function (value, deep) {
+        var allFields = this.allFields;
+        if (this.finalized !== true) {
+            throw new Error("" + this.typeName);
+        }
 
-    return isObject.check(value)
-        && Object.keys(allFields).every(checkFieldByName);
-};
+        function checkFieldByName(name) {
+            var field = allFields[name];
+            var type = field.type;
+            var child = field.getValue(value);
+            return type.check(child, deep);
+        }
 
-Dp.check = function(value, deep) {
-    if (this.finalized !== true) {
-        throw new Error(
-            "prematurely checking unfinalized type " + this.typeName
-        );
-    }
+        return isObject.check(value)
+          && Object.keys(allFields).every(checkFieldByName);
+    };
 
-    // A Def type can only match an object value.
-    if (!isObject.check(value))
-        return false;
-
-    var vDef = Def.fromValue(value);
-    if (!vDef) {
-        // If we couldn't infer the Def associated with the given value,
-        // and we expected it to be a SourceLocation or a Position, it was
-        // probably just missing a "type" field (because Esprima does not
-        // assign a type property to such nodes). Be optimistic and let
-        // this.checkAllFields make the final decision.
-        if (this.typeName === "SourceLocation" ||
-            this.typeName === "Position") {
-            return this.checkAllFields(value, deep);
+    Dp.check = function (value, deep) {
+        if (this.finalized !== true) {
+            throw new Error(
+              "prematurely checking unfinalized type " + this.typeName
+            );
         }
 
-        // Calling this.checkAllFields for any other type of node is both
-        // bad for performance and way too forgiving.
-        return false;
-    }
+        // A Def type can only match an object value.
+        if (!isObject.check(value))
+            return false;
+
+        var vDef = Def.fromValue(value);
+        if (!vDef) {
+            // If we couldn't infer the Def associated with the given value,
+            // and we expected it to be a SourceLocation or a Position, it was
+            // probably just missing a "type" field (because Esprima does not
+            // assign a type property to such nodes). Be optimistic and let
+            // this.checkAllFields make the final decision.
+            if (this.typeName === "SourceLocation" ||
+              this.typeName === "Position") {
+                return this.checkAllFields(value, deep);
+            }
 
-    // If checking deeply and vDef === this, then we only need to call
-    // checkAllFields once. Calling checkAllFields is too strict when deep
-    // is false, because then we only care about this.isSupertypeOf(vDef).
-    if (deep && vDef === this)
-        return this.checkAllFields(value, deep);
-
-    // In most cases we rely exclusively on isSupertypeOf to make O(1)
-    // subtyping determinations. This suffices in most situations outside
-    // of unit tests, since interface conformance is checked whenever new
-    // instances are created using builder functions.
-    if (!this.isSupertypeOf(vDef))
-        return false;
-
-    // The exception is when deep is true; then, we recursively check all
-    // fields.
-    if (!deep)
-        return true;
+            // Calling this.checkAllFields for any other type of node is both
+            // bad for performance and way too forgiving.
+            return false;
+        }
 
-    // Use the more specific Def (vDef) to perform the deep check, but
-    // shallow-check fields defined by the less specific Def (this).
-    return vDef.checkAllFields(value, deep)
-        && this.checkAllFields(value, false);
-};
+        // If checking deeply and vDef === this, then we only need to call
+        // checkAllFields once. Calling checkAllFields is too strict when deep
+        // is false, because then we only care about this.isSupertypeOf(vDef).
+        if (deep && vDef === this)
+            return this.checkAllFields(value, deep);
+
+        // In most cases we rely exclusively on isSupertypeOf to make O(1)
+        // subtyping determinations. This suffices in most situations outside
+        // of unit tests, since interface conformance is checked whenever new
+        // instances are created using builder functions.
+        if (!this.isSupertypeOf(vDef))
+            return false;
+
+        // The exception is when deep is true; then, we recursively check all
+        // fields.
+        if (!deep)
+            return true;
+
+        // Use the more specific Def (vDef) to perform the deep check, but
+        // shallow-check fields defined by the less specific Def (this).
+        return vDef.checkAllFields(value, deep)
+          && this.checkAllFields(value, false);
+    };
 
-Dp.bases = function() {
-    var args = slice.call(arguments);
-    var bases = this.baseNames;
+    Dp.bases = function () {
+        var args = slice.call(arguments);
+        var bases = this.baseNames;
 
-    if (this.finalized) {
-        if (args.length !== bases.length) {
-            throw new Error("");
-        }
-        for (var i = 0; i < args.length; i++) {
-            if (args[i] !== bases[i]) {
+        if (this.finalized) {
+            if (args.length !== bases.length) {
                 throw new Error("");
             }
+            for (var i = 0; i < args.length; i++) {
+                if (args[i] !== bases[i]) {
+                    throw new Error("");
+                }
+            }
+            return this;
         }
-        return this;
-    }
 
-    args.forEach(function(baseName) {
-        isString.assert(baseName);
+        args.forEach(function (baseName) {
+            isString.assert(baseName);
 
-        // This indexOf lookup may be O(n), but the typical number of base
-        // names is very small, and indexOf is a native Array method.
-        if (bases.indexOf(baseName) < 0)
-            bases.push(baseName);
-    });
+            // This indexOf lookup may be O(n), but the typical number of base
+            // names is very small, and indexOf is a native Array method.
+            if (bases.indexOf(baseName) < 0)
+                bases.push(baseName);
+        });
 
-    return this; // For chaining.
-};
+        return this; // For chaining.
+    };
 
-// False by default until .build(...) is called on an instance.
-Object.defineProperty(Dp, "buildable", { value: false });
+    // False by default until .build(...) is called on an instance.
+    Object.defineProperty(Dp, "buildable", {value: false});
 
-var builders = {};
-exports.builders = builders;
+    var builders = {};
+    exports.builders = builders;
 
-// This object is used as prototype for any node created by a builder.
-var nodePrototype = {};
+    // This object is used as prototype for any node created by a builder.
+    var nodePrototype = {};
 
-// Call this function to define a new method to be shared by all AST
-// nodes. The replaced method (if any) is returned for easy wrapping.
-exports.defineMethod = function(name, func) {
-    var old = nodePrototype[name];
+    // Call this function to define a new method to be shared by all AST
+     // nodes. The replaced method (if any) is returned for easy wrapping.
+    exports.defineMethod = function (name, func) {
+        var old = nodePrototype[name];
 
-    // Pass undefined as func to delete nodePrototype[name].
-    if (isUndefined.check(func)) {
-        delete nodePrototype[name];
+        // Pass undefined as func to delete nodePrototype[name].
+        if (isUndefined.check(func)) {
+            delete nodePrototype[name];
 
-    } else {
-        isFunction.assert(func);
+        } else {
+            isFunction.assert(func);
 
-        Object.defineProperty(nodePrototype, name, {
-            enumerable: true, // For discoverability.
-            configurable: true, // For delete proto[name].
-            value: func
-        });
-    }
+            Object.defineProperty(nodePrototype, name, {
+                enumerable: true, // For discoverability.
+                configurable: true, // For delete proto[name].
+                value: func
+            });
+        }
 
-    return old;
-};
+        return old;
+    };
 
-var isArrayOfString = isString.arrayOf();
-
-// Calling the .build method of a Def simultaneously marks the type as
-// buildable (by defining builders[getBuilderName(typeName)]) and
-// specifies the order of arguments that should be passed to the builder
-// function to create an instance of the type.
-Dp.build = function(/* param1, param2, ... */) {
-    var self = this;
-
-    var newBuildParams = slice.call(arguments);
-    isArrayOfString.assert(newBuildParams);
-
-    // Calling Def.prototype.build multiple times has the effect of merely
-    // redefining this property.
-    Object.defineProperty(self, "buildParams", {
-        value: newBuildParams,
-        writable: false,
-        enumerable: false,
-        configurable: true
-    });
-
-    if (self.buildable) {
-        // If this Def is already buildable, update self.buildParams and
-        // continue using the old builder function.
-        return self;
-    }
+    var isArrayOfString = isString.arrayOf();
+
+    // Calling the .build method of a Def simultaneously marks the type as
+    // buildable (by defining builders[getBuilderName(typeName)]) and
+    // specifies the order of arguments that should be passed to the builder
+    // function to create an instance of the type.
+    Dp.build = function (/* param1, param2, ... */) {
+        var self = this;
+
+        var newBuildParams = slice.call(arguments);
+        isArrayOfString.assert(newBuildParams);
+
+        // Calling Def.prototype.build multiple times has the effect of merely
+        // redefining this property.
+        Object.defineProperty(self, "buildParams", {
+            value: newBuildParams,
+            writable: false,
+            enumerable: false,
+            configurable: true
+        });
 
-    // Every buildable type will have its "type" field filled in
-    // automatically. This includes types that are not subtypes of Node,
-    // like SourceLocation, but that seems harmless (TODO?).
-    self.field("type", String, function() { return self.typeName });
+        if (self.buildable) {
+            // If this Def is already buildable, update self.buildParams and
+            // continue using the old builder function.
+            return self;
+        }
 
-    // Override Dp.buildable for this Def instance.
-    Object.defineProperty(self, "buildable", { value: true });
+        // Every buildable type will have its "type" field filled in
+        // automatically. This includes types that are not subtypes of Node,
+        // like SourceLocation, but that seems harmless (TODO?).
+        self.field("type", String, function () { return self.typeName });
 
-    Object.defineProperty(builders, getBuilderName(self.typeName), {
-        enumerable: true,
+        // Override Dp.buildable for this Def instance.
+        Object.defineProperty(self, "buildable", {value: true});
 
-        value: function() {
-            var args = arguments;
-            var argc = args.length;
-            var built = Object.create(nodePrototype);
+        Object.defineProperty(builders, getBuilderName(self.typeName), {
+            enumerable: true,
 
-            if (!self.finalized) {
-                throw new Error(
-                    "attempting to instantiate unfinalized type " +
-                        self.typeName
-                );
-            }
+            value: function () {
+                var args = arguments;
+                var argc = args.length;
+                var built = Object.create(nodePrototype);
 
-            function add(param, i) {
-                if (hasOwn.call(built, param))
-                    return;
+                if (!self.finalized) {
+                    throw new Error(
+                      "attempting to instantiate unfinalized type " +
+                      self.typeName
+                    );
+                }
 
-                var all = self.allFields;
-                if (!hasOwn.call(all, param)) {
-                    throw new Error("" + param);
+                function add(param, i) {
+                    if (hasOwn.call(built, param))
+                        return;
+
+                    var all = self.allFields;
+                    if (!hasOwn.call(all, param)) {
+                        throw new Error("" + param);
+                    }
+
+                    var field = all[param];
+                    var type = field.type;
+                    var value;
+
+                    if (isNumber.check(i) && i < argc) {
+                        value = args[i];
+                    } else if (field.defaultFn) {
+                        // Expose the partially-built object to the default
+                        // function as its `this` object.
+                        value = field.defaultFn.call(built);
+                    } else {
+                        var message = "no value or default function given for field " +
+                          JSON.stringify(param) + " of " + self.typeName + "(" +
+                          self.buildParams.map(function (name) {
+                              return all[name];
+                          }).join(", ") + ")";
+                        throw new Error(message);
+                    }
+
+                    if (!type.check(value)) {
+                        throw new Error(
+                          shallowStringify(value) +
+                          " does not match field " + field +
+                          " of type " + self.typeName
+                        );
+                    }
+
+                    // TODO Could attach getters and setters here to enforce
+                    // dynamic type safety.
+                    built[param] = value;
                 }
 
-                var field = all[param];
-                var type = field.type;
-                var value;
+                self.buildParams.forEach(function (param, i) {
+                    add(param, i);
+                });
 
-                if (isNumber.check(i) && i < argc) {
-                    value = args[i];
-                } else if (field.defaultFn) {
-                    // Expose the partially-built object to the default
-                    // function as its `this` object.
-                    value = field.defaultFn.call(built);
-                } else {
-                    var message = "no value or default function given for field " +
-                        JSON.stringify(param) + " of " + self.typeName + "(" +
-                            self.buildParams.map(function(name) {
-                                return all[name];
-                            }).join(", ") + ")";
-                    throw new Error(message);
-                }
+                Object.keys(self.allFields).forEach(function (param) {
+                    add(param); // Use the default value.
+                });
 
-                if (!type.check(value)) {
-                    throw new Error(
-                        shallowStringify(value) +
-                            " does not match field " + field +
-                            " of type " + self.typeName
-                    );
+                // Make sure that the "type" field was filled automatically.
+                if (built.type !== self.typeName) {
+                    throw new Error("");
                 }
 
-                // TODO Could attach getters and setters here to enforce
-                // dynamic type safety.
-                built[param] = value;
+                return built;
             }
+        });
 
-            self.buildParams.forEach(function(param, i) {
-                add(param, i);
-            });
-
-            Object.keys(self.allFields).forEach(function(param) {
-                add(param); // Use the default value.
-            });
+        return self; // For chaining.
+    };
 
-            // Make sure that the "type" field was filled automatically.
-            if (built.type !== self.typeName) {
-                throw new Error("");
+    function getBuilderName(typeName) {
+        return typeName.replace(/^[A-Z]+/, function (upperCasePrefix) {
+            var len = upperCasePrefix.length;
+            switch (len) {
+                case 0: return "";
+                // If there's only one initial capital letter, just lower-case it.
+                case 1: return upperCasePrefix.toLowerCase();
+                default:
+                    // If there's more than one initial capital letter, lower-case
+                    // all but the last one, so that XMLDefaultDeclaration (for
+                    // example) becomes xmlDefaultDeclaration.
+                    return upperCasePrefix.slice(
+                        0, len - 1).toLowerCase() +
+                      upperCasePrefix.charAt(len - 1);
             }
+        });
+    }
+    exports.getBuilderName = getBuilderName;
 
-            return built;
+    function getStatementBuilderName(typeName) {
+        typeName = getBuilderName(typeName);
+        return typeName.replace(/(Expression)?$/, "Statement");
+    }
+    exports.getStatementBuilderName = getStatementBuilderName;
+
+    // The reason fields are specified using .field(...) instead of an object
+    // literal syntax is somewhat subtle: the object literal syntax would
+    // support only one key and one value, but with .field(...) we can pass
+    // any number of arguments to specify the field.
+    Dp.field = function (name, type, defaultFn, hidden) {
+        if (this.finalized) {
+            console.error("Ignoring attempt to redefine field " +
+              JSON.stringify(name) + " of finalized type " +
+              JSON.stringify(this.typeName));
+            return this;
         }
-    });
+        this.ownFields[name] = new Field(name, type, defaultFn, hidden);
+        return this; // For chaining.
+    };
 
-    return self; // For chaining.
-};
+    var namedTypes = {};
+    exports.namedTypes = namedTypes;
 
-function getBuilderName(typeName) {
-    return typeName.replace(/^[A-Z]+/, function(upperCasePrefix) {
-        var len = upperCasePrefix.length;
-        switch (len) {
-        case 0: return "";
-        // If there's only one initial capital letter, just lower-case it.
-        case 1: return upperCasePrefix.toLowerCase();
-        default:
-            // If there's more than one initial capital letter, lower-case
-            // all but the last one, so that XMLDefaultDeclaration (for
-            // example) becomes xmlDefaultDeclaration.
-            return upperCasePrefix.slice(
-                0, len - 1).toLowerCase() +
-                upperCasePrefix.charAt(len - 1);
+    // Like Object.keys, but aware of what fields each AST type should have.
+    function getFieldNames(object) {
+        var d = Def.fromValue(object);
+        if (d) {
+            return d.fieldNames.slice(0);
         }
-    });
-}
-exports.getBuilderName = getBuilderName;
-
-function getStatementBuilderName(typeName) {
-    typeName = getBuilderName(typeName);
-    return typeName.replace(/(Expression)?$/, "Statement");
-}
-exports.getStatementBuilderName = getStatementBuilderName;
-
-// The reason fields are specified using .field(...) instead of an object
-// literal syntax is somewhat subtle: the object literal syntax would
-// support only one key and one value, but with .field(...) we can pass
-// any number of arguments to specify the field.
-Dp.field = function(name, type, defaultFn, hidden) {
-    if (this.finalized) {
-        console.error("Ignoring attempt to redefine field " +
-                      JSON.stringify(name) + " of finalized type " +
-                      JSON.stringify(this.typeName));
-        return this;
-    }
-    this.ownFields[name] = new Field(name, type, defaultFn, hidden);
-    return this; // For chaining.
-};
 
-var namedTypes = {};
-exports.namedTypes = namedTypes;
+        if ("type" in object) {
+            throw new Error(
+              "did not recognize object of type " +
+              JSON.stringify(object.type)
+            );
+        }
 
-// Like Object.keys, but aware of what fields each AST type should have.
-function getFieldNames(object) {
-    var d = Def.fromValue(object);
-    if (d) {
-        return d.fieldNames.slice(0);
+        return Object.keys(object);
     }
+    exports.getFieldNames = getFieldNames;
+
+    // Get the value of an object property, taking object.type and default
+    // functions into account.
+    function getFieldValue(object, fieldName) {
+        var d = Def.fromValue(object);
+        if (d) {
+            var field = d.allFields[fieldName];
+            if (field) {
+                return field.getValue(object);
+            }
+        }
 
-    if ("type" in object) {
-        throw new Error(
-            "did not recognize object of type " +
-                JSON.stringify(object.type)
-        );
+        return object[fieldName];
     }
+    exports.getFieldValue = getFieldValue;
+
+    // Iterate over all defined fields of an object, including those missing
+    // or undefined, passing each field name and effective value (as returned
+    // by getFieldValue) to the callback. If the object has no corresponding
+    // Def, the callback will never be called.
+    exports.eachField = function (object, callback, context) {
+        getFieldNames(object).forEach(function (name) {
+            callback.call(this, name, getFieldValue(object, name));
+        }, context);
+    };
 
-    return Object.keys(object);
-}
-exports.getFieldNames = getFieldNames;
-
-// Get the value of an object property, taking object.type and default
-// functions into account.
-function getFieldValue(object, fieldName) {
-    var d = Def.fromValue(object);
-    if (d) {
-        var field = d.allFields[fieldName];
-        if (field) {
-            return field.getValue(object);
-        }
-    }
+    // Similar to eachField, except that iteration stops as soon as the
+    // callback returns a truthy value. Like Array.prototype.some, the final
+    // result is either true or false to indicates whether the callback
+    // returned true for any element or not.
+    exports.someField = function (object, callback, context) {
+        return getFieldNames(object).some(function (name) {
+            return callback.call(this, name, getFieldValue(object, name));
+        }, context);
+    };
 
-    return object[fieldName];
-}
-exports.getFieldValue = getFieldValue;
-
-// Iterate over all defined fields of an object, including those missing
-// or undefined, passing each field name and effective value (as returned
-// by getFieldValue) to the callback. If the object has no corresponding
-// Def, the callback will never be called.
-exports.eachField = function(object, callback, context) {
-    getFieldNames(object).forEach(function(name) {
-        callback.call(this, name, getFieldValue(object, name));
-    }, context);
-};
+    // This property will be overridden as true by individual Def instances
+    // when they are finalized.
+    Object.defineProperty(Dp, "finalized", {value: false});
+
+    Dp.finalize = function () {
+        var self = this;
+
+        // It's not an error to finalize a type more than once, but only the
+        // first call to .finalize does anything.
+        if (!self.finalized) {
+            var allFields = self.allFields;
+            var allSupertypes = self.allSupertypes;
+
+            self.baseNames.forEach(function (name) {
+                var def = defCache[name];
+                if (def instanceof Def) {
+                    def.finalize();
+                    extend(allFields, def.allFields);
+                    extend(allSupertypes, def.allSupertypes);
+                } else {
+                    var message = "unknown supertype name " +
+                      JSON.stringify(name) +
+                      " for subtype " +
+                      JSON.stringify(self.typeName);
+                    throw new Error(message);
+                }
+            });
 
-// Similar to eachField, except that iteration stops as soon as the
-// callback returns a truthy value. Like Array.prototype.some, the final
-// result is either true or false to indicates whether the callback
-// returned true for any element or not.
-exports.someField = function(object, callback, context) {
-    return getFieldNames(object).some(function(name) {
-        return callback.call(this, name, getFieldValue(object, name));
-    }, context);
-};
+            // TODO Warn if fields are overridden with incompatible types.
+            extend(allFields, self.ownFields);
+            allSupertypes[self.typeName] = self;
 
-// This property will be overridden as true by individual Def instances
-// when they are finalized.
-Object.defineProperty(Dp, "finalized", { value: false });
-
-Dp.finalize = function() {
-    var self = this;
-
-    // It's not an error to finalize a type more than once, but only the
-    // first call to .finalize does anything.
-    if (!self.finalized) {
-        var allFields = self.allFields;
-        var allSupertypes = self.allSupertypes;
-
-        self.baseNames.forEach(function(name) {
-            var def = defCache[name];
-            if (def instanceof Def) {
-                def.finalize();
-                extend(allFields, def.allFields);
-                extend(allSupertypes, def.allSupertypes);
-            } else {
-                var message = "unknown supertype name " +
-                    JSON.stringify(name) +
-                    " for subtype " +
-                    JSON.stringify(self.typeName);
-                throw new Error(message);
+            self.fieldNames.length = 0;
+            for (var fieldName in allFields) {
+                if (hasOwn.call(allFields, fieldName) &&
+                    !allFields[fieldName].hidden) {
+                        self.fieldNames.push(fieldName);
+                }
             }
-        });
 
-        // TODO Warn if fields are overridden with incompatible types.
-        extend(allFields, self.ownFields);
-        allSupertypes[self.typeName] = self;
+            // Types are exported only once they have been finalized.
+            Object.defineProperty(namedTypes, self.typeName, {
+                enumerable: true,
+                value: self.type
+            });
 
-        self.fieldNames.length = 0;
-        for (var fieldName in allFields) {
-            if (hasOwn.call(allFields, fieldName) &&
-                !allFields[fieldName].hidden) {
-                self.fieldNames.push(fieldName);
+            Object.defineProperty(self, "finalized", {value: true});
+
+            // A linearization of the inheritance hierarchy.
+            populateSupertypeList(self.typeName, self.supertypeList);
+
+            if (self.buildable && self.supertypeList.lastIndexOf("Expression") >= 0) {
+                wrapExpressionBuilderWithStatement(self.typeName);
             }
         }
+    };
 
-        // Types are exported only once they have been finalized.
-        Object.defineProperty(namedTypes, self.typeName, {
-            enumerable: true,
-            value: self.type
-        });
-
-        Object.defineProperty(self, "finalized", { value: true });
+    // Adds an additional builder for Expression subtypes
+    // that wraps the built Expression in an ExpressionStatements.
+    function wrapExpressionBuilderWithStatement(typeName) {
+        var wrapperName = getStatementBuilderName(typeName);
 
-        // A linearization of the inheritance hierarchy.
-        populateSupertypeList(self.typeName, self.supertypeList);
+        // skip if the builder already exists
+        if (builders[wrapperName]) return;
 
-        if (self.buildable && self.supertypeList.lastIndexOf("Expression") >= 0) {
-            wrapExpressionBuilderWithStatement(self.typeName);
-        }
-    }
-};
+        // the builder function to wrap with builders.ExpressionStatement
+        var wrapped = builders[getBuilderName(typeName)];
 
-// Adds an additional builder for Expression subtypes
-// that wraps the built Expression in an ExpressionStatements.
-function wrapExpressionBuilderWithStatement(typeName) {
-    var wrapperName = getStatementBuilderName(typeName);
+        // skip if there is nothing to wrap
+        if (!wrapped) return;
 
-    // skip if the builder already exists
-    if (builders[wrapperName]) return;
+        builders[wrapperName] = function () {
+            return builders.expressionStatement(wrapped.apply(builders, arguments));
+        };
+    }
 
-    // the builder function to wrap with builders.ExpressionStatement
-    var wrapped = builders[getBuilderName(typeName)];
+    function populateSupertypeList(typeName, list) {
+        list.length = 0;
+        list.push(typeName);
 
-    // skip if there is nothing to wrap
-    if (!wrapped) return;
+        var lastSeen = Object.create(null);
 
-    builders[wrapperName] = function() {
-        return builders.expressionStatement(wrapped.apply(builders, arguments));
-    };
-}
+        for (var pos = 0; pos < list.length; ++pos) {
+            typeName = list[pos];
+            var d = defCache[typeName];
+            if (d.finalized !== true) {
+                throw new Error("");
+            }
 
-function populateSupertypeList(typeName, list) {
-    list.length = 0;
-    list.push(typeName);
+            // If we saw typeName earlier in the breadth-first traversal,
+            // delete the last-seen occurrence.
+            if (hasOwn.call(lastSeen, typeName)) {
+                delete list[lastSeen[typeName]];
+            }
 
-    var lastSeen = Object.create(null);
+            // Record the new index of the last-seen occurrence of typeName.
+            lastSeen[typeName] = pos;
 
-    for (var pos = 0; pos < list.length; ++pos) {
-        typeName = list[pos];
-        var d = defCache[typeName];
-        if (d.finalized !== true) {
-            throw new Error("");
+            // Enqueue the base names of this type.
+            list.push.apply(list, d.baseNames);
         }
 
-        // If we saw typeName earlier in the breadth-first traversal,
-        // delete the last-seen occurrence.
-        if (hasOwn.call(lastSeen, typeName)) {
-            delete list[lastSeen[typeName]];
+        // Compaction loop to remove array holes.
+        for (var to = 0, from = to, len = list.length; from < len; ++from) {
+            if (hasOwn.call(list, from)) {
+                list[to++] = list[from];
+            }
         }
 
-        // Record the new index of the last-seen occurrence of typeName.
-        lastSeen[typeName] = pos;
-
-        // Enqueue the base names of this type.
-        list.push.apply(list, d.baseNames);
-    }
-
-    // Compaction loop to remove array holes.
-    for (var to = 0, from = to, len = list.length; from < len; ++from) {
-        if (hasOwn.call(list, from)) {
-            list[to++] = list[from];
-        }
+        list.length = to;
     }
 
-    list.length = to;
-}
+    function extend(into, from) {
+        Object.keys(from).forEach(function (name) {
+            into[name] = from[name];
+        });
 
-function extend(into, from) {
-    Object.keys(from).forEach(function(name) {
-        into[name] = from[name];
-    });
+        return into;
+    };
 
-    return into;
-};
+    exports.finalize = function () {
+        Object.keys(defCache).forEach(function (name) {
+            defCache[name].finalize();
+        });
+    };
 
-exports.finalize = function() {
-    Object.keys(defCache).forEach(function(name) {
-        defCache[name].finalize();
-    });
+    return exports;
 };
diff --git a/main.js b/main.js
index 69f2493..571a15c 100644
--- a/main.js
+++ b/main.js
@@ -1,34 +1,16 @@
-var types = require("./lib/types");
+module.exports = require('./fork')([
+    // This core module of AST types captures ES5 as it is parsed today by
+    // git://github.com/ariya/esprima.git#master.
+    require("./def/core"),
 
-// This core module of AST types captures ES5 as it is parsed today by
-// git://github.com/ariya/esprima.git#master.
-require("./def/core");
-
-// Feel free to add to or remove from this list of extension modules to
-// configure the precise type hierarchy that you need.
-require("./def/es6");
-require("./def/es7");
-require("./def/mozilla");
-require("./def/e4x");
-require("./def/jsx");
-require("./def/flow");
-require("./def/esprima");
-require("./def/babel");
-
-types.finalize();
-
-exports.Type = types.Type;
-exports.builtInTypes = types.builtInTypes;
-exports.namedTypes = types.namedTypes;
-exports.builders = types.builders;
-exports.defineMethod = types.defineMethod;
-exports.getFieldNames = types.getFieldNames;
-exports.getFieldValue = types.getFieldValue;
-exports.eachField = types.eachField;
-exports.someField = types.someField;
-exports.getSupertypeNames = types.getSupertypeNames;
-exports.astNodesAreEquivalent = require("./lib/equiv");
-exports.finalize = types.finalize;
-exports.NodePath = require("./lib/node-path");
-exports.PathVisitor = require("./lib/path-visitor");
-exports.visit = exports.PathVisitor.visit;
+    // Feel free to add to or remove from this list of extension modules to
+    // configure the precise type hierarchy that you need.
+    require("./def/es6"),
+    require("./def/es7"),
+    require("./def/mozilla"),
+    require("./def/e4x"),
+    require("./def/jsx"),
+    require("./def/flow"),
+    require("./def/esprima"),
+    require("./def/babel")
+]);
\ No newline at end of file
diff --git a/package.json b/package.json
index e12bfcb..d1600ef 100644
--- a/package.json
+++ b/package.json
@@ -18,7 +18,7 @@
     "transformation",
     "syntax"
   ],
-  "version": "0.8.16",
+  "version": "0.9.0",
   "homepage": "http://github.com/benjamn/ast-types",
   "repository": {
     "type": "git",
@@ -34,7 +34,7 @@
     "babel-core": "^5.6.15",
     "esprima": "~1.2.2",
     "esprima-fb": "~14001.1.0-dev-harmony-fb",
-    "mocha": "~2.2.5"
+    "mocha": "~2.5.3"
   },
   "engines": {
     "node": ">= 0.8"
diff --git a/test/run.js b/test/run.js
index a2702ed..8590758 100644
--- a/test/run.js
+++ b/test/run.js
@@ -5,13 +5,13 @@ var b = types.builders;
 var path = require("path");
 var fs = require("fs");
 var parse = require("esprima").parse;
-var Path = require("../lib/path");
-var NodePath = require("../lib/node-path");
-var PathVisitor = require("../lib/path-visitor");
-var builtin = types.builtInTypes
+var Path = types.Path;
+var NodePath = types.NodePath;
+var PathVisitor = types.PathVisitor;
+var builtin = types.builtInTypes;
 var isRegExp = builtin.RegExp;
 var isString = builtin.string;
-var rawTypes = require("../lib/types");
+var rawTypes = types.use(require("../lib/types"));
 
 var hasOwn = Object.prototype.hasOwnProperty;
 
@@ -64,7 +64,7 @@ describe("isSupertypeOf", function() {
 
 describe("supertype lookup", function() {
     it("should resolve the most precise supertypes", function() {
-        var table = require("../lib/types").computeSupertypeLookupTable({
+        var table = types.use(require("../lib/types")).computeSupertypeLookupTable({
             Function: true,
             Declaration: true,
             ArrowFunctionExpression: true,

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-ast-types.git



More information about the Pkg-javascript-commits mailing list