Add declarations for more of @babel/types exports (#10504)

This adds lots of missing declarations to the types of @babel/types,
including many utility functions useful to plugin authors.

With the typescript types, I tried to make them as useful as possible
for control flow analysis / inference, but Flow doesn't seem to
support overloads and I'm not as familiar with it anyway so it has
simpler types.
This commit is contained in:
Jessica Franco 2019-10-09 02:07:57 +09:00 committed by Nicolò Ribaudo
parent 3498195ae2
commit bc0966a46f
2 changed files with 231 additions and 24 deletions

View File

@ -121,12 +121,69 @@ for (let i = 0; i < t.TYPES.length; i++) {
} }
lines.push( lines.push(
`declare function validate(n: BabelNode, key: string, value: mixed): void;`, // builders/
// eslint-disable-next-line max-len
`declare function createTypeAnnotationBasedOnTypeof(type: 'string' | 'number' | 'undefined' | 'boolean' | 'function' | 'object' | 'symbol'): ${NODE_PREFIX}TypeAnnotation`,
// eslint-disable-next-line max-len
`declare function createUnionTypeAnnotation(types: Array<${NODE_PREFIX}FlowType>): ${NODE_PREFIX}UnionTypeAnnotation`,
// this smells like "internal API"
// eslint-disable-next-line max-len
`declare 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 function clone<T>(n: T): T;`, `declare function clone<T>(n: T): T;`,
`declare function cloneDeep<T>(n: T): T;`, `declare function cloneDeep<T>(n: T): T;`,
`declare function cloneNode<T>(n: T, deep?: boolean): T;`, `declare function cloneNode<T>(n: T, deep?: boolean): T;`,
`declare function cloneWithoutLoc<T>(n: T): T;`,
// comments/
`declare type CommentTypeShorthand = 'leading' | 'inner' | 'trailing'`,
// eslint-disable-next-line max-len
`declare function addComment<T: Node>(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T`,
// eslint-disable-next-line max-len
`declare function addComments<T: Node>(node: T, type: CommentTypeShorthand, comments: Array<Comment>): T`,
`declare function inheritInnerComments(node: Node, parent: Node): void`,
`declare function inheritLeadingComments(node: Node, parent: Node): void`,
`declare function inheritsComments<T: Node>(node: T, parent: Node): void`,
`declare function inheritTrailingComments(node: Node, parent: Node): void`,
`declare function removeComments<T: Node>(node: T): T`,
// converters/
`declare function ensureBlock(node: ${NODE_PREFIX}, key: string): ${NODE_PREFIX}BlockStatement`,
`declare function toBindingIdentifierName(name?: ?string): string`,
// eslint-disable-next-line max-len
`declare function toBlock(node: ${NODE_PREFIX}Statement | ${NODE_PREFIX}Expression, parent?: ${NODE_PREFIX}Function | null): ${NODE_PREFIX}BlockStatement`,
// eslint-disable-next-line max-len
`declare 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 function toExpression(node: ${NODE_PREFIX}ExpressionStatement | ${NODE_PREFIX}Expression | ${NODE_PREFIX}Class | ${NODE_PREFIX}Function): ${NODE_PREFIX}Expression`,
`declare function toIdentifier(name?: ?string): string`,
// eslint-disable-next-line max-len
`declare 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 function toStatement(node: ${NODE_PREFIX}Statement | ${NODE_PREFIX}Class | ${NODE_PREFIX}Function | ${NODE_PREFIX}AssignmentExpression, ignore?: boolean): ${NODE_PREFIX}Statement | void`,
`declare function valueToNode(value: any): ${NODE_PREFIX}Expression`,
// modifications/
// eslint-disable-next-line max-len
`declare function removeTypeDuplicates(types: Array<${NODE_PREFIX}FlowType>): Array<${NODE_PREFIX}FlowType>`,
// eslint-disable-next-line max-len
`declare function appendToMemberExpression(member: ${NODE_PREFIX}MemberExpression, append: ${NODE_PREFIX}, computed?: boolean): ${NODE_PREFIX}MemberExpression`,
// eslint-disable-next-line max-len
`declare function inherits<T: Node>(child: T, parent: ${NODE_PREFIX} | null | void): T`,
// eslint-disable-next-line max-len
`declare function prependToMemberExpression(member: ${NODE_PREFIX}MemberExpression, prepend: ${NODE_PREFIX}Expression): ${NODE_PREFIX}MemberExpression`,
`declare function removeProperties<T>(n: T, opts: ?{}): void;`, `declare function removeProperties<T>(n: T, opts: ?{}): void;`,
`declare function removePropertiesDeep<T>(n: T, opts: ?{}): T;`, `declare function removePropertiesDeep<T>(n: T, opts: ?{}): T;`,
// retrievers/
// eslint-disable-next-line max-len
`declare 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 function getOuterBindingIdentifiers(node: Node, duplicates: boolean): { [key: string]: ${NODE_PREFIX}Identifier | Array<${NODE_PREFIX}Identifier> }`,
// traverse/
`declare type TraversalAncestors = Array<{ `declare type TraversalAncestors = Array<{
node: BabelNode, node: BabelNode,
key: string, key: string,
@ -139,6 +196,16 @@ lines.push(
};`.replace(/(^|\n) {2}/g, "$1"), };`.replace(/(^|\n) {2}/g, "$1"),
// eslint-disable-next-line // eslint-disable-next-line
`declare function traverse<T>(n: BabelNode, TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;`, `declare function traverse<T>(n: BabelNode, TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;`,
`declare function traverseFast<T>(n: Node, h: TraversalHandler<T>, state?: T): void;`,
// utils/
// cleanJSXElementLiteralChild is not exported
// inherit is not exported
`declare function shallowEqual(actual: Object, expected: Object): boolean`,
// validators/
// eslint-disable-next-line max-len
`declare function buildMatchMemberExpression(match: string, allowPartial?: boolean): (?BabelNode) => boolean`,
`declare function is(type: string, n: BabelNode, opts: Object): boolean;`, `declare function is(type: string, n: BabelNode, opts: Object): boolean;`,
`declare function isBinding(node: BabelNode, parent: BabelNode, grandparent?: BabelNode): boolean`, `declare function isBinding(node: BabelNode, parent: BabelNode, grandparent?: BabelNode): boolean`,
`declare function isBlockScoped(node: BabelNode): boolean`, `declare function isBlockScoped(node: BabelNode): boolean`,
@ -154,7 +221,10 @@ lines.push(
`declare function isValidES3Identifier(name: string): boolean`, `declare function isValidES3Identifier(name: string): boolean`,
`declare function isValidES3Identifier(name: string): boolean`, `declare function isValidES3Identifier(name: string): boolean`,
`declare function isValidIdentifier(name: string): boolean`, `declare function isValidIdentifier(name: string): boolean`,
`declare function isVar(node: BabelNode): boolean` `declare function isVar(node: BabelNode): boolean`,
// eslint-disable-next-line max-len
`declare function matchesPattern(node: ?BabelNode, match: string | Array<string>, allowPartial?: boolean): boolean`,
`declare function validate(n: BabelNode, key: string, value: mixed): void;`
); );
for (const type in t.FLIPPED_ALIAS_KEYS) { for (const type in t.FLIPPED_ALIAS_KEYS) {

View File

@ -62,6 +62,13 @@ for (const type in t.NODE_FIELDS) {
fieldNames.forEach(fieldName => { fieldNames.forEach(fieldName => {
const field = fields[fieldName]; const field = fields[fieldName];
// Future / annoying TODO:
// MemberExpression.property, ObjectProperty.key and ObjectMethod.key need special cases; either:
// - convert the declaration to chain() like ClassProperty.key and ClassMethod.key,
// - declare an alias type for valid keys, detect the case and reuse it here,
// - declare a disjoint union with, for example, ObjectPropertyBase,
// ObjectPropertyLiteralKey and ObjectPropertyComputedKey, and declare ObjectProperty
// as "ObjectPropertyBase & (ObjectPropertyLiteralKey | ObjectPropertyComputedKey)"
let typeAnnotation = stringifyValidator(field.validate, ""); let typeAnnotation = stringifyValidator(field.validate, "");
if (isNullable(field) && !hasDefault(field)) { if (isNullable(field) && !hasDefault(field)) {
@ -109,25 +116,113 @@ for (const type in t.NODE_FIELDS) {
} }
} }
for (let i = 0; i < t.TYPES.length; i++) { for (const typeName of t.TYPES) {
let decl = `export function is${t.TYPES[i]}(node: object | null | undefined, opts?: object | null): `; const result =
t.NODE_FIELDS[typeName] || t.FLIPPED_ALIAS_KEYS[typeName]
? `node is ${typeName}`
: "boolean";
if (t.NODE_FIELDS[t.TYPES[i]]) { lines.push(
decl += `node is ${t.TYPES[i]};`; `export function is${typeName}(node: object | null | undefined, opts?: object | null): ${result};`,
} else if (t.FLIPPED_ALIAS_KEYS[t.TYPES[i]]) { // TypeScript 3.7: https://github.com/microsoft/TypeScript/pull/32695 will allow assert declarations
decl += `node is ${t.TYPES[i]};`; // eslint-disable-next-line max-len
} else { `// export function assert${typeName}(node: object | null | undefined, opts?: object | null): asserts ${
decl += `boolean;`; result === "boolean" ? "node" : result
} };`
);
lines.push(decl);
} }
lines.push( lines.push(
`export function validate(n: Node, key: string, value: any): void;`, // assert/
// Commented out as this declaration requires TypeScript 3.7 (what do?)
`// export function assertNode(obj: any): asserts obj is Node`,
// builders/
// eslint-disable-next-line max-len
`export function createTypeAnnotationBasedOnTypeof(type: 'string' | 'number' | 'undefined' | 'boolean' | 'function' | 'object' | 'symbol'): StringTypeAnnotation | VoidTypeAnnotation | NumberTypeAnnotation | BooleanTypeAnnotation | GenericTypeAnnotation`,
`export function createUnionTypeAnnotation<T extends FlowType>(types: [T]): T`,
// this probably misbehaves if there are 0 elements, and it's not a UnionTypeAnnotation if there's only 1
// it is possible to require "2 or more" for this overload ([T, T, ...T[]]) but it requires typescript 3.0
`export function createUnionTypeAnnotation(types: ReadonlyArray<FlowType>): UnionTypeAnnotation`,
// this smells like "internal API"
// eslint-disable-next-line max-len
`export function buildChildren(node: { children: ReadonlyArray<JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment | JSXEmptyExpression> }): JSXElement['children']`,
// clone/
`export function clone<T extends Node>(n: T): T;`, `export function clone<T extends Node>(n: T): T;`,
`export function cloneDeep<T extends Node>(n: T): T;`, `export function cloneDeep<T extends Node>(n: T): T;`,
`export function cloneNode<T extends Node>(n: T, deep?: boolean): T;`, `export function cloneNode<T extends Node>(n: T, deep?: boolean): T;`,
`export function cloneWithoutLoc<T extends Node>(n: T): T;`,
// comments/
`export type CommentTypeShorthand = 'leading' | 'inner' | 'trailing'`,
// eslint-disable-next-line max-len
`export function addComment<T extends Node>(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T`,
// eslint-disable-next-line max-len
`export function addComments<T extends Node>(node: T, type: CommentTypeShorthand, comments: ReadonlyArray<Comment>): T`,
`export function inheritInnerComments(node: Node, parent: Node): void`,
`export function inheritLeadingComments(node: Node, parent: Node): void`,
`export function inheritsComments<T extends Node>(node: T, parent: Node): void`,
`export function inheritTrailingComments(node: Node, parent: Node): void`,
`export function removeComments<T extends Node>(node: T): T`,
// converters/
// eslint-disable-next-line max-len
`export function ensureBlock(node: Extract<Node, { body: BlockStatement | Statement | Expression }>): BlockStatement`,
// too complex?
// eslint-disable-next-line max-len
`export function ensureBlock<K extends keyof Extract<Node, { body: BlockStatement | Statement | Expression }> = 'body'>(node: Extract<Node, Record<K, BlockStatement | Statement | Expression>>, key: K): BlockStatement`,
// gatherSequenceExpressions is not exported
`export function toBindingIdentifierName(name: { toString(): string } | null | undefined): string`,
`export function toBlock(node: Statement | Expression, parent?: Function | null): BlockStatement`,
// it is possible for `node` to be an arbitrary object if `key` is always provided,
// but that doesn't look like intended API
// eslint-disable-next-line max-len
`export function toComputedKey<T extends Extract<Node, { computed: boolean | null }>>(node: T, key?: Expression | Identifier): Expression`,
`export function toExpression(node: Function): FunctionExpression`,
`export function toExpression(node: Class): ClassExpression`,
`export function toExpression(node: ExpressionStatement | Expression | Class | Function): Expression`,
`export function toIdentifier(name: { toString(): string } | null | undefined): string`,
`export function toKeyAlias(node: Method | Property, key?: Node): string`,
// NOTE: this actually uses Scope from @babel/traverse, but we can't add a dependency on its types,
// as they live in @types. Declare the structural subset that is required.
// eslint-disable-next-line max-len
`export function toSequenceExpression(nodes: ReadonlyArray<Node>, scope: { push(value: { id: LVal; kind: 'var'; init?: Expression}): void; buildUndefinedNode(): Node }): SequenceExpression | undefined`,
`export function toStatement(node: AssignmentExpression, ignore?: boolean): ExpressionStatement`,
`export function toStatement(node: Statement | AssignmentExpression, ignore?: boolean): Statement`,
`export function toStatement(node: Class, ignore: true): ClassDeclaration | undefined`,
`export function toStatement(node: Class, ignore?: boolean): ClassDeclaration`,
`export function toStatement(node: Function, ignore: true): FunctionDeclaration | undefined`,
`export function toStatement(node: Function, ignore?: boolean): FunctionDeclaration`,
// eslint-disable-next-line max-len
`export function toStatement(node: Statement | Class | Function | AssignmentExpression, ignore: true): Statement | undefined`,
// eslint-disable-next-line max-len
`export function toStatement(node: Statement | Class | Function | AssignmentExpression, ignore?: boolean): Statement`,
// eslint-disable-next-line max-len
`export function valueToNode(value: undefined): Identifier`, // (should this not be a UnaryExpression to avoid shadowing?)
`export function valueToNode(value: boolean): BooleanLiteral`,
`export function valueToNode(value: null): NullLiteral`,
`export function valueToNode(value: string): StringLiteral`,
// Infinities and NaN need to use a BinaryExpression; negative values must be wrapped in UnaryExpression
`export function valueToNode(value: number): NumericLiteral | BinaryExpression | UnaryExpression`,
`export function valueToNode(value: RegExp): RegExpLiteral`,
// eslint-disable-next-line max-len
`export function valueToNode(value: ReadonlyArray<undefined | boolean | null | string | number | RegExp | object>): ArrayExpression`,
// this throws with objects that are not PlainObject according to lodash,
// or if there are non-valueToNode-able values
`export function valueToNode(value: object): ObjectExpression`,
// eslint-disable-next-line max-len
`export function valueToNode(value: undefined | boolean | null | string | number | RegExp | object): Expression`,
// modifications/
// eslint-disable-next-line max-len
`export function removeTypeDuplicates(types: ReadonlyArray<FlowType | false | null | undefined>): FlowType[]`,
// eslint-disable-next-line max-len
`export function appendToMemberExpression<T extends Pick<MemberExpression, 'object' | 'property'>>(member: T, append: MemberExpression['property'], computed?: boolean): T`,
// eslint-disable-next-line max-len
`export function inherits<T extends Node | null | undefined>(child: T, parent: Node | null | undefined): T`,
// eslint-disable-next-line max-len
`export function prependToMemberExpression<T extends Pick<MemberExpression, 'object' | 'property'>>(member: T, prepend: MemberExpression['object']): T`,
`export function removeProperties( `export function removeProperties(
n: Node, n: Node,
opts?: { preserveComments: boolean } | null opts?: { preserveComments: boolean } | null
@ -136,34 +231,76 @@ lines.push(
n: T, n: T,
opts?: { preserveComments: boolean } | null opts?: { preserveComments: boolean } | null
): T;`, ): T;`,
// retrievers/
// eslint-disable-next-line max-len
`export function getBindingIdentifiers(node: Node, duplicates: true, outerOnly?: boolean): Record<string, Array<Identifier>>`,
// eslint-disable-next-line max-len
`export function getBindingIdentifiers(node: Node, duplicates?: false, outerOnly?: boolean): Record<string, Identifier>`,
// eslint-disable-next-line max-len
`export function getBindingIdentifiers(node: Node, duplicates: boolean, outerOnly?: boolean): Record<string, Identifier | Array<Identifier>>`,
// eslint-disable-next-line max-len
`export function getOuterBindingIdentifiers(node: Node, duplicates: true): Record<string, Array<Identifier>>`,
`export function getOuterBindingIdentifiers(node: Node, duplicates?: false): Record<string, Identifier>`,
// eslint-disable-next-line max-len
`export function getOuterBindingIdentifiers(node: Node, duplicates: boolean): Record<string, Identifier | Array<Identifier>>`,
// traverse/
`export type TraversalAncestors = ReadonlyArray<{ `export type TraversalAncestors = ReadonlyArray<{
node: Node, node: Node,
key: string, key: string,
index?: number, index?: number,
}>; }>;
export type TraversalHandler<T> = (node: Node, parent: TraversalAncestors, type: T) => void; export type TraversalHandler<T> = (
this: undefined, node: Node, parent: TraversalAncestors, type: T
) => void;
export type TraversalHandlers<T> = { export type TraversalHandlers<T> = {
enter?: TraversalHandler<T>, enter?: TraversalHandler<T>,
exit?: TraversalHandler<T>, exit?: TraversalHandler<T>,
};`.replace(/(^|\n) {2}/g, "$1"), };`.replace(/(^|\n) {2}/g, "$1"),
// eslint-disable-next-line // eslint-disable-next-line
`export function traverse<T>(n: Node, h: TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;`, `export function traverse<T>(n: Node, h: TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;`,
`export function is(type: string, n: Node, opts: object): boolean;`, `export function traverseFast<T>(n: Node, h: TraversalHandler<T>, state?: T): void;`,
// utils/
// cleanJSXElementLiteralChild is not exported
// inherit is not exported
`export function shallowEqual<T extends object>(actual: object, expected: T): actual is T`,
// validators/
// eslint-disable-next-line max-len
`export function buildMatchMemberExpression(match: string, allowPartial?: boolean): (node: Node | null | undefined) => node is MemberExpression`,
// eslint-disable-next-line max-len
`export function is<T extends Node['type']>(type: T, n: Node | null | undefined, required?: undefined): n is Extract<Node, { type: T }>`,
// eslint-disable-next-line max-len
`export function is<T extends Node['type'], P extends Extract<Node, { type: T }>>(type: T, n: Node | null | undefined, required: Partial<P>): n is P`,
// eslint-disable-next-line max-len
`export function is<P extends Node>(type: string, n: Node | null | undefined, required: Partial<P>): n is P`,
`export function is(type: string, n: Node | null | undefined, required?: Partial<Node>): n is Node`,
`export function isBinding(node: Node, parent: Node, grandparent?: Node): boolean`, `export function isBinding(node: Node, parent: Node, grandparent?: Node): boolean`,
`export function isBlockScoped(node: Node): boolean`, // eslint-disable-next-line max-len
`export function isImmutable(node: Node): boolean`, `export function isBlockScoped(node: Node): node is FunctionDeclaration | ClassDeclaration | VariableDeclaration`,
`export function isLet(node: Node): boolean`, `export function isImmutable(node: Node): node is Immutable`,
`export function isNode(node: object | null | undefined): boolean`, `export function isLet(node: Node): node is VariableDeclaration`,
`export function isNode(node: object | null | undefined): node is Node`,
`export function isNodesEquivalent<T extends Partial<Node>>(a: T, b: any): b is T`,
`export function isNodesEquivalent(a: any, b: any): boolean`, `export function isNodesEquivalent(a: any, b: any): boolean`,
`export function isPlaceholderType(placeholderType: string, targetType: string): boolean`, `export function isPlaceholderType(placeholderType: Node['type'], targetType: Node['type']): boolean`,
`export function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean`, `export function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean`,
`export function isScope(node: Node, parent: Node): boolean`, `export function isScope(node: Node, parent: Node): node is Scopable`,
`export function isSpecifierDefault(specifier: ModuleSpecifier): boolean`, `export function isSpecifierDefault(specifier: ModuleSpecifier): boolean`,
`export function isType<T extends Node['type']>(nodetype: string, targetType: T): nodetype is T`,
`export function isType(nodetype: string | null | undefined, targetType: string): boolean`, `export function isType(nodetype: string | null | undefined, targetType: string): boolean`,
`export function isValidES3Identifier(name: string): boolean`, `export function isValidES3Identifier(name: string): boolean`,
`export function isValidES3Identifier(name: string): boolean`,
`export function isValidIdentifier(name: string): boolean`, `export function isValidIdentifier(name: string): boolean`,
`export function isVar(node: Node): boolean` `export function isVar(node: Node): node is VariableDeclaration`,
// the MemberExpression implication is incidental, but it follows from the implementation
// eslint-disable-next-line max-len
`export function matchesPattern(node: Node | null | undefined, match: string | ReadonlyArray<string>, allowPartial?: boolean): node is MemberExpression`,
// TypeScript 3.7: ": asserts n is T"
// eslint-disable-next-line max-len
`export function validate<T extends Node, K extends keyof T>(n: Node | null | undefined, key: K, value: T[K]): void`,
`export function validate(n: Node, key: string, value: any): void;`
); );
for (const type in t.DEPRECATED_KEYS) { for (const type in t.DEPRECATED_KEYS) {