Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add assertShape to validate templateElement #10198

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/babel-types/scripts/utils/stringifyValidator.js
Expand Up @@ -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"];
};

Expand Down
11 changes: 10 additions & 1 deletion packages/babel-types/src/definitions/es2015.js
@@ -1,5 +1,6 @@
// @flow
import defineType, {
assertShape,
assertNodeType,
assertValueType,
chain,
Expand Down Expand Up @@ -537,7 +538,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"),
Expand Down
29 changes: 29 additions & 0 deletions packages/babel-types/src/definitions/utils.js
@@ -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> } = {};
Expand Down Expand Up @@ -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) {
Expand Down
9 changes: 9 additions & 0 deletions packages/babel-types/src/validators/validate.js
Expand Up @@ -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;

Expand Down
@@ -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"
`;
25 changes: 25 additions & 0 deletions packages/babel-types/test/builders/es2015/templateElement.js
@@ -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();
});
});
});
});