Add placeholders support to @babel/types and @babel/generator (#9542)
This commit is contained in:
parent
095e5c0e09
commit
15dfce33df
@ -82,3 +82,13 @@ export function DirectiveLiteral(node: Object) {
|
|||||||
export function InterpreterDirective(node: Object) {
|
export function InterpreterDirective(node: Object) {
|
||||||
this.token(`#!${node.value}\n`);
|
this.token(`#!${node.value}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Placeholder(node: Object) {
|
||||||
|
this.token("%%");
|
||||||
|
this.print(node.name);
|
||||||
|
this.token("%%");
|
||||||
|
|
||||||
|
if (node.expectedNode === "Statement") {
|
||||||
|
this.semicolon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
8
packages/babel-generator/test/fixtures/misc/placeholders/input.js
vendored
Normal file
8
packages/babel-generator/test/fixtures/misc/placeholders/input.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
var %%a%% = %%b%%
|
||||||
|
|
||||||
|
%%c%%
|
||||||
|
|
||||||
|
class %%d%% {}
|
||||||
|
class A %%e%%
|
||||||
|
|
||||||
|
function %%f%%(...%%g%%) %%h%%
|
||||||
3
packages/babel-generator/test/fixtures/misc/placeholders/options.json
vendored
Normal file
3
packages/babel-generator/test/fixtures/misc/placeholders/options.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["placeholders"]
|
||||||
|
}
|
||||||
8
packages/babel-generator/test/fixtures/misc/placeholders/output.js
vendored
Normal file
8
packages/babel-generator/test/fixtures/misc/placeholders/output.js
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
var %%a%% = %%b%%;
|
||||||
|
%%c%%;
|
||||||
|
|
||||||
|
class %%d%% {}
|
||||||
|
|
||||||
|
class A %%e%%
|
||||||
|
|
||||||
|
function %%f%%(...%%g%%) %%h%%
|
||||||
@ -1,14 +1,37 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
const definitions = require("../../lib/definitions");
|
const definitions = require("../../lib/definitions");
|
||||||
|
|
||||||
|
const has = Function.call.bind(Object.prototype.hasOwnProperty);
|
||||||
|
|
||||||
|
function joinComparisons(leftArr, right) {
|
||||||
|
return (
|
||||||
|
leftArr.map(JSON.stringify).join(` === ${right} || `) + ` === ${right}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function addIsHelper(type, aliasKeys, deprecated) {
|
function addIsHelper(type, aliasKeys, deprecated) {
|
||||||
const targetType = JSON.stringify(type);
|
const targetType = JSON.stringify(type);
|
||||||
let aliasSource = "";
|
let aliasSource = "";
|
||||||
if (aliasKeys) {
|
if (aliasKeys) {
|
||||||
aliasSource =
|
aliasSource = " || " + joinComparisons(aliasKeys, "nodeType");
|
||||||
" || " +
|
}
|
||||||
aliasKeys.map(JSON.stringify).join(" === nodeType || ") +
|
|
||||||
" === nodeType";
|
let placeholderSource = "";
|
||||||
|
const placeholderTypes = [];
|
||||||
|
if (
|
||||||
|
definitions.PLACEHOLDERS.includes(type) &&
|
||||||
|
has(definitions.FLIPPED_ALIAS_KEYS, type)
|
||||||
|
) {
|
||||||
|
placeholderTypes.push(type);
|
||||||
|
}
|
||||||
|
if (has(definitions.PLACEHOLDERS_FLIPPED_ALIAS, type)) {
|
||||||
|
placeholderTypes.push(...definitions.PLACEHOLDERS_FLIPPED_ALIAS[type]);
|
||||||
|
}
|
||||||
|
if (placeholderTypes.length > 0) {
|
||||||
|
placeholderSource =
|
||||||
|
' || nodeType === "Placeholder" && (' +
|
||||||
|
joinComparisons(placeholderTypes, "node.expectedNode") +
|
||||||
|
")";
|
||||||
}
|
}
|
||||||
|
|
||||||
return `export function is${type}(node: ?Object, opts?: Object): boolean {
|
return `export function is${type}(node: ?Object, opts?: Object): boolean {
|
||||||
@ -16,7 +39,7 @@ function addIsHelper(type, aliasKeys, deprecated) {
|
|||||||
if (!node) return false;
|
if (!node) return false;
|
||||||
|
|
||||||
const nodeType = node.type;
|
const nodeType = node.type;
|
||||||
if (nodeType === ${targetType}${aliasSource}) {
|
if (nodeType === ${targetType}${aliasSource}${placeholderSource}) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -660,6 +660,9 @@ export function assertJSXClosingFragment(
|
|||||||
export function assertNoop(node: Object, opts?: Object = {}): void {
|
export function assertNoop(node: Object, opts?: Object = {}): void {
|
||||||
assert("Noop", node, opts);
|
assert("Noop", node, opts);
|
||||||
}
|
}
|
||||||
|
export function assertPlaceholder(node: Object, opts?: Object = {}): void {
|
||||||
|
assert("Placeholder", node, opts);
|
||||||
|
}
|
||||||
export function assertArgumentPlaceholder(
|
export function assertArgumentPlaceholder(
|
||||||
node: Object,
|
node: Object,
|
||||||
opts?: Object = {},
|
opts?: Object = {},
|
||||||
|
|||||||
@ -596,6 +596,10 @@ export function Noop(...args: Array<any>): Object {
|
|||||||
return builder("Noop", ...args);
|
return builder("Noop", ...args);
|
||||||
}
|
}
|
||||||
export { Noop as noop };
|
export { Noop as noop };
|
||||||
|
export function Placeholder(...args: Array<any>): Object {
|
||||||
|
return builder("Placeholder", ...args);
|
||||||
|
}
|
||||||
|
export { Placeholder as placeholder };
|
||||||
export function ArgumentPlaceholder(...args: Array<any>): Object {
|
export function ArgumentPlaceholder(...args: Array<any>): Object {
|
||||||
return builder("ArgumentPlaceholder", ...args);
|
return builder("ArgumentPlaceholder", ...args);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,11 @@ import {
|
|||||||
BUILDER_KEYS,
|
BUILDER_KEYS,
|
||||||
DEPRECATED_KEYS,
|
DEPRECATED_KEYS,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
|
import {
|
||||||
|
PLACEHOLDERS,
|
||||||
|
PLACEHOLDERS_ALIAS,
|
||||||
|
PLACEHOLDERS_FLIPPED_ALIAS,
|
||||||
|
} from "./placeholders";
|
||||||
|
|
||||||
// We do this here, because at this point the visitor keys should be ready and setup
|
// We do this here, because at this point the visitor keys should be ready and setup
|
||||||
toFastProperties(VISITOR_KEYS);
|
toFastProperties(VISITOR_KEYS);
|
||||||
@ -24,6 +29,9 @@ toFastProperties(NODE_FIELDS);
|
|||||||
toFastProperties(BUILDER_KEYS);
|
toFastProperties(BUILDER_KEYS);
|
||||||
toFastProperties(DEPRECATED_KEYS);
|
toFastProperties(DEPRECATED_KEYS);
|
||||||
|
|
||||||
|
toFastProperties(PLACEHOLDERS_ALIAS);
|
||||||
|
toFastProperties(PLACEHOLDERS_FLIPPED_ALIAS);
|
||||||
|
|
||||||
const TYPES: Array<string> = Object.keys(VISITOR_KEYS)
|
const TYPES: Array<string> = Object.keys(VISITOR_KEYS)
|
||||||
.concat(Object.keys(FLIPPED_ALIAS_KEYS))
|
.concat(Object.keys(FLIPPED_ALIAS_KEYS))
|
||||||
.concat(Object.keys(DEPRECATED_KEYS));
|
.concat(Object.keys(DEPRECATED_KEYS));
|
||||||
@ -35,5 +43,8 @@ export {
|
|||||||
NODE_FIELDS,
|
NODE_FIELDS,
|
||||||
BUILDER_KEYS,
|
BUILDER_KEYS,
|
||||||
DEPRECATED_KEYS,
|
DEPRECATED_KEYS,
|
||||||
|
PLACEHOLDERS,
|
||||||
|
PLACEHOLDERS_ALIAS,
|
||||||
|
PLACEHOLDERS_FLIPPED_ALIAS,
|
||||||
TYPES,
|
TYPES,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,21 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import defineType from "./utils";
|
import defineType, { assertNodeType, assertOneOf } from "./utils";
|
||||||
|
import { PLACEHOLDERS } from "./placeholders";
|
||||||
|
|
||||||
defineType("Noop", {
|
defineType("Noop", {
|
||||||
visitor: [],
|
visitor: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defineType("Placeholder", {
|
||||||
|
visitor: [],
|
||||||
|
builder: ["expectedNode", "name"],
|
||||||
|
// aliases: [], defined in placeholders.js
|
||||||
|
fields: {
|
||||||
|
name: {
|
||||||
|
validate: assertNodeType("Identifier"),
|
||||||
|
},
|
||||||
|
expectedNode: {
|
||||||
|
validate: assertOneOf(...PLACEHOLDERS),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
33
packages/babel-types/src/definitions/placeholders.js
Normal file
33
packages/babel-types/src/definitions/placeholders.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { ALIAS_KEYS } from "./utils";
|
||||||
|
|
||||||
|
export const PLACEHOLDERS = [
|
||||||
|
"Identifier",
|
||||||
|
"StringLiteral",
|
||||||
|
"Expression",
|
||||||
|
"Statement",
|
||||||
|
"Declaration",
|
||||||
|
"BlockStatement",
|
||||||
|
"ClassBody",
|
||||||
|
"Pattern",
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PLACEHOLDERS_ALIAS: { [string]: Array<string> } = {
|
||||||
|
Declaration: ["Statement"],
|
||||||
|
Pattern: ["PatternLike", "LVal"],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const type of PLACEHOLDERS) {
|
||||||
|
const alias = ALIAS_KEYS[type];
|
||||||
|
if (alias && alias.length) PLACEHOLDERS_ALIAS[type] = alias;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PLACEHOLDERS_FLIPPED_ALIAS: { [string]: Array<string> } = {};
|
||||||
|
|
||||||
|
Object.keys(PLACEHOLDERS_ALIAS).forEach(type => {
|
||||||
|
PLACEHOLDERS_ALIAS[type].forEach(alias => {
|
||||||
|
if (!Object.hasOwnProperty.call(PLACEHOLDERS_FLIPPED_ALIAS, alias)) {
|
||||||
|
PLACEHOLDERS_FLIPPED_ALIAS[alias] = [];
|
||||||
|
}
|
||||||
|
PLACEHOLDERS_FLIPPED_ALIAS[alias].push(type);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -100,6 +100,7 @@ export { default as isImmutable } from "./validators/isImmutable";
|
|||||||
export { default as isLet } from "./validators/isLet";
|
export { default as isLet } from "./validators/isLet";
|
||||||
export { default as isNode } from "./validators/isNode";
|
export { default as isNode } from "./validators/isNode";
|
||||||
export { default as isNodesEquivalent } from "./validators/isNodesEquivalent";
|
export { default as isNodesEquivalent } from "./validators/isNodesEquivalent";
|
||||||
|
export { default as isPlaceholderType } from "./validators/isPlaceholderType";
|
||||||
export { default as isReferenced } from "./validators/isReferenced";
|
export { default as isReferenced } from "./validators/isReferenced";
|
||||||
export { default as isScope } from "./validators/isScope";
|
export { default as isScope } from "./validators/isScope";
|
||||||
export { default as isSpecifierDefault } from "./validators/isSpecifierDefault";
|
export { default as isSpecifierDefault } from "./validators/isSpecifierDefault";
|
||||||
|
|||||||
@ -2093,6 +2093,20 @@ export function isNoop(node: ?Object, opts?: Object): boolean {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
export function isPlaceholder(node: ?Object, opts?: Object): boolean {
|
||||||
|
if (!node) return false;
|
||||||
|
|
||||||
|
const nodeType = node.type;
|
||||||
|
if (nodeType === "Placeholder") {
|
||||||
|
if (typeof opts === "undefined") {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return shallowEqual(node, opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
export function isArgumentPlaceholder(node: ?Object, opts?: Object): boolean {
|
export function isArgumentPlaceholder(node: ?Object, opts?: Object): boolean {
|
||||||
if (!node) return false;
|
if (!node) return false;
|
||||||
|
|
||||||
@ -3280,7 +3294,11 @@ export function isExpression(node: ?Object, opts?: Object): boolean {
|
|||||||
"BigIntLiteral" === nodeType ||
|
"BigIntLiteral" === nodeType ||
|
||||||
"TSAsExpression" === nodeType ||
|
"TSAsExpression" === nodeType ||
|
||||||
"TSTypeAssertion" === nodeType ||
|
"TSTypeAssertion" === nodeType ||
|
||||||
"TSNonNullExpression" === nodeType
|
"TSNonNullExpression" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" &&
|
||||||
|
("Expression" === node.expectedNode ||
|
||||||
|
"Identifier" === node.expectedNode ||
|
||||||
|
"StringLiteral" === node.expectedNode))
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3331,7 +3349,8 @@ export function isScopable(node: ?Object, opts?: Object): boolean {
|
|||||||
"ClassExpression" === nodeType ||
|
"ClassExpression" === nodeType ||
|
||||||
"ForOfStatement" === nodeType ||
|
"ForOfStatement" === nodeType ||
|
||||||
"ClassMethod" === nodeType ||
|
"ClassMethod" === nodeType ||
|
||||||
"ClassPrivateMethod" === nodeType
|
"ClassPrivateMethod" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "BlockStatement" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3362,7 +3381,8 @@ export function isBlockParent(node: ?Object, opts?: Object): boolean {
|
|||||||
"ArrowFunctionExpression" === nodeType ||
|
"ArrowFunctionExpression" === nodeType ||
|
||||||
"ForOfStatement" === nodeType ||
|
"ForOfStatement" === nodeType ||
|
||||||
"ClassMethod" === nodeType ||
|
"ClassMethod" === nodeType ||
|
||||||
"ClassPrivateMethod" === nodeType
|
"ClassPrivateMethod" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "BlockStatement" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3380,7 +3400,8 @@ export function isBlock(node: ?Object, opts?: Object): boolean {
|
|||||||
if (
|
if (
|
||||||
nodeType === "Block" ||
|
nodeType === "Block" ||
|
||||||
"BlockStatement" === nodeType ||
|
"BlockStatement" === nodeType ||
|
||||||
"Program" === nodeType
|
"Program" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "BlockStatement" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3442,7 +3463,11 @@ export function isStatement(node: ?Object, opts?: Object): boolean {
|
|||||||
"TSModuleDeclaration" === nodeType ||
|
"TSModuleDeclaration" === nodeType ||
|
||||||
"TSImportEqualsDeclaration" === nodeType ||
|
"TSImportEqualsDeclaration" === nodeType ||
|
||||||
"TSExportAssignment" === nodeType ||
|
"TSExportAssignment" === nodeType ||
|
||||||
"TSNamespaceExportDeclaration" === nodeType
|
"TSNamespaceExportDeclaration" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" &&
|
||||||
|
("Statement" === node.expectedNode ||
|
||||||
|
"Declaration" === node.expectedNode ||
|
||||||
|
"BlockStatement" === node.expectedNode))
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3667,7 +3692,8 @@ export function isPureish(node: ?Object, opts?: Object): boolean {
|
|||||||
"ArrowFunctionExpression" === nodeType ||
|
"ArrowFunctionExpression" === nodeType ||
|
||||||
"ClassDeclaration" === nodeType ||
|
"ClassDeclaration" === nodeType ||
|
||||||
"ClassExpression" === nodeType ||
|
"ClassExpression" === nodeType ||
|
||||||
"BigIntLiteral" === nodeType
|
"BigIntLiteral" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "StringLiteral" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3708,7 +3734,8 @@ export function isDeclaration(node: ?Object, opts?: Object): boolean {
|
|||||||
"TSInterfaceDeclaration" === nodeType ||
|
"TSInterfaceDeclaration" === nodeType ||
|
||||||
"TSTypeAliasDeclaration" === nodeType ||
|
"TSTypeAliasDeclaration" === nodeType ||
|
||||||
"TSEnumDeclaration" === nodeType ||
|
"TSEnumDeclaration" === nodeType ||
|
||||||
"TSModuleDeclaration" === nodeType
|
"TSModuleDeclaration" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "Declaration" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3729,7 +3756,9 @@ export function isPatternLike(node: ?Object, opts?: Object): boolean {
|
|||||||
"RestElement" === nodeType ||
|
"RestElement" === nodeType ||
|
||||||
"AssignmentPattern" === nodeType ||
|
"AssignmentPattern" === nodeType ||
|
||||||
"ArrayPattern" === nodeType ||
|
"ArrayPattern" === nodeType ||
|
||||||
"ObjectPattern" === nodeType
|
"ObjectPattern" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" &&
|
||||||
|
("Pattern" === node.expectedNode || "Identifier" === node.expectedNode))
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3752,7 +3781,9 @@ export function isLVal(node: ?Object, opts?: Object): boolean {
|
|||||||
"AssignmentPattern" === nodeType ||
|
"AssignmentPattern" === nodeType ||
|
||||||
"ArrayPattern" === nodeType ||
|
"ArrayPattern" === nodeType ||
|
||||||
"ObjectPattern" === nodeType ||
|
"ObjectPattern" === nodeType ||
|
||||||
"TSParameterProperty" === nodeType
|
"TSParameterProperty" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" &&
|
||||||
|
("Pattern" === node.expectedNode || "Identifier" === node.expectedNode))
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3770,7 +3801,8 @@ export function isTSEntityName(node: ?Object, opts?: Object): boolean {
|
|||||||
if (
|
if (
|
||||||
nodeType === "TSEntityName" ||
|
nodeType === "TSEntityName" ||
|
||||||
"Identifier" === nodeType ||
|
"Identifier" === nodeType ||
|
||||||
"TSQualifiedName" === nodeType
|
"TSQualifiedName" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "Identifier" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3793,7 +3825,8 @@ export function isLiteral(node: ?Object, opts?: Object): boolean {
|
|||||||
"BooleanLiteral" === nodeType ||
|
"BooleanLiteral" === nodeType ||
|
||||||
"RegExpLiteral" === nodeType ||
|
"RegExpLiteral" === nodeType ||
|
||||||
"TemplateLiteral" === nodeType ||
|
"TemplateLiteral" === nodeType ||
|
||||||
"BigIntLiteral" === nodeType
|
"BigIntLiteral" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "StringLiteral" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3824,7 +3857,8 @@ export function isImmutable(node: ?Object, opts?: Object): boolean {
|
|||||||
"JSXFragment" === nodeType ||
|
"JSXFragment" === nodeType ||
|
||||||
"JSXOpeningFragment" === nodeType ||
|
"JSXOpeningFragment" === nodeType ||
|
||||||
"JSXClosingFragment" === nodeType ||
|
"JSXClosingFragment" === nodeType ||
|
||||||
"BigIntLiteral" === nodeType
|
"BigIntLiteral" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "StringLiteral" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
@ -3940,7 +3974,8 @@ export function isPattern(node: ?Object, opts?: Object): boolean {
|
|||||||
nodeType === "Pattern" ||
|
nodeType === "Pattern" ||
|
||||||
"AssignmentPattern" === nodeType ||
|
"AssignmentPattern" === nodeType ||
|
||||||
"ArrayPattern" === nodeType ||
|
"ArrayPattern" === nodeType ||
|
||||||
"ObjectPattern" === nodeType
|
"ObjectPattern" === nodeType ||
|
||||||
|
(nodeType === "Placeholder" && "Pattern" === node.expectedNode)
|
||||||
) {
|
) {
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import shallowEqual from "../utils/shallowEqual";
|
import shallowEqual from "../utils/shallowEqual";
|
||||||
import isType from "./isType";
|
import isType from "./isType";
|
||||||
|
import isPlaceholderType from "./isPlaceholderType";
|
||||||
|
import { FLIPPED_ALIAS_KEYS } from "../definitions";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether `node` is of given `type`.
|
* Returns whether `node` is of given `type`.
|
||||||
@ -11,7 +13,21 @@ export default function is(type: string, node: Object, opts?: Object): boolean {
|
|||||||
if (!node) return false;
|
if (!node) return false;
|
||||||
|
|
||||||
const matches = isType(node.type, type);
|
const matches = isType(node.type, type);
|
||||||
if (!matches) return false;
|
if (!matches) {
|
||||||
|
if (!opts && node.type === "Placeholder" && type in FLIPPED_ALIAS_KEYS) {
|
||||||
|
// We can only return true if the placeholder doesn't replace a real node,
|
||||||
|
// but it replaces a category of nodes (an alias).
|
||||||
|
//
|
||||||
|
// t.is("Identifier", node) gives some guarantees about node's shape, so we
|
||||||
|
// can't say that Placeholder(expectedNode: "Identifier") is an identifier
|
||||||
|
// because it doesn't have the same properties.
|
||||||
|
// On the other hand, t.is("Expression", node) doesn't say anything about
|
||||||
|
// the shape of node because Expression can be many different nodes: we can,
|
||||||
|
// and should, safely report expression placeholders as Expressions.
|
||||||
|
return isPlaceholderType(node.expectedNode, type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof opts === "undefined") {
|
if (typeof opts === "undefined") {
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
21
packages/babel-types/src/validators/isPlaceholderType.js
Normal file
21
packages/babel-types/src/validators/isPlaceholderType.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// @flow
|
||||||
|
import { PLACEHOLDERS_ALIAS } from "../definitions";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if a `placeholderType` is a `targetType` or if `targetType` is an alias of `placeholderType`.
|
||||||
|
*/
|
||||||
|
export default function isPlaceholderType(
|
||||||
|
placeholderType: ?string,
|
||||||
|
targetType: string,
|
||||||
|
): boolean {
|
||||||
|
if (placeholderType === targetType) return true;
|
||||||
|
|
||||||
|
const aliases: ?Array<string> = PLACEHOLDERS_ALIAS[placeholderType];
|
||||||
|
if (aliases) {
|
||||||
|
for (const alias of aliases) {
|
||||||
|
if (targetType === alias) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@ -216,4 +216,95 @@ describe("validators", function() {
|
|||||||
expect(t.isType(undefined, "Expression")).toBe(false);
|
expect(t.isType(undefined, "Expression")).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("placeholders", function() {
|
||||||
|
describe("isPlaceholderType", function() {
|
||||||
|
describe("when placeholderType is a specific node type", function() {
|
||||||
|
const placeholder = "Identifier";
|
||||||
|
|
||||||
|
it("returns true if targetType is placeholderType", function() {
|
||||||
|
expect(t.isPlaceholderType(placeholder, "Identifier")).toBe(true);
|
||||||
|
});
|
||||||
|
it("returns true if targetType an alias for placeholderType", function() {
|
||||||
|
expect(t.isPlaceholderType(placeholder, "Expression")).toBe(true);
|
||||||
|
});
|
||||||
|
it("returns false for unrelated types", function() {
|
||||||
|
expect(t.isPlaceholderType(placeholder, "String")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when placeholderType is a generic alias type", function() {
|
||||||
|
const placeholder = "Pattern";
|
||||||
|
|
||||||
|
it("returns true if targetType is placeholderType", function() {
|
||||||
|
expect(t.isPlaceholderType(placeholder, "Pattern")).toBe(true);
|
||||||
|
});
|
||||||
|
it("returns true if targetType an alias for placeholderType", function() {
|
||||||
|
expect(t.isPlaceholderType(placeholder, "LVal")).toBe(true);
|
||||||
|
});
|
||||||
|
it("returns false for unrelated types", function() {
|
||||||
|
expect(t.isPlaceholderType(placeholder, "Expression")).toBe(false);
|
||||||
|
});
|
||||||
|
it("returns false if targetType is aliased by placeholderType", function() {
|
||||||
|
// i.e. a Pattern might not be an Identifier
|
||||||
|
expect(t.isPlaceholderType(placeholder, "Identifier")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("is", function() {
|
||||||
|
describe("when the placeholder matches a specific node", function() {
|
||||||
|
const identifier = t.placeholder("Identifier", t.identifier("foo"));
|
||||||
|
|
||||||
|
it("returns false if targetType is expectedNode", function() {
|
||||||
|
expect(t.is("Identifier", identifier)).toBe(false);
|
||||||
|
});
|
||||||
|
it("returns true if targetType is an alias", function() {
|
||||||
|
expect(t.is("LVal", identifier)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the placeholder matches a generic alias", function() {
|
||||||
|
const pattern = t.placeholder("Pattern", t.identifier("bar"));
|
||||||
|
|
||||||
|
it("returns false if targetType is aliased as expectedNode", function() {
|
||||||
|
// i.e. a Pattern might not be an Identifier
|
||||||
|
expect(t.is("Identifier", pattern)).toBe(false);
|
||||||
|
});
|
||||||
|
it("returns true if targetType is expectedNode", function() {
|
||||||
|
expect(t.is("Pattern", pattern)).toBe(true);
|
||||||
|
});
|
||||||
|
it("returns true if targetType is an alias for expectedNode", function() {
|
||||||
|
expect(t.is("LVal", pattern)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("is[Type]", function() {
|
||||||
|
describe("when the placeholder matches a specific node", function() {
|
||||||
|
const identifier = t.placeholder("Identifier", t.identifier("foo"));
|
||||||
|
|
||||||
|
it("returns false if targetType is expectedNode", function() {
|
||||||
|
expect(t.isIdentifier(identifier)).toBe(false);
|
||||||
|
});
|
||||||
|
it("returns true if targetType is an alias", function() {
|
||||||
|
expect(t.isLVal(identifier)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when the placeholder matches a generic alias", function() {
|
||||||
|
const pattern = t.placeholder("Pattern", t.identifier("bar"));
|
||||||
|
|
||||||
|
it("returns false if targetType is aliased as expectedNode", function() {
|
||||||
|
expect(t.isIdentifier(pattern)).toBe(false);
|
||||||
|
});
|
||||||
|
it("returns true if targetType is expectedNode", function() {
|
||||||
|
expect(t.isPattern(pattern)).toBe(true);
|
||||||
|
});
|
||||||
|
it("returns true if targetType is an alias for expectedNode", function() {
|
||||||
|
expect(t.isLVal(pattern)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user