add validation to babel-types - fixes #1858

This commit is contained in:
Sebastian McKenzie 2015-09-01 04:58:11 +01:00
parent ab78685124
commit 59f71a3a09
7 changed files with 225 additions and 82 deletions

View File

@ -24,7 +24,7 @@ export function insertBefore(nodes) {
return this._containerInsertBefore(nodes);
} else if (this.isStatementOrBlock()) {
if (this.node) nodes.push(this.node);
this.node = this.container[this.key] = t.blockStatement(nodes);
this._replaceWith(t.blockStatement(nodes));
} else {
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
}
@ -101,7 +101,7 @@ export function insertAfter(nodes) {
return this._containerInsertAfter(nodes);
} else if (this.isStatementOrBlock()) {
if (this.node) nodes.unshift(this.node);
this.node = this.container[this.key] = t.blockStatement(nodes);
this._replaceWith(t.blockStatement(nodes));
} else {
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
}
@ -115,6 +115,8 @@ export function insertAfter(nodes) {
*/
export function updateSiblingKeys(fromIndex, incrementBy) {
if (!this.parent) return;
var paths = this.parent._paths;
for (var i = 0; i < paths.length; i++) {
let path = paths[i];

View File

@ -40,13 +40,12 @@ export function _callRemovalHooks(position) {
}
}
export function _remove() {
if (Array.isArray(this.container)) {
this.container.splice(this.key, 1);
this.updateSiblingKeys(this.key, -1);
} else {
this.container[this.key] = null;
this._replaceWith(null);
}
}

View File

@ -141,13 +141,27 @@ export function replaceWith(replacement, whateverAllowed) {
if (oldNode) t.inheritsComments(replacement, oldNode);
// replace the node
this.node = this.container[this.key] = replacement;
this._replaceWith(replacement);
this.type = replacement.type;
// potentially create new scope
this.setScope();
}
/**
* Description
*/
export function _replaceWith(node) {
if (this.inList) {
t.validate(this.key, this.parent, [node]);
} else {
t.validate(this.key, this.parent, node);
}
this.node = this.container[this.key] = node;
}
/**
* This method takes an array of statements nodes and then explodes it
* into expressions. This method retains completion records which is

View File

@ -1,11 +1,21 @@
import define from "./index";
import define, { assertValueType, assertNodeType } from "./index";
define("ArrayExpression", {
fields: {
elements: { validate: assertValueType("array") }
},
visitor: ["elements"],
aliases: ["Expression"]
});
define("AssignmentExpression", {
fields: {
elements: {
operator: { validate: assertValueType("string") },
left: { validate: assertNodeType("LVal") },
right: { validate: assertNodeType("Expression") }
}
},
builder: ["operator", "left", "right"],
visitor: ["left", "right"],
aliases: ["Expression"]
@ -13,12 +23,20 @@ define("AssignmentExpression", {
define("BinaryExpression", {
builder: ["operator", "left", "right"],
fields: {
operator: { validate: assertValueType("string") },
left: { validate: assertNodeType("Expression") },
right: { validate: assertNodeType("Expression") }
},
visitor: ["left", "right"],
aliases: ["Binary", "Expression"]
});
define("BlockStatement", {
visitor: ["body"],
fields: {
body: { validate: assertValueType("array") }
},
aliases: ["Scopable", "BlockParent", "Block", "Statement"]
});
@ -29,6 +47,10 @@ define("BreakStatement", {
define("CallExpression", {
visitor: ["callee", "arguments"],
fields: {
callee: { validate: assertNodeType("Expression") },
arguments: { validate: assertValueType("array") }
},
aliases: ["Expression"]
});
@ -39,6 +61,11 @@ define("CatchClause", {
define("ConditionalExpression", {
visitor: ["test", "consequent", "alternate"],
fields: {
test: { validate: assertNodeType("Expression") },
consequent: { validate: assertNodeType("Expression") },
alternate: { validate: assertNodeType("Expression") }
},
aliases: ["Expression"]
});
@ -62,12 +89,18 @@ define("EmptyStatement", {
define("ExpressionStatement", {
visitor: ["expression"],
fields: {
expression: { validate: assertNodeType("Expression") }
},
aliases: ["Statement"]
});
define("File", {
builder: ["program", "comments", "tokens"],
visitor: ["program"]
visitor: ["program"],
fields: {
program: { validate: assertNodeType("Program") }
}
});
define("ForInStatement", {
@ -81,24 +114,37 @@ define("ForStatement", {
});
define("FunctionDeclaration", {
builder: {
id: null,
params: null,
body: null,
generator: false,
async: false
},
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") },
generator: {
default: false,
validate: assertValueType("boolean")
},
async: {
default: false,
validate: assertValueType("boolean")
}
},
aliases: ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Statement", "Pure", "Declaration"]
});
define("FunctionExpression", {
builder: {
id: null,
params: null,
body: null,
generator: false,
async: false
builder: ["id", "params", "body", "generator", "async"],
fields: {
params: { validate: assertValueType("array") },
body: { validate: assertNodeType("BlockStatement") },
generator: {
default: false,
validate: assertValueType("boolean")
},
async: {
default: false,
validate: assertValueType("boolean")
}
},
visitor: ["id", "params", "body", "returnType", "typeParameters"],
aliases: ["Scopable", "Function", "Func", "BlockParent", "FunctionParent", "Expression", "Pure"]
@ -107,7 +153,7 @@ define("FunctionExpression", {
define("Identifier", {
builder: ["name"],
visitor: ["typeAnnotation"],
aliases: ["Expression"]
aliases: ["Expression", "LVal"]
});
define("IfStatement", {
@ -120,9 +166,44 @@ define("LabeledStatement", {
aliases: ["Statement"]
});
define("Literal", {
define("StringLiteral", {
builder: ["value"],
aliases: ["Expression", "Pure"]
fields: {
value: { validate: assertValueType("string") }
},
aliases: ["Expression", "Pure", "Literal", "Immutable"]
});
define("NumberLiteral", {
builder: ["value"],
fields: {
value: { validate: assertValueType("number") }
},
aliases: ["Expression", "Pure", "Literal", "Immutable"]
});
define("NullLiteral", {
aliases: ["Expression", "Pure", "Literal", "Immutable"]
});
define("BooleanLiteral", {
builder: ["value"],
fields: {
value: { validate: assertValueType("boolean") }
},
aliases: ["Expression", "Pure", "Literal", "Immutable"]
});
define("RegexLiteral", {
builder: ["pattern", "flags"],
fields: {
pattern: { validate: assertValueType("string") },
flags: {
validate: assertValueType("string"),
default: ""
}
},
aliases: ["Expression", "Literal"]
});
define("LogicalExpression", {
@ -132,13 +213,12 @@ define("LogicalExpression", {
});
define("MemberExpression", {
builder: {
object: null,
property: null,
computed: false
builder: ["object", "property", "computed"],
fields: {
computed: { default: false }
},
visitor: ["object", "property"],
aliases: ["Expression"]
aliases: ["Expression", "LVal"]
});
define("NewExpression", {
@ -153,15 +233,17 @@ define("ObjectExpression", {
define("Program", {
visitor: ["body"],
fields: {
body: { validate: assertValueType("array") }
},
aliases: ["Scopable", "BlockParent", "Block", "FunctionParent"]
});
define("Property", {
builder: {
kind: "init",
key: null,
value: null,
computed: false
builder: ["kind", "key", "value", "computed"],
fields: {
kind: { default: "init" },
computed: { default: false }
},
visitor: ["key", "value", "decorators"],
aliases: ["UserWhitespacable"]
@ -178,6 +260,9 @@ define("ReturnStatement", {
define("SequenceExpression", {
visitor: ["expressions"],
fields: {
expressions: { validate: assertValueType("array") }
},
aliases: ["Expression"]
});
@ -206,20 +291,18 @@ define("TryStatement", {
});
define("UnaryExpression", {
builder: {
operator: null,
argument: null,
prefix: false
builder: ["operator", "argument", "prefix"],
fields: {
prefix: { default: false }
},
visitor: ["argument"],
aliases: ["UnaryLike", "Expression"]
});
define("UpdateExpression", {
builder: {
operator: null,
argument: null,
prefix: false
builder: ["operator", "argument", "prefix"],
fields: {
prefix: { default: false }
},
visitor: ["argument"],
aliases: ["Expression"]

View File

@ -2,12 +2,12 @@ import define from "./index";
define("AssignmentPattern", {
visitor: ["left", "right"],
aliases: ["Pattern"]
aliases: ["Pattern", "LVal"]
});
define("ArrayPattern", {
visitor: ["elements", "typeAnnotation"],
aliases: ["Pattern"]
aliases: ["Pattern", "LVal"]
});
define("ArrowFunctionExpression", {
@ -91,19 +91,18 @@ define("MetaProperty", {
});
define("MethodDefinition", {
builder: {
key: null,
value: null,
kind: "method",
computed: false,
static: false
builder: ["key", "value", "kind", "computed", "static"],
fields: {
kind: { default: "method" },
computed: { default: false },
static: { default: false }
},
visitor: ["key", "value", "decorators"]
});
define("ObjectPattern", {
visitor: ["properties", "typeAnnotation"],
aliases: ["Pattern"]
aliases: ["Pattern", "LVal"]
});
define("SpreadElement", {
@ -124,7 +123,7 @@ define("TemplateElement");
define("TemplateLiteral", {
visitor: ["quasis", "expressions"],
aliases: ["Expression"]
aliases: ["Expression", "Literal"]
});
define("YieldExpression", {

View File

@ -1,21 +1,67 @@
import * as t from "../index";
export var VISITOR_KEYS = {};
export var ALIAS_KEYS = {};
export var NODE_FIELDS = {};
export var BUILDER_KEYS = {};
function builderFromArray(arr) {
var builder = {};
for (var key of (arr: Array)) builder[key] = null;
return builder;
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)}`);
}
};
}
export function assertNodeType(...types) {
return function (node, key) {
var valid = false;
for (var type of types) {
if (t.is(type, node)) {
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}`);
}
};
}
export function assertValueType(type) {
return function (val, key) {
var valid = typeof val === type;
if (type === "array" && Array.isArray(val)) valid = true;
if (!valid) {
throw new TypeError(`Property ${key} expected type of ${type} but got ${typeof val}`);
}
};
}
export default function define(type, opts = {}) {
opts.fields = opts.fields || {};
opts.visitor = opts.visitor || [];
opts.aliases = opts.aliases || [];
opts.builder = opts.builder || opts.visitor || [];
if (!opts.builder) opts.builder = builderFromArray(opts.visitor);
if (Array.isArray(opts.builder)) opts.builder = builderFromArray(opts.builder);
// ensure all field keys are represented in `fields`
for (let key of (opts.visitor.concat(opts.builder): Array)) {
opts.fields[key] = opts.fields[key] || {};
}
for (let key in opts.fields) {
var field = opts.fields[key];
if (field.default === undefined) {
field.default = null;
}
}
VISITOR_KEYS[type] = opts.visitor;
ALIAS_KEYS[type] = opts.aliases;
BUILDER_KEYS[type] = opts.builder;
NODE_FIELDS[type] = opts.fields;
ALIAS_KEYS[type] = opts.aliases;
}

View File

@ -49,8 +49,8 @@ export const NUMBER_UNARY_OPERATORS = ["+", "-", "++", "--", "~"];
export const STRING_UNARY_OPERATORS = ["typeof"];
import "./definitions/init";
import { VISITOR_KEYS, BUILDER_KEYS, ALIAS_KEYS } from "./definitions";
export { VISITOR_KEYS, BUILDER_KEYS, ALIAS_KEYS };
import { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS } from "./definitions";
export { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS };
export * as react from "./react";
/**
@ -127,44 +127,44 @@ export function isType(nodeType: string, targetType: string): boolean {
return false;
}
/**
* [Please add a description.]
*/
each(t.VISITOR_KEYS, function (keys, type) {
if (t.BUILDER_KEYS[type]) return;
var defs = {};
each(keys, function (key) {
defs[key] = null;
});
t.BUILDER_KEYS[type] = defs;
});
/**
* [Please add a description.]
*/
each(t.BUILDER_KEYS, function (keys, type) {
var builder = function () {
function builder() {
var node = {};
node.type = type;
var i = 0;
for (var key in keys) {
for (var key of (keys: Array)) {
var field = t.NODE_FIELDS[type][key];
var arg = arguments[i++];
if (arg === undefined) arg = keys[key];
if (arg === undefined) arg = field.default;
if (field.validate) field.validate(arg, key);
node[key] = arg;
}
return node;
};
}
t[type] = builder;
t[type[0].toLowerCase() + type.slice(1)] = builder;
});
/**
* Description
*/
export function validate(key, parent, node) {
var fields = t.NODE_FIELDS[parent.type];
if (!fields) return;
var field = fields[key];
if (!field || !field.validate) return;
field.validate(node, key);
}
/**
* Test if an object is shallowly equal.
*/