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); }