add assertShape to validate templateElement (#10198)

* add assertShape to validate templateElement

* Update packages/babel-types/src/definitions/utils.js

Co-Authored-By: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>

* templateElement optional cooked
This commit is contained in:
Tan Li Hau 2019-07-15 07:03:50 +08:00 committed by Nicolò Ribaudo
parent d3fe22f0e1
commit ee68d6d1b0
6 changed files with 133 additions and 1 deletions

View File

@ -31,6 +31,29 @@ module.exports = function stringifyValidator(validator, nodePrefix) {
return validator.type;
}
if (validator.shapeOf) {
return (
"{ " +
Object.keys(validator.shapeOf)
.map(shapeKey => {
const propertyDefinition = validator.shapeOf[shapeKey];
if (propertyDefinition.validate) {
const isOptional =
propertyDefinition.optional || propertyDefinition.default != null;
return (
shapeKey +
(isOptional ? "?: " : ": ") +
stringifyValidator(propertyDefinition.validate)
);
}
return null;
})
.filter(Boolean)
.join(", ") +
" }"
);
}
return ["any"];
};

View File

@ -1,5 +1,6 @@
// @flow
import defineType, {
assertShape,
assertNodeType,
assertValueType,
chain,
@ -542,7 +543,15 @@ defineType("TemplateElement", {
builder: ["value", "tail"],
fields: {
value: {
// todo: flatten `raw` into main node
validate: assertShape({
raw: {
validate: assertValueType("string"),
},
cooked: {
validate: assertValueType("string"),
optional: true,
},
}),
},
tail: {
validate: assertValueType("boolean"),

View File

@ -1,5 +1,6 @@
// @flow
import is from "../validators/is";
import { validateField } from "../validators/validate";
export const VISITOR_KEYS: { [string]: Array<string> } = {};
export const ALIAS_KEYS: { [string]: Array<string> } = {};
@ -161,6 +162,34 @@ export function assertValueType(type: string): Validator {
return validate;
}
export function assertShape(shape: { [string]: FieldOptions }): Validator {
function validate(node, key, val) {
const errors = [];
for (const property of Object.keys(shape)) {
try {
validateField(node, property, val[property], shape[property]);
} catch (error) {
if (error instanceof TypeError) {
errors.push(error.message);
continue;
}
throw error;
}
}
if (errors.length) {
throw new TypeError(
`Property ${key} of ${
node.type
} expected to have the following:\n${errors.join("\n")}`,
);
}
}
validate.shapeOf = shape;
return validate;
}
export function chain(...fns: Array<Validator>): Validator {
function validate(...args) {
for (const fn of fns) {

View File

@ -8,6 +8,15 @@ export default function validate(node?: Object, key: string, val: any): void {
if (!fields) return;
const field = fields[key];
validateField(node, key, val, field);
}
export function validateField(
node?: Object,
key: string,
val: any,
field: any,
): void {
if (!field || !field.validate) return;
if (field.optional && val == null) return;

View File

@ -0,0 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`builders es2015 templateElement should validate 1`] = `
Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"cooked": "foo",
"raw": "foo",
},
}
`;
exports[`builders es2015 templateElement should validate 2`] = `
Object {
"tail": false,
"type": "TemplateElement",
"value": Object {
"raw": "foo",
},
}
`;
exports[`builders es2015 templateElement should validate 3`] = `
"Property value of TemplateElement expected to have the following:
Property raw expected type of string but got number"
`;
exports[`builders es2015 templateElement should validate 4`] = `
"Property value of TemplateElement expected to have the following:
Property cooked expected type of string but got number"
`;
exports[`builders es2015 templateElement should validate 5`] = `
"Property value of TemplateElement expected to have the following:
Property raw expected type of string but got undefined"
`;

View File

@ -0,0 +1,25 @@
import * as t from "../../..";
describe("builders", function() {
describe("es2015", function() {
describe("templateElement", function() {
it("should validate", function() {
expect(
t.templateElement({ raw: "foo", cooked: "foo" }),
).toMatchSnapshot();
expect(t.templateElement({ raw: "foo" })).toMatchSnapshot();
expect(() =>
t.templateElement({ raw: 1 }),
).toThrowErrorMatchingSnapshot();
expect(() =>
t.templateElement({ raw: "foo", cooked: 1 }),
).toThrowErrorMatchingSnapshot();
expect(() => t.templateElement("foo")).toThrowErrorMatchingSnapshot();
});
});
});
});