scripts/updateBabelTypesFlowTypes.js (258 lines of code) (raw):
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const t = require('@babel/types');
// This file is a copy of https://raw.githubusercontent.com/MichaReiser/babel/babel-types-flow-types/packages/babel-types/scripts/generators/flow.js
// The goal is to remove this file after the PR has been merged into the babel repository.
// Disable eslint since this code isn't owned by us.
/* eslint-disable */
const NODE_PREFIX = 'BabelNode';
let code = `// NOTE: This file is autogenerated. Do not modify.
// See xplat/js/tools/metro/scripts/updateBabelTypesFlowTypes.js.
// @flow
'use strict';
declare type ${NODE_PREFIX}BaseComment = {|
value: string;
start: number;
end: number;
loc: ${NODE_PREFIX}SourceLocation;
|};
declare type ${NODE_PREFIX}CommentBlock = {|
...${NODE_PREFIX}BaseComment;
type: "CommentBlock";
|};
declare type ${NODE_PREFIX}CommentLine ={|
...${NODE_PREFIX}BaseComment,
type: "CommentLine";
|};
declare type ${NODE_PREFIX}Comment = ${NODE_PREFIX}CommentBlock | ${NODE_PREFIX}CommentLine;
declare type ${NODE_PREFIX}SourceLocation = {|
start: {
line: number;
column: number;
};
end: {
line: number;
column: number;
};
|};
\n\n`;
const lines = [];
for (const type in t.NODE_FIELDS) {
const fields = t.NODE_FIELDS[type];
const struct = ['type: "' + type + '";'];
const args = [];
const builderNames = t.BUILDER_KEYS[type];
Object.keys(t.NODE_FIELDS[type])
.sort((fieldA, fieldB) => {
const indexA = t.BUILDER_KEYS[type].indexOf(fieldA);
const indexB = t.BUILDER_KEYS[type].indexOf(fieldB);
if (indexA === indexB) return fieldA < fieldB ? -1 : 1;
if (indexA === -1) return 1;
if (indexB === -1) return -1;
return indexA - indexB;
})
.forEach(fieldName => {
const field = fields[fieldName];
let suffix = '';
if (field.optional || field.default != null) suffix += '?';
let typeAnnotation = 'any';
const validate = field.validate;
if (validate) {
typeAnnotation = stringifyValidator(validate, NODE_PREFIX);
}
if (typeAnnotation) {
suffix += ': ' + typeAnnotation;
}
if (builderNames.includes(fieldName)) {
args.push(t.toBindingIdentifierName(fieldName) + suffix);
}
if (t.isValidIdentifier(fieldName)) {
struct.push(fieldName + suffix + ';');
}
});
// Flow seems to deoptimize the union type if another type is spread into the node declaration.
// Defining the base props over and over again significantely speeds up the type checking.
code += `declare type ${NODE_PREFIX}${type} = {|
leadingComments?: Array<${NODE_PREFIX}Comment>;
innerComments?: Array<${NODE_PREFIX}Comment>;
trailingComments?: Array<${NODE_PREFIX}Comment>;
start: ?number;
end: ?number;
loc: ?${NODE_PREFIX}SourceLocation,
${struct.join('\n ').trim()}
|};\n\n`;
// Flow chokes on super() and import() :/
if (type !== 'Super' && type !== 'Import') {
lines.push(
`declare export function ${toFunctionName(type)}(${args.join(
', ',
)}): ${NODE_PREFIX}${type};`,
);
} else {
const functionName = toFunctionName(type);
lines.push(
`declare var _${functionName}: (${args.join(
', ',
)}) => ${NODE_PREFIX}${type};`,
`declare export { _${functionName} as ${functionName} }`,
);
}
}
for (let i = 0; i < t.TYPES.length; i++) {
const type = t.TYPES[i];
let decl = `declare export function is${type}(node: ?Object, opts?: ?Object): boolean`;
const realName = t.DEPRECATED_KEYS[type] ?? type;
if (t.NODE_FIELDS[realName]) {
decl += ` %checks (node != null && node.type === '${realName}');`;
} else if (t.FLIPPED_ALIAS_KEYS[realName]) {
const types = t.FLIPPED_ALIAS_KEYS[realName];
const checks = types.map(t => `node.type === '${t}'`).join(' || ');
decl += ` %checks (node != null && (${checks}));`;
} else {
continue;
}
lines.push(decl);
}
lines.push(
// builders/
// eslint-disable-next-line max-len
`declare export function createTypeAnnotationBasedOnTypeof(type: 'string' | 'number' | 'undefined' | 'boolean' | 'function' | 'object' | 'symbol'): ${NODE_PREFIX}TypeAnnotation`,
// eslint-disable-next-line max-len
`declare export function createUnionTypeAnnotation(types: Array<${NODE_PREFIX}FlowType>): ${NODE_PREFIX}UnionTypeAnnotation`,
// eslint-disable-next-line max-len
`declare export function createFlowUnionType(types: Array<${NODE_PREFIX}FlowType>): ${NODE_PREFIX}UnionTypeAnnotation`,
// this smells like "internal API"
// eslint-disable-next-line max-len
`declare export function buildChildren(node: { children: Array<${NODE_PREFIX}JSXText | ${NODE_PREFIX}JSXExpressionContainer | ${NODE_PREFIX}JSXSpreadChild | ${NODE_PREFIX}JSXElement | ${NODE_PREFIX}JSXFragment | ${NODE_PREFIX}JSXEmptyExpression> }): Array<${NODE_PREFIX}JSXText | ${NODE_PREFIX}JSXExpressionContainer | ${NODE_PREFIX}JSXSpreadChild | ${NODE_PREFIX}JSXElement | ${NODE_PREFIX}JSXFragment>`,
// clone/
`declare export function clone<T>(n: T): T;`,
`declare export function cloneDeep<T>(n: T): T;`,
`declare export function cloneDeepWithoutLoc<T>(n: T): T;`,
`declare export function cloneNode<T>(n: T, deep?: boolean, withoutLoc?: boolean): T;`,
`declare export function cloneWithoutLoc<T>(n: T): T;`,
// comments/
`declare type CommentTypeShorthand = 'leading' | 'inner' | 'trailing'`,
// eslint-disable-next-line max-len
`declare export function addComment<T: Node>(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T`,
// eslint-disable-next-line max-len
`declare export function addComments<T: Node>(node: T, type: CommentTypeShorthand, comments: Array<Comment>): T`,
`declare export function inheritInnerComments(node: Node, parent: Node): void`,
`declare export function inheritLeadingComments(node: Node, parent: Node): void`,
`declare export function inheritsComments<T: Node>(node: T, parent: Node): void`,
`declare export function inheritTrailingComments(node: Node, parent: Node): void`,
`declare export function removeComments<T: Node>(node: T): T`,
// converters/
`declare export function ensureBlock(node: ${NODE_PREFIX}, key: string): ${NODE_PREFIX}BlockStatement`,
`declare export function toBindingIdentifierName(name?: ?string): string`,
// eslint-disable-next-line max-len
`declare export function toBlock(node: ${NODE_PREFIX}Statement | ${NODE_PREFIX}Expression, parent?: ${NODE_PREFIX}Function | null): ${NODE_PREFIX}BlockStatement`,
// eslint-disable-next-line max-len
`declare export function toComputedKey(node: ${NODE_PREFIX}Method | ${NODE_PREFIX}Property, key?: ${NODE_PREFIX}Expression | ${NODE_PREFIX}Identifier): ${NODE_PREFIX}Expression`,
// eslint-disable-next-line max-len
`declare export function toExpression(node: ${NODE_PREFIX}ExpressionStatement | ${NODE_PREFIX}Expression | ${NODE_PREFIX}Class | ${NODE_PREFIX}Function): ${NODE_PREFIX}Expression`,
`declare export function toIdentifier(name?: ?string): string`,
// eslint-disable-next-line max-len
`declare export function toKeyAlias(node: ${NODE_PREFIX}Method | ${NODE_PREFIX}Property, key?: ${NODE_PREFIX}): string`,
// toSequenceExpression relies on types that aren't declared in flow
// eslint-disable-next-line max-len
`declare export function toStatement(node: ${NODE_PREFIX}Statement | ${NODE_PREFIX}Class | ${NODE_PREFIX}Function | ${NODE_PREFIX}AssignmentExpression, ignore?: boolean): ${NODE_PREFIX}Statement | void`,
`declare export function valueToNode(value: any): ${NODE_PREFIX}Expression`,
// modifications/
// eslint-disable-next-line max-len
`declare export function removeTypeDuplicates(types: Array<${NODE_PREFIX}FlowType>): Array<${NODE_PREFIX}FlowType>`,
// eslint-disable-next-line max-len
`declare export function appendToMemberExpression(member: ${NODE_PREFIX}MemberExpression, append: ${NODE_PREFIX}, computed?: boolean): ${NODE_PREFIX}MemberExpression`,
// eslint-disable-next-line max-len
`declare export function inherits<T: Node>(child: T, parent: ${NODE_PREFIX} | null | void): T`,
// eslint-disable-next-line max-len
`declare export function prependToMemberExpression(member: ${NODE_PREFIX}MemberExpression, prepend: ${NODE_PREFIX}Expression): ${NODE_PREFIX}MemberExpression`,
`declare export function removeProperties<T>(n: T, opts: ?{}): void;`,
`declare export function removePropertiesDeep<T>(n: T, opts: ?{}): T;`,
// retrievers/
// eslint-disable-next-line max-len
`declare export function getBindingIdentifiers(node: ${NODE_PREFIX}, duplicates: boolean, outerOnly?: boolean): { [key: string]: ${NODE_PREFIX}Identifier | Array<${NODE_PREFIX}Identifier> }`,
// eslint-disable-next-line max-len
`declare export function getOuterBindingIdentifiers(node: Node, duplicates: boolean): { [key: string]: ${NODE_PREFIX}Identifier | Array<${NODE_PREFIX}Identifier> }`,
// traverse/
`declare export type TraversalAncestors = Array<{
node: BabelNode,
key: string,
index?: number,
}>;
declare export type TraversalHandler<T> = (BabelNode, TraversalAncestors, T) => void;
declare export type TraversalHandlers<T> = {
enter?: TraversalHandler<T>,
exit?: TraversalHandler<T>,
};`.replace(/(^|\n) {2}/g, '$1'),
// eslint-disable-next-line
`declare export function traverse<T>(n: BabelNode, TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;`,
`declare export function traverseFast<T>(n: Node, h: TraversalHandler<T>, state?: T): void;`,
// utils/
// cleanJSXElementLiteralChild is not exported
// inherit is not exported
`declare export function shallowEqual(actual: Object, expected: Object): boolean`,
// validators/
// eslint-disable-next-line max-len
`declare export function buildMatchMemberExpression(match: string, allowPartial?: boolean): (?BabelNode) => boolean`,
`declare export function is(type: string, n: BabelNode, opts: Object): boolean;`,
`declare export function isBinding(node: BabelNode, parent: BabelNode, grandparent?: BabelNode): boolean`,
`declare export function isBlockScoped(node: BabelNode): boolean`,
`declare export function isLet(node: BabelNode): boolean %checks (node.type === 'VariableDeclaration')`,
`declare export function isNode(node: ?Object): boolean`,
`declare export function isNodesEquivalent(a: any, b: any): boolean`,
`declare export function isPlaceholderType(placeholderType: string, targetType: string): boolean`,
`declare export function isReferenced(node: BabelNode, parent: BabelNode, grandparent?: BabelNode): boolean`,
`declare export function isScope(node: BabelNode, parent: BabelNode): boolean %checks (node.type === 'BlockStatement' || node.type === 'CatchClause' || node.type === 'DoWhileStatement' || node.type === 'ForInStatement' || node.type === 'ForStatement' || node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'Program' || node.type === 'ObjectMethod' || node.type === 'SwitchStatement' || node.type === 'WhileStatement' || node.type === 'ArrowFunctionExpression' || node.type === 'ClassExpression' || node.type === 'ClassDeclaration' || node.type === 'ForOfStatement' || node.type === 'ClassMethod' || node.type === 'ClassPrivateMethod' || node.type === 'TSModuleBlock')`,
`declare export function isSpecifierDefault(specifier: BabelNodeModuleSpecifier): boolean`,
`declare export function isType(nodetype: ?string, targetType: string): boolean`,
`declare export function isValidES3Identifier(name: string): boolean`,
`declare export function isValidES3Identifier(name: string): boolean`,
`declare export function isValidIdentifier(name: string): boolean`,
`declare export function isVar(node: BabelNode): boolean %checks (node.type === 'VariableDeclaration')`,
// eslint-disable-next-line max-len
`declare export function matchesPattern(node: ?BabelNode, match: string | Array<string>, allowPartial?: boolean): boolean`,
`declare export function validate(n: BabelNode, key: string, value: mixed): void;`,
);
code += `declare type ${NODE_PREFIX} = ${Object.keys(t.NODE_FIELDS)
.map(type => `${NODE_PREFIX}${type}`)
.join(' | ')};\n`;
for (const type in t.FLIPPED_ALIAS_KEYS) {
const types = t.FLIPPED_ALIAS_KEYS[type];
code += `declare type ${NODE_PREFIX}${type} = ${types
.map(type => `${NODE_PREFIX}${type}`)
.join(' | ')};\n`;
}
// Module level exports without NODE_PREFIX prefix
const aliasedTypes = [
'CommentBlock',
'CommentLine',
'Comment',
'SourceLocation',
...Object.keys(t.NODE_FIELDS),
...Object.keys(t.FLIPPED_ALIAS_KEYS),
];
lines.push(
`declare export type Node = ${NODE_PREFIX};`,
...aliasedTypes.map(
type => `declare export type ${type} = ${NODE_PREFIX}${type};`,
),
);
code += `\ndeclare module "@babel/types" {
${lines.join('\n').replace(/\n/g, '\n ').trim()}
}\n`;
process.stdout.write(code);
// Copied from https://raw.githubusercontent.com/babel/babel/main/packages/babel-types/scripts/utils/stringifyValidator.js
function stringifyValidator(validator, nodePrefix: string): string {
if (validator === undefined) {
return 'any';
}
if (validator.each) {
return `Array<${stringifyValidator(validator.each, nodePrefix)}>`;
}
if (validator.chainOf) {
return stringifyValidator(validator.chainOf[1], nodePrefix);
}
if (validator.oneOf) {
return validator.oneOf.map(JSON.stringify).join(' | ');
}
if (validator.oneOfNodeTypes) {
return validator.oneOfNodeTypes.map(_ => nodePrefix + _).join(' | ');
}
if (validator.oneOfNodeOrValueTypes) {
return validator.oneOfNodeOrValueTypes
.map(_ => {
return isValueType(_) ? _ : nodePrefix + _;
})
.join(' | ');
}
if (validator.type) {
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'];
}
/**
* Heuristic to decide whether or not the given type is a value type (eg. "null")
* or a Node type (eg. "Expression").
*/
function isValueType(type: string): boolean {
return type.charAt(0).toLowerCase() === type.charAt(0);
}
// Copied from https://raw.githubusercontent.com/babel/babel/main/packages/babel-types/scripts/utils/toFunctionName.js
function toFunctionName(typeName: string): string {
const _ = typeName.replace(/^TS/, 'ts').replace(/^JSX/, 'jsx');
return _.slice(0, 1).toLowerCase() + _.slice(1);
}