From cfec910a178d4b93933517f781ad268952920baf Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Tue, 15 Sep 2015 06:26:17 +0100 Subject: [PATCH] add validation to node builders --- packages/babel-types/src/definitions/core.js | 432 +++++++++++++++--- .../babel-types/src/definitions/es2015.js | 243 ++++++++-- .../src/definitions/experimental.js | 79 +++- packages/babel-types/src/definitions/flow.js | 175 +++++-- packages/babel-types/src/definitions/index.js | 54 ++- packages/babel-types/src/definitions/jsx.js | 74 ++- packages/babel-types/src/definitions/misc.js | 9 +- packages/babel-types/src/index.js | 23 +- 8 files changed, 920 insertions(+), 169 deletions(-) diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index ce35a2941a..be0ba1387c 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -1,8 +1,11 @@ -import define, { assertValueType, assertNodeType } from "./index"; +import * as t from "../index"; +import define, { assertValueType, assertNodeType, assertEach, chain, assertOneOf } from "./index"; define("ArrayExpression", { fields: { - elements: { validate: assertValueType("array") } + elements: { + validate: assertValueType("array") + } }, visitor: ["elements"], aliases: ["Expression"] @@ -11,9 +14,15 @@ define("ArrayExpression", { define("AssignmentExpression", { fields: { elements: { - operator: { validate: assertValueType("string") }, - left: { validate: assertNodeType("LVal") }, - right: { validate: assertNodeType("Expression") } + operator: { + validate: assertValueType("string") + }, + left: { + validate: assertNodeType("LVal") + }, + right: { + validate: assertNodeType("Expression") + } } }, builder: ["operator", "left", "right"], @@ -24,53 +33,104 @@ define("AssignmentExpression", { define("BinaryExpression", { builder: ["operator", "left", "right"], fields: { - operator: { validate: assertValueType("string") }, - left: { validate: assertNodeType("Expression") }, - right: { validate: assertNodeType("Expression") } + operator: { + validate: assertValueType("string") + }, + left: { + validate: assertNodeType("Expression") + }, + right: { + validate: assertNodeType("Expression") + } }, visitor: ["left", "right"], aliases: ["Binary", "Expression"] }); -define("BlockStatement", { - visitor: ["body"], +define("Directive", { fields: { - body: { validate: assertValueType("array") } + value: { + validate: assertValueType("string") + } + } +}); + +define("BlockStatement", { + builder: ["body", "directives"], + visitor: ["directives", "body"], + fields: { + directives: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("Directive"))), + default: [] + }, + body: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("Statement"))) + } }, aliases: ["Scopable", "BlockParent", "Block", "Statement"] }); define("BreakStatement", { visitor: ["label"], + fields: { + label: { + validate: assertNodeType("Identifier"), + optional: true + } + }, aliases: ["Statement", "Terminatorless", "CompletionStatement"] }); define("CallExpression", { visitor: ["callee", "arguments"], fields: { - callee: { validate: assertNodeType("Expression") }, - arguments: { validate: assertValueType("array") } + callee: { + validate: assertNodeType("Expression") + }, + arguments: { + validate: assertValueType("array") + } }, aliases: ["Expression"] }); define("CatchClause", { visitor: ["param", "body"], + fields: { + param: { + validate: assertNodeType("Identifier") + }, + body: { + validate: assertNodeType("BlockStatement") + } + }, aliases: ["Scopable"] }); define("ConditionalExpression", { visitor: ["test", "consequent", "alternate"], fields: { - test: { validate: assertNodeType("Expression") }, - consequent: { validate: assertNodeType("Expression") }, - alternate: { validate: assertNodeType("Expression") } + test: { + validate: assertNodeType("Expression") + }, + consequent: { + validate: assertNodeType("Expression") + }, + alternate: { + validate: assertNodeType("Expression") + } }, aliases: ["Expression"] }); define("ContinueStatement", { visitor: ["label"], + fields: { + label: { + validate: assertNodeType("Identifier"), + optional: true + } + }, aliases: ["Statement", "Terminatorless", "CompletionStatement"] }); @@ -79,7 +139,15 @@ define("DebuggerStatement", { }); define("DoWhileStatement", { - visitor: ["body", "test"], + visitor: ["test", "body"], + fields: { + test: { + validate: assertNodeType("Expression") + }, + body: { + validate: assertNodeType("BlockStatement") + } + }, aliases: ["Statement", "BlockParent", "Loop", "While", "Scopable"] }); @@ -90,7 +158,9 @@ define("EmptyStatement", { define("ExpressionStatement", { visitor: ["expression"], fields: { - expression: { validate: assertNodeType("Expression") } + expression: { + validate: assertNodeType("Expression") + } }, aliases: ["Statement"] }); @@ -99,27 +169,60 @@ define("File", { builder: ["program", "comments", "tokens"], visitor: ["program"], fields: { - program: { validate: assertNodeType("Program") } + program: { + validate: assertNodeType("Program") + } } }); define("ForInStatement", { visitor: ["left", "right", "body"], - aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop", "ForXStatement"] + aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop", "ForXStatement"], + fields: { + left: { + validate: assertNodeType("VariableDeclaration", "LVal") + }, + right: { + validate: assertNodeType("Expression") + }, + body: { + validate: assertNodeType("Statement") + } + } }); define("ForStatement", { visitor: ["init", "test", "update", "body"], - aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop"] + aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop"], + fields: { + init: { + validate: assertNodeType("VariableDeclaration", "Expression") + }, + test: { + validate: assertNodeType("Expression") + }, + update: { + validate: assertNodeType("Expression") + }, + body: { + validate: assertNodeType("Statement") + } + } }); define("FunctionDeclaration", { builder: ["id", "params", "body", "generator", "async"], visitor: ["id", "params", "body", "returnType", "typeParameters"], fields: { - id: { validate: assertNodeType("Identifier") }, - params: { validate: assertValueType("array") }, - body: { validate: assertNodeType("BlockStatement") }, + id: { + validate: assertNodeType("Identifier") + }, + params: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("LVal"))) + }, + body: { + validate: assertNodeType("BlockStatement") + }, generator: { default: false, validate: assertValueType("boolean") @@ -129,14 +232,22 @@ define("FunctionDeclaration", { validate: assertValueType("boolean") } }, - aliases: ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Statement", "Pure", "Declaration"] + aliases: ["Scopable", "Function", "BlockParent", "FunctionParent", "Statement", "Pure", "Declaration"] }); define("FunctionExpression", { builder: ["id", "params", "body", "generator", "async"], fields: { - params: { validate: assertValueType("array") }, - body: { validate: assertNodeType("BlockStatement") }, + id: { + validate: assertNodeType("Identifier"), + optional: true + }, + params: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("LVal"))) + }, + body: { + validate: assertNodeType("BlockStatement") + }, generator: { default: false, validate: assertValueType("boolean") @@ -147,29 +258,61 @@ define("FunctionExpression", { } }, visitor: ["id", "params", "body", "returnType", "typeParameters"], - aliases: ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Expression", "Pure"] + aliases: ["Scopable", "Function", "BlockParent", "FunctionParent", "Expression", "Pure"] }); define("Identifier", { builder: ["name"], visitor: ["typeAnnotation"], - aliases: ["Expression", "LVal"] + aliases: ["Expression", "LVal"], + fields: { + name: { + validate(node, key, val) { + if (!t.isValidIdentifier(val)) { + // todo + } + } + } + } }); define("IfStatement", { visitor: ["test", "consequent", "alternate"], - aliases: ["Statement"] + aliases: ["Statement"], + fields: { + test: { + validate: assertNodeType("Expression") + }, + consequent: { + optional: true, + validate: assertNodeType("Statement") + }, + alternate: { + optional: true, + validate: assertNodeType("Statement") + } + } }); define("LabeledStatement", { visitor: ["label", "body"], - aliases: ["Statement"] + aliases: ["Statement"], + fields: { + label: { + validate: assertNodeType("Identifier") + }, + body: { + validate: assertNodeType("Statement") + } + } }); define("StringLiteral", { builder: ["value"], fields: { - value: { validate: assertValueType("string") } + value: { + validate: assertValueType("string") + } }, aliases: ["Expression", "Pure", "Literal", "Immutable"] }); @@ -177,7 +320,9 @@ define("StringLiteral", { define("NumberLiteral", { builder: ["value"], fields: { - value: { validate: assertValueType("number") } + value: { + validate: assertValueType("number") + } }, aliases: ["Expression", "Pure", "Literal", "Immutable"] }); @@ -189,73 +334,161 @@ define("NullLiteral", { define("BooleanLiteral", { builder: ["value"], fields: { - value: { validate: assertValueType("boolean") } + value: { + validate: assertValueType("boolean") + } }, aliases: ["Expression", "Pure", "Literal", "Immutable"] }); define("RegexLiteral", { builder: ["pattern", "flags"], + aliases: ["Expression", "Literal"], fields: { - pattern: { validate: assertValueType("string") }, + pattern: { + validate: assertValueType("string") + }, flags: { validate: assertValueType("string"), default: "" } - }, - aliases: ["Expression", "Literal"] + } }); define("LogicalExpression", { builder: ["operator", "left", "right"], visitor: ["left", "right"], - aliases: ["Binary", "Expression"] + aliases: ["Binary", "Expression"], + fields: { + operator: { + // todo + }, + left: { + validate: assertNodeType("Expression") + }, + right: { + validate: assertNodeType("Expression") + } + } }); define("MemberExpression", { builder: ["object", "property", "computed"], - fields: { - computed: { default: false } - }, visitor: ["object", "property"], - aliases: ["Expression", "LVal"] + aliases: ["Expression", "LVal"], + fields: { + object: { + validate: assertNodeType("Expression") + }, + property: { + validate(node, key, val) { + var expectedType = node.computed ? "Expression" : "Identifier"; + assertNodeType(expectedType)(node, key, val); + } + }, + computed: { + default: false + } + } }); define("NewExpression", { visitor: ["callee", "arguments"], - aliases: ["Expression"] + aliases: ["Expression"], + fields: { + callee: { + validate: assertNodeType("Expression") + }, + arguments: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("Expression"))) + } + } }); define("ObjectExpression", { visitor: ["properties"], - aliases: ["Expression"] + aliases: ["Expression"], + fields: { + properties: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("Property", "SpreadProperty"))) + } + } }); define("Program", { - visitor: ["body"], + visitor: ["directives", "body"], + builder: ["body", "directives"], fields: { - body: { validate: assertValueType("array") } + directives: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("Directive"))), + default: [] + }, + body: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("Statement"))) + } }, aliases: ["Scopable", "BlockParent", "Block", "FunctionParent"] }); define("Property", { - builder: ["kind", "key", "value", "computed"], + builder: ["kind", "key", "value", "computed", "method", "shorthand"], fields: { - kind: { default: "init" }, - computed: { default: false } + kind: { + validate: chain(assertValueType("string"), assertOneOf("init", "get", "set")), + default: "init" + }, + computed: { + validate: assertValueType("boolean"), + default: false + }, + key: { + validate(node, key, val) { + var expectedTypes = node.computed ? "Expression" : ["Identifier", "Literal"]; + assertNodeType(...expectedTypes)(node, key, val); + } + }, + value: { + validate(node, key, val) { + var expectedType = "Expression"; + if (node.kind === "get" || node.kind === "set" || node.method) { + expectedType = "FunctionExpression"; + } + assertNodeType(expectedType)(node, key, val); + } + }, + method: { + validate: assertValueType("boolean"), + default: false + }, + shorthand: { + validate: assertValueType("boolean"), + default: false + }, + decorators: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("Decorator"))) + } }, visitor: ["key", "value", "decorators"], aliases: ["UserWhitespacable"] }); define("RestElement", { - visitor: ["argument", "typeAnnotation"] + visitor: ["argument", "typeAnnotation"], + fields: { + argument: { + validate: assertNodeType("LVal") + } + } }); define("ReturnStatement", { visitor: ["argument"], - aliases: ["Statement", "Terminatorless", "CompletionStatement"] + aliases: ["Statement", "Terminatorless", "CompletionStatement"], + fields: { + argument: { + validate: assertNodeType("Expression") + } + } }); define("SequenceExpression", { @@ -267,12 +500,18 @@ define("SequenceExpression", { }); define("SwitchCase", { - visitor: ["test", "consequent"] + visitor: ["test", "consequent"], + fields: { + // todo + } }); define("SwitchStatement", { visitor: ["discriminant", "cases"], - aliases: ["Statement", "BlockParent", "Scopable"] + aliases: ["Statement", "BlockParent", "Scopable"], + fields: { + // todo + } }); define("ThisExpression", { @@ -281,19 +520,45 @@ define("ThisExpression", { define("ThrowStatement", { visitor: ["argument"], - aliases: ["Statement", "Terminatorless", "CompletionStatement"] + aliases: ["Statement", "Terminatorless", "CompletionStatement"], + fields: { + argument: { + validate: assertNodeType("Expression") + } + } }); +// todo: at least handler or finalizer should be set to be valid define("TryStatement", { - builder: ["block", "handler", "finalizer"], - visitor: ["block", "handlers", "handler", "guardedHandlers", "finalizer"], - aliases: ["Statement"] + visitor: ["block", "handler", "finalizer"], + aliases: ["Statement"], + fields: { + body: { + validate: assertNodeType("BlockStatement") + }, + handler: { + optional: true, + handler: assertNodeType("BlockStatement") + }, + finalizer: { + optional: true, + validate: assertNodeType("BlockStatement") + } + } }); define("UnaryExpression", { builder: ["operator", "argument", "prefix"], fields: { - prefix: { default: false } + prefix: { + default: false + }, + argument: { + validate: assertNodeType("Expression") + }, + operator: { + // todo + } }, visitor: ["argument"], aliases: ["UnaryLike", "Expression"] @@ -302,7 +567,15 @@ define("UnaryExpression", { define("UpdateExpression", { builder: ["operator", "argument", "prefix"], fields: { - prefix: { default: false } + prefix: { + default: false + }, + argument: { + validate: assertNodeType("Expression") + }, + operator: { + // todo + } }, visitor: ["argument"], aliases: ["Expression"] @@ -311,19 +584,52 @@ define("UpdateExpression", { define("VariableDeclaration", { builder: ["kind", "declarations"], visitor: ["declarations"], - aliases: ["Statement", "Declaration"] + aliases: ["Statement", "Declaration"], + fields: { + kind: { + validate: chain(assertValueType("string"), assertOneOf("var", "let", "const")) + }, + declarations: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("VariableDeclarator"))) + } + } }); define("VariableDeclarator", { - visitor: ["id", "init"] + visitor: ["id", "init"], + fields: { + id: { + validate: assertNodeType("LVal") + }, + init: { + optional: true, + validate: assertNodeType("Expression") + } + } }); define("WhileStatement", { visitor: ["test", "body"], - aliases: ["Statement", "BlockParent", "Loop", "While", "Scopable"] + aliases: ["Statement", "BlockParent", "Loop", "While", "Scopable"], + fields: { + test: { + validate: assertNodeType("Expression") + }, + body: { + validate: assertNodeType("BlockStatement", "Statement") + } + } }); define("WithStatement", { visitor: ["object", "body"], - aliases: ["Statement"] + aliases: ["Statement"], + fields: { + object: { + object: assertNodeType("Expression") + }, + body: { + validate: assertNodeType("BlockStatement") + } + } }); diff --git a/packages/babel-types/src/definitions/es2015.js b/packages/babel-types/src/definitions/es2015.js index 7f1d6d238e..09425ecebd 100644 --- a/packages/babel-types/src/definitions/es2015.js +++ b/packages/babel-types/src/definitions/es2015.js @@ -1,113 +1,248 @@ -import define from "./index"; +import define, { assertNodeType, assertValueType, chain, assertEach, assertOneOf } from "./index"; define("AssignmentPattern", { visitor: ["left", "right"], - aliases: ["Pattern", "LVal"] + aliases: ["Pattern", "LVal"], + fields: { + left: { + validate: assertNodeType("Identifier") + }, + right: { + validate: assertNodeType("Expression") + } + } }); define("ArrayPattern", { visitor: ["elements", "typeAnnotation"], - aliases: ["Pattern", "LVal"] + aliases: ["Pattern", "LVal"], + fields: { + elements: { + validate: chain(assertValueType("array"), assertEach(assertValueType("Expression"))) + } + } }); define("ArrowFunctionExpression", { builder: ["params", "body", "async"], visitor: ["params", "body", "returnType"], - aliases: ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Expression", "Pure"] + aliases: ["Scopable", "Function", "BlockParent", "FunctionParent", "Expression", "Pure"], + fields: { + params: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("LVal"))) + }, + body: { + validate: assertNodeType("BlockStatement", "Expression") + }, + async: { + validate: assertValueType("boolean"), + default: false + } + } }); define("ClassBody", { - visitor: ["body"] + visitor: ["body"], + fields: { + body: { + validate: chain(assertValueType("array"), assertEach(assertValueType("MethodDefinition", "ClassProperty"))) + } + } }); define("ClassDeclaration", { + builder: ["id", "superClass", "body", "decorators"], visitor: ["id", "body", "superClass", "typeParameters", "superTypeParameters", "implements", "decorators"], - aliases: ["Scopable", "Class", "Statement", "Declaration"] + aliases: ["Scopable", "Class", "Statement", "Declaration"], + fields: { + id: { + validate: assertNodeType("Identifier") + }, + body: { + validate: assertNodeType("ClassBody") + }, + superClass: { + optional: true, + validate: assertNodeType("Expression") + }, + decorators: { + validate: chain(assertValueType("array"), assertEach(assertValueType("Decorator"))) + } + } }); define("ClassExpression", { + builder: ["id", "superClass", "body", "decorators"], visitor: ["id", "body", "superClass", "typeParameters", "superTypeParameters", "implements", "decorators"], - aliases: ["Scopable", "Class", "Expression"] + aliases: ["Scopable", "Class", "Expression"], + fields: { + id: { + optional: true, + validate: assertNodeType("Identifier") + }, + body: { + validate: assertNodeType("ClassBody") + }, + superClass: { + optional: true, + validate: assertNodeType("Expression") + }, + decorators: { + validate: chain(assertValueType("array"), assertEach(assertValueType("Decorator"))) + } + } }); define("ExportAllDeclaration", { visitor: ["source", "exported"], - aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"] + aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + fields: { + // todo + } }); define("ExportDefaultDeclaration", { visitor: ["declaration"], - aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"] + aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + fields: { + // todo + } }); define("ExportNamedDeclaration", { visitor: ["declaration", "specifiers", "source"], - aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"] -}); - -define("ExportDefaultSpecifier", { - visitor: ["exported"], - aliases: ["ModuleSpecifier"] -}); - -define("ExportNamespaceSpecifier", { - visitor: ["exported"], - aliases: ["ModuleSpecifier"] + aliases: ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + fields: { + // todo + } }); define("ExportSpecifier", { visitor: ["local", "exported"], - aliases: ["ModuleSpecifier"] + aliases: ["ModuleSpecifier"], + fields: { + local: { + validate: assertNodeType("Identifier") + }, + imported: { + validate: assertNodeType("Identifier") + } + } }); define("ForOfStatement", { visitor: ["left", "right", "body"], - aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop", "ForXStatement"] + aliases: ["Scopable", "Statement", "For", "BlockParent", "Loop", "ForXStatement"], + fields: { + left: { + validate: assertNodeType("VariableDeclaration", "LVal") + }, + right: { + validate: assertNodeType("Expression") + }, + body: { + validate: assertNodeType("Statement") + } + } }); define("ImportDeclaration", { visitor: ["specifiers", "source"], - aliases: ["Statement", "Declaration", "ModuleDeclaration"] + aliases: ["Statement", "Declaration", "ModuleDeclaration"], + fields: { + specifiers: { + // todo + }, + source: { + validate: assertNodeType("StringLiteral") + } + } }); define("ImportDefaultSpecifier", { visitor: ["local"], - aliases: ["ModuleSpecifier"] + aliases: ["ModuleSpecifier"], + fields: { + local: { + validate: assertNodeType("Identifier") + } + } }); define("ImportNamespaceSpecifier", { visitor: ["local"], - aliases: ["ModuleSpecifier"] + aliases: ["ModuleSpecifier"], + fields: { + local: { + validate: assertNodeType("Identifier") + } + } }); define("ImportSpecifier", { visitor: ["local", "imported"], - aliases: ["ModuleSpecifier"] + aliases: ["ModuleSpecifier"], + fields: { + local: { + validate: assertNodeType("Identifier") + }, + imported: { + validate: assertNodeType("Identifier") + } + } }); define("MetaProperty", { visitor: ["meta", "property"], - aliases: ["Expression"] + aliases: ["Expression"], + fields: { + // todo: limit to new.target + meta: { + validate: assertValueType("string") + }, + property: { + validate: assertValueType("string") + } + } }); define("MethodDefinition", { builder: ["key", "value", "kind", "computed", "static"], + visitor: ["key", "value", "decorators"], fields: { - kind: { default: "method" }, - computed: { default: false }, - static: { default: false } - }, - visitor: ["key", "value", "decorators"] + kind: { + validate: chain(assertValueType("string"), assertOneOf("get", "set", "method", "constructor")), + default: "method" + }, + computed: { + default: false, + validate: assertValueType("boolean") + }, + static: { + default: false, + validate: assertValueType("boolean") + } + } }); define("ObjectPattern", { visitor: ["properties", "typeAnnotation"], - aliases: ["Pattern", "LVal"] + aliases: ["Pattern", "LVal"], + fields: { + properties: { + validate: chain(assertValueType("array"), assertEach(assertValueType("RestProperty", "Property"))) + } + } }); define("SpreadElement", { visitor: ["argument"], - aliases: ["UnaryLike"] + aliases: ["UnaryLike"], + fields: { + argument: { + validate: assertNodeType("Expression") + } + } }); define("Super", { @@ -116,18 +251,50 @@ define("Super", { define("TaggedTemplateExpression", { visitor: ["tag", "quasi"], - aliases: ["Expression"] + aliases: ["Expression"], + fields: { + tag: { + validate: assertNodeType("Expression") + }, + quasi: { + validate: assertNodeType("TemplateLiteral") + } + } }); -define("TemplateElement"); +define("TemplateElement", { + builder: ["value", "tail"], + fields: { + value: { + // todo: flatten `raw` into main node + }, + tail: { + validate: assertValueType("boolean"), + default: false + } + } +}); define("TemplateLiteral", { visitor: ["quasis", "expressions"], - aliases: ["Expression", "Literal"] + aliases: ["Expression", "Literal"], + fields: { + // todo + } }); define("YieldExpression", { builder: ["argument", "delegate"], visitor: ["argument"], - aliases: ["Expression", "Terminatorless"] + aliases: ["Expression", "Terminatorless"], + fields: { + delegate: { + validate: assertValueType("boolean"), + default: false + }, + argument: { + optional: true, + validate: assertNodeType("Expression"), + } + } }); diff --git a/packages/babel-types/src/definitions/experimental.js b/packages/babel-types/src/definitions/experimental.js index 6af58866b9..1e0d171782 100644 --- a/packages/babel-types/src/definitions/experimental.js +++ b/packages/babel-types/src/definitions/experimental.js @@ -1,34 +1,97 @@ -import define from "./index"; +import define, { assertNodeType, assertValueType } from "./index"; define("AwaitExpression", { builder: ["argument", "all"], visitor: ["argument"], - aliases: ["Expression", "Terminatorless"] + aliases: ["Expression", "Terminatorless"], + fields: { + all: { + validate: assertValueType("boolean"), + default: false + }, + argument: { + validate: assertNodeType("Expression"), + } + } }); define("BindExpression", { - visitor: ["object", "callee"] + visitor: ["object", "callee"], + fields: { + // todo + } }); define("ComprehensionBlock", { - visitor: ["left", "right"] + visitor: ["left", "right"], + fields: { + // todo + } }); define("ComprehensionExpression", { visitor: ["filter", "blocks", "body"], - aliases: ["Expression", "Scopable"] + aliases: ["Expression", "Scopable"], + fields: { + // todo + } }); define("Decorator", { - visitor: ["expression"] + visitor: ["expression"], + fields: { + expression: { + validate: assertNodeType("Expression") + } + } }); define("DoExpression", { visitor: ["body"], - aliases: ["Expression"] + aliases: ["Expression"], + fields: { + body: { + validate: assertNodeType("BlockStatement") + } + } +}); + +define("ExportDefaultSpecifier", { + visitor: ["exported"], + aliases: ["ModuleSpecifier"], + fields: { + exported: { + validate: assertNodeType("Identifier") + } + } +}); + +define("ExportNamespaceSpecifier", { + visitor: ["exported"], + aliases: ["ModuleSpecifier"], + fields: { + exported: { + validate: assertNodeType("Identifier") + } + } +}); + +define("RestProperty", { + visitor: ["argument"], + aliases: ["UnaryLike"], + fields: { + argument: { + validate: assertNodeType("LVal") + } + } }); define("SpreadProperty", { visitor: ["argument"], - aliases: ["UnaryLike"] + aliases: ["UnaryLike"], + fields: { + argument: { + validate: assertNodeType("Expression") + } + } }); diff --git a/packages/babel-types/src/definitions/flow.js b/packages/babel-types/src/definitions/flow.js index 2cfcc3e3e1..f5fe07c9d6 100644 --- a/packages/babel-types/src/definitions/flow.js +++ b/packages/babel-types/src/definitions/flow.js @@ -1,80 +1,128 @@ import define from "./index"; define("AnyTypeAnnotation", { - aliases: ["Flow", "FlowBaseAnnotation"] + aliases: ["Flow", "FlowBaseAnnotation"], + fields: { + // todo + } }); define("ArrayTypeAnnotation", { visitor: ["elementType"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("BooleanTypeAnnotation", { - aliases: ["Flow", "FlowBaseAnnotation"] + aliases: ["Flow", "FlowBaseAnnotation"], + fields: { + // todo + } }); define("BooleanLiteralTypeAnnotation", { - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("ClassImplements", { visitor: ["id", "typeParameters"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("ClassProperty", { visitor: ["key", "value", "typeAnnotation", "decorators"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("DeclareClass", { visitor: ["id", "typeParameters", "extends", "body"], - aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"] + aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"], + fields: { + // todo + } }); define("DeclareFunction", { visitor: ["id"], - aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"] + aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"], + fields: { + // todo + } }); define("DeclareModule", { visitor: ["id", "body"], - aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"] + aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"], + fields: { + // todo + } }); define("DeclareVariable", { visitor: ["id"], - aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"] + aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"], + fields: { + // todo + } }); define("FunctionTypeAnnotation", { visitor: ["typeParameters", "params", "rest", "returnType"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("FunctionTypeParam", { visitor: ["name", "typeAnnotation"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("GenericTypeAnnotation", { visitor: ["id", "typeParameters"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("InterfaceExtends", { visitor: ["id", "typeParameters"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("InterfaceDeclaration", { visitor: ["id", "typeParameters", "extends", "body"], - aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"] + aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"], + fields: { + // todo + } }); define("IntersectionTypeAnnotation", { visitor: ["types"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("MixedTypeAnnotation", { @@ -83,90 +131,147 @@ define("MixedTypeAnnotation", { define("NullableTypeAnnotation", { visitor: ["typeAnnotation"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("NumberLiteralTypeAnnotation", { - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("NumberTypeAnnotation", { - aliases: ["Flow", "FlowBaseAnnotation"] + aliases: ["Flow", "FlowBaseAnnotation"], + fields: { + // todo + } }); define("StringLiteralTypeAnnotation", { - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("StringTypeAnnotation", { - aliases: ["Flow", "FlowBaseAnnotation"] + aliases: ["Flow", "FlowBaseAnnotation"], + fields: { + // todo + } }); define("TupleTypeAnnotation", { visitor: ["types"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("TypeofTypeAnnotation", { visitor: ["argument"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("TypeAlias", { visitor: ["id", "typeParameters", "right"], - aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"] + aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"], + fields: { + // todo + } }); define("TypeAnnotation", { visitor: ["typeAnnotation"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("TypeCastExpression", { visitor: ["expression", "typeAnnotation"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("TypeParameterDeclaration", { visitor: ["params"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("TypeParameterInstantiation", { visitor: ["params"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("ObjectTypeAnnotation", { visitor: ["properties", "indexers", "callProperties"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("ObjectTypeCallProperty", { visitor: ["value"], - aliases: ["Flow", "UserWhitespacable"] + aliases: ["Flow", "UserWhitespacable"], + fields: { + // todo + } }); define("ObjectTypeIndexer", { visitor: ["id", "key", "value"], - aliases: ["Flow", "UserWhitespacable"] + aliases: ["Flow", "UserWhitespacable"], + fields: { + // todo + } }); define("ObjectTypeProperty", { visitor: ["key", "value"], - aliases: ["Flow", "UserWhitespacable"] + aliases: ["Flow", "UserWhitespacable"], + fields: { + // todo + } }); define("QualifiedTypeIdentifier", { visitor: ["id", "qualification"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("UnionTypeAnnotation", { visitor: ["types"], - aliases: ["Flow"] + aliases: ["Flow"], + fields: { + // todo + } }); define("VoidTypeAnnotation", { - aliases: ["Flow", "FlowBaseAnnotation"] + aliases: ["Flow", "FlowBaseAnnotation"], + fields: { + // todo + } }); diff --git a/packages/babel-types/src/definitions/index.js b/packages/babel-types/src/definitions/index.js index b384f7e1be..e9199969f8 100644 --- a/packages/babel-types/src/definitions/index.js +++ b/packages/babel-types/src/definitions/index.js @@ -5,38 +5,76 @@ export var ALIAS_KEYS = {}; export var NODE_FIELDS = {}; export var BUILDER_KEYS = {}; +function getType(val) { + if (Array.isArray(val)) { + return "array"; + } else if (val === null) { + return "null"; + } else if (val === undefined) { + return "undefined"; + } else { + return typeof val; + } +} + export function assertContains(vals) { return function (val, key) { if (vals.indexOf(val) < 0) { - throw new TypeError(`Property ${key} with the value of ${val} expected to be one of ${JSON.stringify(vals)}`); + throw new TypeError(`Property ${key} with the value of ${JSON.stringify(val)} expected to be one of ${JSON.stringify(vals)}`); + } + }; +} + +export function assertEach(callback) { + return function (node, key, val) { + if (!Array.isArray(val)) return; + + for (var i = 0; i < val.length; i++) { + callback(node, `${key}[${i}]`, val[i]); + } + }; +} + +export function assertOneOf(...vals) { + return function (node, key, val) { + if (vals.indexOf(val) < 0) { + throw new TypeError(`Property ${key} expected value to be one of ${JSON.stringify(vals)} but got ${JSON.stringify(val)}`); } }; } export function assertNodeType(...types) { - return function (node, key) { + return function (node, key, val) { var valid = false; for (var type of types) { - if (t.is(type, node)) { + if (t.is(type, val)) { valid = true; break; } } if (!valid) { - throw new TypeError(`Property ${key} expected node to be of a type ${JSON.stringify(types)} but instead got ${node && node.type}`); + throw new TypeError(`Property ${key} expected node to be of a type ${JSON.stringify(types)} but instead got ${JSON.stringify(val && val.type)}`); } }; } export function assertValueType(type) { - return function (val, key) { - var valid = typeof val === type; - if (type === "array" && Array.isArray(val)) valid = true; + return function (node, key, val) { + var valid = getType(val) === type; if (!valid) { - throw new TypeError(`Property ${key} expected type of ${type} but got ${typeof val}`); + console.log(type, key, val); + throw new TypeError(`Property ${key} expected type of ${type} but got ${getType(val)}`); + } + }; +} + +export function chain(...fns) { + return function (...args) { + for (var fn of fns) { + fn(...args); } }; } diff --git a/packages/babel-types/src/definitions/jsx.js b/packages/babel-types/src/definitions/jsx.js index 8f2acabac0..2da69891c8 100644 --- a/packages/babel-types/src/definitions/jsx.js +++ b/packages/babel-types/src/definitions/jsx.js @@ -1,13 +1,27 @@ -import define, { assertValueType } from "./index"; +import define, { assertNodeType, assertValueType, chain, assertEach } from "./index"; define("JSXAttribute", { visitor: ["name", "value"], - aliases: ["JSX", "Immutable"] + aliases: ["JSX", "Immutable"], + fields: { + name: { + validate: assertNodeType("JSXIdentifier", "JSXMemberExpression") + }, + value: { + optional: true, + validate: assertNodeType("JSXElement", "StringLiteral", "JSXExpressionContainer") + } + } }); define("JSXClosingElement", { visitor: ["name"], - aliases: ["JSX", "Immutable"] + aliases: ["JSX", "Immutable"], + fields: { + name: { + validate: assertNodeType("JSXIdentifier", "JSXMemberExpression") + } + } }); define("JSXElement", { @@ -34,37 +48,79 @@ define("JSXEmptyExpression", { define("JSXExpressionContainer", { visitor: ["expression"], - aliases: ["JSX", "Immutable"] + aliases: ["JSX", "Immutable"], + fields: { + expression: { + validate: assertNodeType("Expression") + } + } }); define("JSXIdentifier", { + builder: ["name"], aliases: ["JSX", "Expression"] }); define("JSXMemberExpression", { visitor: ["object", "property"], - aliases: ["JSX", "Expression"] + aliases: ["JSX", "Expression"], + fields: { + object: { + validate: assertNodeType("JSXIdentifier") + }, + property: { + validate: assertNodeType("JSXIdentifier") + } + } }); define("JSXNamespacedName", { visitor: ["namespace", "name"], - aliases: ["JSX"] + aliases: ["JSX"], + fields: { + namespace: { + validate: assertNodeType("JSXIdentifier") + }, + name: { + validate: assertNodeType("JSXIdentifier") + } + } }); define("JSXOpeningElement", { + builder: ["name", "attributes", "selfClosing"], visitor: ["name", "attributes"], - aliases: ["JSX", "Immutable"] + aliases: ["JSX", "Immutable"], + fields: { + name: { + validate: assertNodeType("JSXIdentifier", "JSXMemberExpression") + }, + selfClosing: { + default: false, + validate: assertValueType("boolean") + }, + attributes: { + validate: chain(assertValueType("array"), assertEach(assertNodeType("JSXAttribute", "JSXSpreadAttribute"))) + } + } }); define("JSXSpreadAttribute", { visitor: ["argument"], - aliases: ["JSX"] + aliases: ["JSX"], + fields: { + argument: { + validate: assertNodeType("Expression") + } + } }); define("JSXText", { aliases: ["JSX"], builder: ["value"], fields: { - value: { validate: assertValueType("string") } + value: { + validate: assertValueType("string") + } } }); diff --git a/packages/babel-types/src/definitions/misc.js b/packages/babel-types/src/definitions/misc.js index 28730499f1..744beacc2d 100644 --- a/packages/babel-types/src/definitions/misc.js +++ b/packages/babel-types/src/definitions/misc.js @@ -1,4 +1,4 @@ -import define from "./index"; +import define, { assertNodeType } from "./index"; define("Noop", { visitor: [] @@ -6,5 +6,10 @@ define("Noop", { define("ParenthesizedExpression", { visitor: ["expression"], - aliases: ["Expression"] + aliases: ["Expression"], + fields: { + expression: { + validate: assertNodeType("expression") + } + } }); diff --git a/packages/babel-types/src/index.js b/packages/babel-types/src/index.js index 8e9644134a..31d53df153 100644 --- a/packages/babel-types/src/index.js +++ b/packages/babel-types/src/index.js @@ -1,6 +1,7 @@ import toFastProperties from "to-fast-properties"; import compact from "lodash/array/compact"; import assign from "lodash/object/assign"; +import loClone from "lodash/lang/clone"; import each from "lodash/collection/each"; import uniq from "lodash/array/uniq"; @@ -129,21 +130,28 @@ export function isType(nodeType: string, targetType: string): boolean { each(t.BUILDER_KEYS, function (keys, type) { function builder() { + if (arguments.length > keys.length) { + // todo: error + } + var node = {}; node.type = type; var i = 0; - for (var key of (keys: Array)) { + for (let key of (keys: Array)) { var field = t.NODE_FIELDS[type][key]; var arg = arguments[i++]; - if (arg === undefined) arg = field.default; - if (field.validate) field.validate(arg, key); + if (arg === undefined) arg = loClone(field.default); node[key] = arg; } + for (let key in node) { + validate(node, key, node[key]); + } + return node; } @@ -155,14 +163,17 @@ each(t.BUILDER_KEYS, function (keys, type) { * Description */ -export function validate(key, parent, node) { - var fields = t.NODE_FIELDS[parent.type]; +export function validate(node, key, val) { + if (!node) return; + + var fields = t.NODE_FIELDS[node.type]; if (!fields) return; var field = fields[key]; if (!field || !field.validate) return; + if (field.optional && val == null) return; - field.validate(node, key); + field.validate(node, key, val); } /**