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

improve node type definitions to avoid any's in generated types #11687

Merged
merged 4 commits into from Jun 8, 2020
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
134 changes: 94 additions & 40 deletions packages/babel-types/src/definitions/core.js
Expand Up @@ -84,10 +84,13 @@ defineType("BinaryExpression", {
const expression = assertNodeType("Expression");
const inOp = assertNodeType("Expression", "PrivateName");

return function (node, key, val) {
const validator = function (node, key, val) {
const validator = node.operator === "in" ? inOp : expression;
validator(node, key, val);
};
// todo(ts): can be discriminated union by `operator` property
validator.oneOfNodeTypes = ["Expression", "PrivateName"];
return validator;
})(),
},
right: {
Expand Down Expand Up @@ -276,6 +279,15 @@ defineType("File", {
program: {
validate: assertNodeType("Program"),
},
comments: {
validate: assertEach(assertNodeType("Comment")),
optional: true,
},
Comment on lines +282 to +285
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just FYI this seems to have broken Jests toMatchInlineSnapshot matcher for tests written in TypeScript (jestjs/jest#10208)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, this is not intended, effect… probably this should be considered a breaking change and so to go to babel 8

tokens: {
// todo(ts): add Token type
validate: assertEach(Object.assign(() => {}, { type: "any" })),
optional: true,
},
},
});

Expand Down Expand Up @@ -457,13 +469,19 @@ defineType("Identifier", {
fields: {
...patternLikeCommon,
name: {
validate: chain(assertValueType("string"), function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

if (!isValidIdentifier(val, false)) {
throw new TypeError(`"${val}" is not a valid identifier name`);
}
}),
validate: chain(
assertValueType("string"),
Object.assign(
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

if (!isValidIdentifier(val, false)) {
throw new TypeError(`"${val}" is not a valid identifier name`);
}
},
{ type: "string" },
),
),
},
optional: {
validate: assertValueType("boolean"),
Expand Down Expand Up @@ -583,14 +601,20 @@ defineType("RegExpLiteral", {
validate: assertValueType("string"),
},
flags: {
validate: chain(assertValueType("string"), function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

const invalid = /[^gimsuy]/.exec(val);
if (invalid) {
throw new TypeError(`"${invalid[0]}" is not a valid RegExp flag`);
}
}),
validate: chain(
assertValueType("string"),
Object.assign(
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

const invalid = /[^gimsuy]/.exec(val);
if (invalid) {
throw new TypeError(`"${invalid[0]}" is not a valid RegExp flag`);
}
},
{ type: "string" },
),
),
default: "",
},
},
Expand Down Expand Up @@ -626,10 +650,13 @@ defineType("MemberExpression", {
const normal = assertNodeType("Identifier", "PrivateName");
const computed = assertNodeType("Expression");

return function (node, key, val) {
const validator = function (node, key, val) {
const validator = node.computed ? computed : normal;
validator(node, key, val);
};
// todo(ts): can be discriminated union by `computed` property
validator.oneOfNodeTypes = ["Expression", "Identifier", "PrivateName"];
return validator;
})(),
},
computed: {
Expand Down Expand Up @@ -719,10 +746,18 @@ defineType("ObjectMethod", {
);
const computed = assertNodeType("Expression");

return function (node, key, val) {
const validator = function (node, key, val) {
const validator = node.computed ? computed : normal;
validator(node, key, val);
};
// todo(ts): can be discriminated union by `computed` property
validator.oneOfNodeTypes = [
"Expression",
"Identifier",
"StringLiteral",
"NumericLiteral",
];
return validator;
})(),
},
decorators: {
Expand Down Expand Up @@ -776,10 +811,18 @@ defineType("ObjectProperty", {
);
const computed = assertNodeType("Expression");

return function (node, key, val) {
const validator = function (node, key, val) {
const validator = node.computed ? computed : normal;
validator(node, key, val);
};
// todo(ts): can be discriminated union by `computed` property
validator.oneOfNodeTypes = [
"Expression",
"Identifier",
"StringLiteral",
"NumericLiteral",
];
return validator;
})(),
},
value: {
Expand All @@ -790,15 +833,18 @@ defineType("ObjectProperty", {
shorthand: {
validate: chain(
assertValueType("boolean"),
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

if (val && node.computed) {
throw new TypeError(
"Property shorthand of ObjectProperty cannot be true if computed is true",
);
}
},
Object.assign(
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

if (val && node.computed) {
throw new TypeError(
"Property shorthand of ObjectProperty cannot be true if computed is true",
);
}
},
{ type: "boolean" },
),
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

Expand Down Expand Up @@ -945,18 +991,26 @@ defineType("TryStatement", {
aliases: ["Statement"],
fields: {
block: {
validate: chain(assertNodeType("BlockStatement"), function (node) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

// This validator isn't put at the top level because we can run it
// even if this node doesn't have a parent.

if (!node.handler && !node.finalizer) {
throw new TypeError(
"TryStatement expects either a handler or finalizer, or both",
);
}
}),
validate: chain(
assertNodeType("BlockStatement"),
Object.assign(
function (node) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

// This validator isn't put at the top level because we can run it
// even if this node doesn't have a parent.

if (!node.handler && !node.finalizer) {
throw new TypeError(
"TryStatement expects either a handler or finalizer, or both",
);
}
},
{
oneOfNodeTypes: ["BlockStatement"],
},
),
),
},
handler: {
optional: true,
Expand Down
144 changes: 104 additions & 40 deletions packages/babel-types/src/definitions/es2015.js
Expand Up @@ -170,13 +170,62 @@ defineType("ClassExpression", {
),
optional: true,
},
mixins: {
validate: assertNodeType("InterfaceExtends"),
optional: true,
},
},
});

defineType("ClassDeclaration", {
inherits: "ClassExpression",
aliases: ["Scopable", "Class", "Statement", "Declaration"],
fields: {
id: {
validate: assertNodeType("Identifier"),
},
typeParameters: {
validate: assertNodeType(
"TypeParameterDeclaration",
"TSTypeParameterDeclaration",
"Noop",
),
optional: true,
},
body: {
validate: assertNodeType("ClassBody"),
},
superClass: {
optional: true,
validate: assertNodeType("Expression"),
},
superTypeParameters: {
validate: assertNodeType(
"TypeParameterInstantiation",
"TSTypeParameterInstantiation",
),
optional: true,
},
implements: {
validate: chain(
assertValueType("array"),
assertEach(
assertNodeType("TSExpressionWithTypeArguments", "ClassImplements"),
),
),
optional: true,
},
decorators: {
validate: chain(
assertValueType("array"),
assertEach(assertNodeType("Decorator")),
),
optional: true,
},
mixins: {
validate: assertNodeType("InterfaceExtends"),
optional: true,
},
declare: {
validate: assertValueType("boolean"),
optional: true,
Expand Down Expand Up @@ -247,18 +296,21 @@ defineType("ExportNamedDeclaration", {
optional: true,
validate: chain(
assertNodeType("Declaration"),
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

// This validator isn't put at the top level because we can run it
// even if this node doesn't have a parent.

if (val && node.specifiers.length) {
throw new TypeError(
"Only declaration or specifiers is allowed on ExportNamedDeclaration",
);
}
},
Object.assign(
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

// This validator isn't put at the top level because we can run it
// even if this node doesn't have a parent.

if (val && node.specifiers.length) {
throw new TypeError(
"Only declaration or specifiers is allowed on ExportNamedDeclaration",
);
}
},
{ oneOfNodeTypes: ["Declaration"] },
),
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

Expand Down Expand Up @@ -433,25 +485,31 @@ defineType("MetaProperty", {
aliases: ["Expression"],
fields: {
meta: {
validate: chain(assertNodeType("Identifier"), function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

let property;
switch (val.name) {
case "function":
property = "sent";
break;
case "new":
property = "target";
break;
case "import":
property = "meta";
break;
}
if (!is("Identifier", node.property, { name: property })) {
throw new TypeError("Unrecognised MetaProperty");
}
}),
validate: chain(
assertNodeType("Identifier"),
Object.assign(
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

let property;
switch (val.name) {
case "function":
property = "sent";
break;
case "new":
property = "target";
break;
case "import":
property = "meta";
break;
}
if (!is("Identifier", node.property, { name: property })) {
throw new TypeError("Unrecognised MetaProperty");
}
},
{ oneOfNodeTypes: ["Identifier"] },
),
),
},
property: {
validate: assertNodeType("Identifier"),
Expand Down Expand Up @@ -665,15 +723,21 @@ defineType("YieldExpression", {
aliases: ["Expression", "Terminatorless"],
fields: {
delegate: {
validate: chain(assertValueType("boolean"), function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

if (val && !node.argument) {
throw new TypeError(
"Property delegate of YieldExpression cannot be true if there is no argument",
);
}
}),
validate: chain(
assertValueType("boolean"),
Object.assign(
function (node, key, val) {
if (!process.env.BABEL_TYPES_8_BREAKING) return;

if (val && !node.argument) {
throw new TypeError(
"Property delegate of YieldExpression cannot be true if there is no argument",
);
}
},
{ type: "boolean" },
),
),
default: false,
},
argument: {
Expand Down