diff --git a/packages/babel-types/scripts/generators/flow.js b/packages/babel-types/scripts/generators/flow.js index f3d8a85233fd..8b40554e2123 100644 --- a/packages/babel-types/scripts/generators/flow.js +++ b/packages/babel-types/scripts/generators/flow.js @@ -121,12 +121,69 @@ for (let i = 0; i < t.TYPES.length; i++) { } 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(n: T): T;`, `declare function cloneDeep(n: T): T;`, `declare function cloneNode(n: T, deep?: boolean): T;`, + `declare function cloneWithoutLoc(n: T): T;`, + + // comments/ + `declare type CommentTypeShorthand = 'leading' | 'inner' | 'trailing'`, + // eslint-disable-next-line max-len + `declare function addComment(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T`, + // eslint-disable-next-line max-len + `declare function addComments(node: T, type: CommentTypeShorthand, comments: Array): T`, + `declare function inheritInnerComments(node: Node, parent: Node): void`, + `declare function inheritLeadingComments(node: Node, parent: Node): void`, + `declare function inheritsComments(node: T, parent: Node): void`, + `declare function inheritTrailingComments(node: Node, parent: Node): void`, + `declare function removeComments(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(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(n: T, opts: ?{}): void;`, `declare function removePropertiesDeep(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<{ node: BabelNode, key: string, @@ -139,6 +196,16 @@ lines.push( };`.replace(/(^|\n) {2}/g, "$1"), // eslint-disable-next-line `declare function traverse(n: BabelNode, TraversalHandler | TraversalHandlers, state?: T): void;`, + `declare function traverseFast(n: Node, h: TraversalHandler, 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 isBinding(node: BabelNode, parent: BabelNode, grandparent?: 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 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, allowPartial?: boolean): boolean`, + `declare function validate(n: BabelNode, key: string, value: mixed): void;` ); for (const type in t.FLIPPED_ALIAS_KEYS) { diff --git a/packages/babel-types/scripts/generators/typescript.js b/packages/babel-types/scripts/generators/typescript.js index 6666a277d37e..01e6c4782819 100644 --- a/packages/babel-types/scripts/generators/typescript.js +++ b/packages/babel-types/scripts/generators/typescript.js @@ -62,6 +62,13 @@ for (const type in t.NODE_FIELDS) { fieldNames.forEach(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, ""); 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++) { - let decl = `export function is${t.TYPES[i]}(node: object | null | undefined, opts?: object | null): `; - - if (t.NODE_FIELDS[t.TYPES[i]]) { - decl += `node is ${t.TYPES[i]};`; - } else if (t.FLIPPED_ALIAS_KEYS[t.TYPES[i]]) { - decl += `node is ${t.TYPES[i]};`; - } else { - decl += `boolean;`; - } - - lines.push(decl); +for (const typeName of t.TYPES) { + const result = + t.NODE_FIELDS[typeName] || t.FLIPPED_ALIAS_KEYS[typeName] + ? `node is ${typeName}` + : "boolean"; + + lines.push( + `export function is${typeName}(node: object | null | undefined, opts?: object | null): ${result};`, + // TypeScript 3.7: https://github.com/microsoft/TypeScript/pull/32695 will allow assert declarations + // eslint-disable-next-line max-len + `// export function assert${typeName}(node: object | null | undefined, opts?: object | null): asserts ${ + result === "boolean" ? "node" : result + };` + ); } 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(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): UnionTypeAnnotation`, + // this smells like "internal API" + // eslint-disable-next-line max-len + `export function buildChildren(node: { children: ReadonlyArray }): JSXElement['children']`, + + // clone/ `export function clone(n: T): T;`, `export function cloneDeep(n: T): T;`, `export function cloneNode(n: T, deep?: boolean): T;`, + `export function cloneWithoutLoc(n: T): T;`, + + // comments/ + `export type CommentTypeShorthand = 'leading' | 'inner' | 'trailing'`, + // eslint-disable-next-line max-len + `export function addComment(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T`, + // eslint-disable-next-line max-len + `export function addComments(node: T, type: CommentTypeShorthand, comments: ReadonlyArray): T`, + `export function inheritInnerComments(node: Node, parent: Node): void`, + `export function inheritLeadingComments(node: Node, parent: Node): void`, + `export function inheritsComments(node: T, parent: Node): void`, + `export function inheritTrailingComments(node: Node, parent: Node): void`, + `export function removeComments(node: T): T`, + + // converters/ + // eslint-disable-next-line max-len + `export function ensureBlock(node: Extract): BlockStatement`, + // too complex? + // eslint-disable-next-line max-len + `export function ensureBlock = 'body'>(node: Extract>, 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>(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, 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): 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[]`, + // eslint-disable-next-line max-len + `export function appendToMemberExpression>(member: T, append: MemberExpression['property'], computed?: boolean): T`, + // eslint-disable-next-line max-len + `export function inherits(child: T, parent: Node | null | undefined): T`, + // eslint-disable-next-line max-len + `export function prependToMemberExpression>(member: T, prepend: MemberExpression['object']): T`, `export function removeProperties( n: Node, opts?: { preserveComments: boolean } | null @@ -136,34 +231,76 @@ lines.push( n: T, opts?: { preserveComments: boolean } | null ): T;`, + + // retrievers/ + // eslint-disable-next-line max-len + `export function getBindingIdentifiers(node: Node, duplicates: true, outerOnly?: boolean): Record>`, + // eslint-disable-next-line max-len + `export function getBindingIdentifiers(node: Node, duplicates?: false, outerOnly?: boolean): Record`, + // eslint-disable-next-line max-len + `export function getBindingIdentifiers(node: Node, duplicates: boolean, outerOnly?: boolean): Record>`, + // eslint-disable-next-line max-len + `export function getOuterBindingIdentifiers(node: Node, duplicates: true): Record>`, + `export function getOuterBindingIdentifiers(node: Node, duplicates?: false): Record`, + // eslint-disable-next-line max-len + `export function getOuterBindingIdentifiers(node: Node, duplicates: boolean): Record>`, + + // traverse/ `export type TraversalAncestors = ReadonlyArray<{ node: Node, key: string, index?: number, }>; - export type TraversalHandler = (node: Node, parent: TraversalAncestors, type: T) => void; + export type TraversalHandler = ( + this: undefined, node: Node, parent: TraversalAncestors, type: T + ) => void; export type TraversalHandlers = { enter?: TraversalHandler, exit?: TraversalHandler, };`.replace(/(^|\n) {2}/g, "$1"), // eslint-disable-next-line `export function traverse(n: Node, h: TraversalHandler | TraversalHandlers, state?: T): void;`, - `export function is(type: string, n: Node, opts: object): boolean;`, + `export function traverseFast(n: Node, h: TraversalHandler, state?: T): void;`, + + // utils/ + // cleanJSXElementLiteralChild is not exported + // inherit is not exported + `export function shallowEqual(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(type: T, n: Node | null | undefined, required?: undefined): n is Extract`, + // eslint-disable-next-line max-len + `export function is>(type: T, n: Node | null | undefined, required: Partial

): n is P`, + // eslint-disable-next-line max-len + `export function is

(type: string, n: Node | null | undefined, required: Partial

): n is P`, + `export function is(type: string, n: Node | null | undefined, required?: Partial): n is Node`, `export function isBinding(node: Node, parent: Node, grandparent?: Node): boolean`, - `export function isBlockScoped(node: Node): boolean`, - `export function isImmutable(node: Node): boolean`, - `export function isLet(node: Node): boolean`, - `export function isNode(node: object | null | undefined): boolean`, + // eslint-disable-next-line max-len + `export function isBlockScoped(node: Node): node is FunctionDeclaration | ClassDeclaration | VariableDeclaration`, + `export function isImmutable(node: Node): node is Immutable`, + `export function isLet(node: Node): node is VariableDeclaration`, + `export function isNode(node: object | null | undefined): node is Node`, + `export function isNodesEquivalent>(a: T, b: any): b is T`, `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 isScope(node: Node, parent: Node): boolean`, + `export function isScope(node: Node, parent: Node): node is Scopable`, `export function isSpecifierDefault(specifier: ModuleSpecifier): boolean`, + `export function isType(nodetype: string, targetType: T): nodetype is T`, `export function isType(nodetype: string | null | undefined, targetType: string): boolean`, `export function isValidES3Identifier(name: string): boolean`, - `export function isValidES3Identifier(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, allowPartial?: boolean): node is MemberExpression`, + // TypeScript 3.7: ": asserts n is T" + // eslint-disable-next-line max-len + `export function validate(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) {