1 | import t from "../../lib/index.js";
|
---|
2 | import stringifyValidator from "../utils/stringifyValidator.js";
|
---|
3 | import toFunctionName from "../utils/toFunctionName.js";
|
---|
4 |
|
---|
5 | let code = `// NOTE: This file is autogenerated. Do not modify.
|
---|
6 | // See packages/babel-types/scripts/generators/typescript-legacy.js for script used.
|
---|
7 |
|
---|
8 | interface BaseComment {
|
---|
9 | value: string;
|
---|
10 | start: number;
|
---|
11 | end: number;
|
---|
12 | loc: SourceLocation;
|
---|
13 | type: "CommentBlock" | "CommentLine";
|
---|
14 | }
|
---|
15 |
|
---|
16 | export interface CommentBlock extends BaseComment {
|
---|
17 | type: "CommentBlock";
|
---|
18 | }
|
---|
19 |
|
---|
20 | export interface CommentLine extends BaseComment {
|
---|
21 | type: "CommentLine";
|
---|
22 | }
|
---|
23 |
|
---|
24 | export type Comment = CommentBlock | CommentLine;
|
---|
25 |
|
---|
26 | export interface SourceLocation {
|
---|
27 | start: {
|
---|
28 | line: number;
|
---|
29 | column: number;
|
---|
30 | };
|
---|
31 |
|
---|
32 | end: {
|
---|
33 | line: number;
|
---|
34 | column: number;
|
---|
35 | };
|
---|
36 | }
|
---|
37 |
|
---|
38 | interface BaseNode {
|
---|
39 | leadingComments: ReadonlyArray<Comment> | null;
|
---|
40 | innerComments: ReadonlyArray<Comment> | null;
|
---|
41 | trailingComments: ReadonlyArray<Comment> | null;
|
---|
42 | start: number | null;
|
---|
43 | end: number | null;
|
---|
44 | loc: SourceLocation | null;
|
---|
45 | type: Node["type"];
|
---|
46 | extra?: Record<string, unknown>;
|
---|
47 | }
|
---|
48 |
|
---|
49 | export type Node = ${t.TYPES.sort().join(" | ")};\n\n`;
|
---|
50 |
|
---|
51 | //
|
---|
52 |
|
---|
53 | const lines = [];
|
---|
54 |
|
---|
55 | for (const type in t.NODE_FIELDS) {
|
---|
56 | const fields = t.NODE_FIELDS[type];
|
---|
57 | const fieldNames = sortFieldNames(Object.keys(t.NODE_FIELDS[type]), type);
|
---|
58 | const builderNames = t.BUILDER_KEYS[type];
|
---|
59 |
|
---|
60 | const struct = ['type: "' + type + '";'];
|
---|
61 | const args = [];
|
---|
62 |
|
---|
63 | fieldNames.forEach(fieldName => {
|
---|
64 | const field = fields[fieldName];
|
---|
65 | // Future / annoying TODO:
|
---|
66 | // MemberExpression.property, ObjectProperty.key and ObjectMethod.key need special cases; either:
|
---|
67 | // - convert the declaration to chain() like ClassProperty.key and ClassMethod.key,
|
---|
68 | // - declare an alias type for valid keys, detect the case and reuse it here,
|
---|
69 | // - declare a disjoint union with, for example, ObjectPropertyBase,
|
---|
70 | // ObjectPropertyLiteralKey and ObjectPropertyComputedKey, and declare ObjectProperty
|
---|
71 | // as "ObjectPropertyBase & (ObjectPropertyLiteralKey | ObjectPropertyComputedKey)"
|
---|
72 | let typeAnnotation = stringifyValidator(field.validate, "");
|
---|
73 |
|
---|
74 | if (isNullable(field) && !hasDefault(field)) {
|
---|
75 | typeAnnotation += " | null";
|
---|
76 | }
|
---|
77 |
|
---|
78 | if (builderNames.includes(fieldName)) {
|
---|
79 | if (areAllRemainingFieldsNullable(fieldName, builderNames, fields)) {
|
---|
80 | args.push(
|
---|
81 | `${t.toBindingIdentifierName(fieldName)}${
|
---|
82 | isNullable(field) ? "?:" : ":"
|
---|
83 | } ${typeAnnotation}`
|
---|
84 | );
|
---|
85 | } else {
|
---|
86 | args.push(
|
---|
87 | `${t.toBindingIdentifierName(fieldName)}: ${typeAnnotation}${
|
---|
88 | isNullable(field) ? " | undefined" : ""
|
---|
89 | }`
|
---|
90 | );
|
---|
91 | }
|
---|
92 | }
|
---|
93 |
|
---|
94 | const alphaNumeric = /^\w+$/;
|
---|
95 |
|
---|
96 | if (t.isValidIdentifier(fieldName) || alphaNumeric.test(fieldName)) {
|
---|
97 | struct.push(`${fieldName}: ${typeAnnotation};`);
|
---|
98 | } else {
|
---|
99 | struct.push(`"${fieldName}": ${typeAnnotation};`);
|
---|
100 | }
|
---|
101 | });
|
---|
102 |
|
---|
103 | code += `export interface ${type} extends BaseNode {
|
---|
104 | ${struct.join("\n ").trim()}
|
---|
105 | }\n\n`;
|
---|
106 |
|
---|
107 | // super and import are reserved words in JavaScript
|
---|
108 | if (type !== "Super" && type !== "Import") {
|
---|
109 | lines.push(
|
---|
110 | `export function ${toFunctionName(type)}(${args.join(", ")}): ${type};`
|
---|
111 | );
|
---|
112 | } else {
|
---|
113 | const functionName = toFunctionName(type);
|
---|
114 | lines.push(
|
---|
115 | `declare function _${functionName}(${args.join(", ")}): ${type};`,
|
---|
116 | `export { _${functionName} as ${functionName}}`
|
---|
117 | );
|
---|
118 | }
|
---|
119 | }
|
---|
120 |
|
---|
121 | for (const typeName of t.TYPES) {
|
---|
122 | const isDeprecated = !!t.DEPRECATED_KEYS[typeName];
|
---|
123 | const realName = isDeprecated ? t.DEPRECATED_KEYS[typeName] : typeName;
|
---|
124 |
|
---|
125 | const result =
|
---|
126 | t.NODE_FIELDS[realName] || t.FLIPPED_ALIAS_KEYS[realName]
|
---|
127 | ? `node is ${realName}`
|
---|
128 | : "boolean";
|
---|
129 |
|
---|
130 | if (isDeprecated) {
|
---|
131 | lines.push(`/** @deprecated Use \`is${realName}\` */`);
|
---|
132 | }
|
---|
133 | lines.push(
|
---|
134 | `export function is${typeName}(node: object | null | undefined, opts?: object | null): ${result};`
|
---|
135 | );
|
---|
136 |
|
---|
137 | if (isDeprecated) {
|
---|
138 | lines.push(`/** @deprecated Use \`assert${realName}\` */`);
|
---|
139 | }
|
---|
140 | lines.push(
|
---|
141 | `export function assert${typeName}(node: object | null | undefined, opts?: object | null): void;`
|
---|
142 | );
|
---|
143 | }
|
---|
144 |
|
---|
145 | lines.push(
|
---|
146 | // assert/
|
---|
147 | `export function assertNode(obj: any): void`,
|
---|
148 |
|
---|
149 | // builders/
|
---|
150 | // eslint-disable-next-line max-len
|
---|
151 | `export function createTypeAnnotationBasedOnTypeof(type: 'string' | 'number' | 'undefined' | 'boolean' | 'function' | 'object' | 'symbol'): StringTypeAnnotation | VoidTypeAnnotation | NumberTypeAnnotation | BooleanTypeAnnotation | GenericTypeAnnotation`,
|
---|
152 | `export function createUnionTypeAnnotation<T extends FlowType>(types: [T]): T`,
|
---|
153 | `export function createFlowUnionType<T extends FlowType>(types: [T]): T`,
|
---|
154 | // this probably misbehaves if there are 0 elements, and it's not a UnionTypeAnnotation if there's only 1
|
---|
155 | // it is possible to require "2 or more" for this overload ([T, T, ...T[]]) but it requires typescript 3.0
|
---|
156 | `export function createUnionTypeAnnotation(types: ReadonlyArray<FlowType>): UnionTypeAnnotation`,
|
---|
157 | `export function createFlowUnionType(types: ReadonlyArray<FlowType>): UnionTypeAnnotation`,
|
---|
158 | // this smells like "internal API"
|
---|
159 | // eslint-disable-next-line max-len
|
---|
160 | `export function buildChildren(node: { children: ReadonlyArray<JSXText | JSXExpressionContainer | JSXSpreadChild | JSXElement | JSXFragment | JSXEmptyExpression> }): JSXElement['children']`,
|
---|
161 |
|
---|
162 | // clone/
|
---|
163 | `export function clone<T extends Node>(n: T): T;`,
|
---|
164 | `export function cloneDeep<T extends Node>(n: T): T;`,
|
---|
165 | `export function cloneDeepWithoutLoc<T extends Node>(n: T): T;`,
|
---|
166 | `export function cloneNode<T extends Node>(n: T, deep?: boolean, withoutLoc?: boolean): T;`,
|
---|
167 | `export function cloneWithoutLoc<T extends Node>(n: T): T;`,
|
---|
168 |
|
---|
169 | // comments/
|
---|
170 | `export type CommentTypeShorthand = 'leading' | 'inner' | 'trailing'`,
|
---|
171 | // eslint-disable-next-line max-len
|
---|
172 | `export function addComment<T extends Node>(node: T, type: CommentTypeShorthand, content: string, line?: boolean): T`,
|
---|
173 | // eslint-disable-next-line max-len
|
---|
174 | `export function addComments<T extends Node>(node: T, type: CommentTypeShorthand, comments: ReadonlyArray<Comment>): T`,
|
---|
175 | `export function inheritInnerComments(node: Node, parent: Node): void`,
|
---|
176 | `export function inheritLeadingComments(node: Node, parent: Node): void`,
|
---|
177 | `export function inheritsComments<T extends Node>(node: T, parent: Node): void`,
|
---|
178 | `export function inheritTrailingComments(node: Node, parent: Node): void`,
|
---|
179 | `export function removeComments<T extends Node>(node: T): T`,
|
---|
180 |
|
---|
181 | // converters/
|
---|
182 | // eslint-disable-next-line max-len
|
---|
183 | `export function ensureBlock(node: Extract<Node, { body: BlockStatement | Statement | Expression }>): BlockStatement`,
|
---|
184 | // too complex?
|
---|
185 | // eslint-disable-next-line max-len
|
---|
186 | `export function ensureBlock<K extends keyof Extract<Node, { body: BlockStatement | Statement | Expression }> = 'body'>(node: Extract<Node, Record<K, BlockStatement | Statement | Expression>>, key: K): BlockStatement`,
|
---|
187 | // gatherSequenceExpressions is not exported
|
---|
188 | `export function toBindingIdentifierName(name: { toString(): string } | null | undefined): string`,
|
---|
189 | `export function toBlock(node: Statement | Expression, parent?: Function | null): BlockStatement`,
|
---|
190 | // it is possible for `node` to be an arbitrary object if `key` is always provided,
|
---|
191 | // but that doesn't look like intended API
|
---|
192 | // eslint-disable-next-line max-len
|
---|
193 | `export function toComputedKey<T extends Extract<Node, { computed: boolean | null }>>(node: T, key?: Expression | Identifier): Expression`,
|
---|
194 | `export function toExpression(node: Function): FunctionExpression`,
|
---|
195 | `export function toExpression(node: Class): ClassExpression`,
|
---|
196 | `export function toExpression(node: ExpressionStatement | Expression | Class | Function): Expression`,
|
---|
197 | `export function toIdentifier(name: { toString(): string } | null | undefined): string`,
|
---|
198 | `export function toKeyAlias(node: Method | Property, key?: Node): string`,
|
---|
199 | // NOTE: this actually uses Scope from @babel/traverse, but we can't add a dependency on its types,
|
---|
200 | // as they live in @types. Declare the structural subset that is required.
|
---|
201 | // eslint-disable-next-line max-len
|
---|
202 | `export function toSequenceExpression(nodes: ReadonlyArray<Node>, scope: { push(value: { id: LVal; kind: 'var'; init?: Expression}): void; buildUndefinedNode(): Node }): SequenceExpression | undefined`,
|
---|
203 | `export function toStatement(node: AssignmentExpression, ignore?: boolean): ExpressionStatement`,
|
---|
204 | `export function toStatement(node: Statement | AssignmentExpression, ignore?: boolean): Statement`,
|
---|
205 | `export function toStatement(node: Class, ignore: true): ClassDeclaration | undefined`,
|
---|
206 | `export function toStatement(node: Class, ignore?: boolean): ClassDeclaration`,
|
---|
207 | `export function toStatement(node: Function, ignore: true): FunctionDeclaration | undefined`,
|
---|
208 | `export function toStatement(node: Function, ignore?: boolean): FunctionDeclaration`,
|
---|
209 | // eslint-disable-next-line max-len
|
---|
210 | `export function toStatement(node: Statement | Class | Function | AssignmentExpression, ignore: true): Statement | undefined`,
|
---|
211 | // eslint-disable-next-line max-len
|
---|
212 | `export function toStatement(node: Statement | Class | Function | AssignmentExpression, ignore?: boolean): Statement`,
|
---|
213 | // eslint-disable-next-line max-len
|
---|
214 | `export function valueToNode(value: undefined): Identifier`, // (should this not be a UnaryExpression to avoid shadowing?)
|
---|
215 | `export function valueToNode(value: boolean): BooleanLiteral`,
|
---|
216 | `export function valueToNode(value: null): NullLiteral`,
|
---|
217 | `export function valueToNode(value: string): StringLiteral`,
|
---|
218 | // Infinities and NaN need to use a BinaryExpression; negative values must be wrapped in UnaryExpression
|
---|
219 | `export function valueToNode(value: number): NumericLiteral | BinaryExpression | UnaryExpression`,
|
---|
220 | `export function valueToNode(value: RegExp): RegExpLiteral`,
|
---|
221 | // eslint-disable-next-line max-len
|
---|
222 | `export function valueToNode(value: ReadonlyArray<undefined | boolean | null | string | number | RegExp | object>): ArrayExpression`,
|
---|
223 | // this throws with objects that are not PlainObject according to lodash,
|
---|
224 | // or if there are non-valueToNode-able values
|
---|
225 | `export function valueToNode(value: object): ObjectExpression`,
|
---|
226 | // eslint-disable-next-line max-len
|
---|
227 | `export function valueToNode(value: undefined | boolean | null | string | number | RegExp | object): Expression`,
|
---|
228 |
|
---|
229 | // modifications/
|
---|
230 | // eslint-disable-next-line max-len
|
---|
231 | `export function removeTypeDuplicates(types: ReadonlyArray<FlowType | false | null | undefined>): FlowType[]`,
|
---|
232 | // eslint-disable-next-line max-len
|
---|
233 | `export function appendToMemberExpression<T extends Pick<MemberExpression, 'object' | 'property'>>(member: T, append: MemberExpression['property'], computed?: boolean): T`,
|
---|
234 | // eslint-disable-next-line max-len
|
---|
235 | `export function inherits<T extends Node | null | undefined>(child: T, parent: Node | null | undefined): T`,
|
---|
236 | // eslint-disable-next-line max-len
|
---|
237 | `export function prependToMemberExpression<T extends Pick<MemberExpression, 'object' | 'property'>>(member: T, prepend: MemberExpression['object']): T`,
|
---|
238 | `export function removeProperties(
|
---|
239 | n: Node,
|
---|
240 | opts?: { preserveComments: boolean } | null
|
---|
241 | ): void;`,
|
---|
242 | `export function removePropertiesDeep<T extends Node>(
|
---|
243 | n: T,
|
---|
244 | opts?: { preserveComments: boolean } | null
|
---|
245 | ): T;`,
|
---|
246 |
|
---|
247 | // retrievers/
|
---|
248 | // eslint-disable-next-line max-len
|
---|
249 | `export function getBindingIdentifiers(node: Node, duplicates: true, outerOnly?: boolean): Record<string, Array<Identifier>>`,
|
---|
250 | // eslint-disable-next-line max-len
|
---|
251 | `export function getBindingIdentifiers(node: Node, duplicates?: false, outerOnly?: boolean): Record<string, Identifier>`,
|
---|
252 | // eslint-disable-next-line max-len
|
---|
253 | `export function getBindingIdentifiers(node: Node, duplicates: boolean, outerOnly?: boolean): Record<string, Identifier | Array<Identifier>>`,
|
---|
254 | // eslint-disable-next-line max-len
|
---|
255 | `export function getOuterBindingIdentifiers(node: Node, duplicates: true): Record<string, Array<Identifier>>`,
|
---|
256 | `export function getOuterBindingIdentifiers(node: Node, duplicates?: false): Record<string, Identifier>`,
|
---|
257 | // eslint-disable-next-line max-len
|
---|
258 | `export function getOuterBindingIdentifiers(node: Node, duplicates: boolean): Record<string, Identifier | Array<Identifier>>`,
|
---|
259 |
|
---|
260 | // traverse/
|
---|
261 | `export type TraversalAncestors = ReadonlyArray<{
|
---|
262 | node: Node,
|
---|
263 | key: string,
|
---|
264 | index?: number,
|
---|
265 | }>;
|
---|
266 | export type TraversalHandler<T> = (
|
---|
267 | this: undefined, node: Node, parent: TraversalAncestors, type: T
|
---|
268 | ) => void;
|
---|
269 | export type TraversalHandlers<T> = {
|
---|
270 | enter?: TraversalHandler<T>,
|
---|
271 | exit?: TraversalHandler<T>,
|
---|
272 | };`.replace(/(^|\n) {2}/g, "$1"),
|
---|
273 | // eslint-disable-next-line
|
---|
274 | `export function traverse<T>(n: Node, h: TraversalHandler<T> | TraversalHandlers<T>, state?: T): void;`,
|
---|
275 | `export function traverseFast<T>(n: Node, h: TraversalHandler<T>, state?: T): void;`,
|
---|
276 |
|
---|
277 | // utils/
|
---|
278 | // cleanJSXElementLiteralChild is not exported
|
---|
279 | // inherit is not exported
|
---|
280 | `export function shallowEqual<T extends object>(actual: object, expected: T): actual is T`,
|
---|
281 |
|
---|
282 | // validators/
|
---|
283 | // eslint-disable-next-line max-len
|
---|
284 | `export function buildMatchMemberExpression(match: string, allowPartial?: boolean): (node: Node | null | undefined) => node is MemberExpression`,
|
---|
285 | // eslint-disable-next-line max-len
|
---|
286 | `export function is<T extends Node['type']>(type: T, n: Node | null | undefined, required?: undefined): n is Extract<Node, { type: T }>`,
|
---|
287 | // eslint-disable-next-line max-len
|
---|
288 | `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`,
|
---|
289 | // eslint-disable-next-line max-len
|
---|
290 | `export function is<P extends Node>(type: string, n: Node | null | undefined, required: Partial<P>): n is P`,
|
---|
291 | `export function is(type: string, n: Node | null | undefined, required?: Partial<Node>): n is Node`,
|
---|
292 | `export function isBinding(node: Node, parent: Node, grandparent?: Node): boolean`,
|
---|
293 | // eslint-disable-next-line max-len
|
---|
294 | `export function isBlockScoped(node: Node): node is FunctionDeclaration | ClassDeclaration | VariableDeclaration`,
|
---|
295 | `export function isImmutable(node: Node): node is Immutable`,
|
---|
296 | `export function isLet(node: Node): node is VariableDeclaration`,
|
---|
297 | `export function isNode(node: object | null | undefined): node is Node`,
|
---|
298 | `export function isNodesEquivalent<T extends Partial<Node>>(a: T, b: any): b is T`,
|
---|
299 | `export function isNodesEquivalent(a: any, b: any): boolean`,
|
---|
300 | `export function isPlaceholderType(placeholderType: Node['type'], targetType: Node['type']): boolean`,
|
---|
301 | `export function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean`,
|
---|
302 | `export function isScope(node: Node, parent: Node): node is Scopable`,
|
---|
303 | `export function isSpecifierDefault(specifier: ModuleSpecifier): boolean`,
|
---|
304 | `export function isType<T extends Node['type']>(nodetype: string, targetType: T): nodetype is T`,
|
---|
305 | `export function isType(nodetype: string | null | undefined, targetType: string): boolean`,
|
---|
306 | `export function isValidES3Identifier(name: string): boolean`,
|
---|
307 | `export function isValidIdentifier(name: string): boolean`,
|
---|
308 | `export function isVar(node: Node): node is VariableDeclaration`,
|
---|
309 | // the MemberExpression implication is incidental, but it follows from the implementation
|
---|
310 | // eslint-disable-next-line max-len
|
---|
311 | `export function matchesPattern(node: Node | null | undefined, match: string | ReadonlyArray<string>, allowPartial?: boolean): node is MemberExpression`,
|
---|
312 | // eslint-disable-next-line max-len
|
---|
313 | `export function validate<T extends Node, K extends keyof T>(n: Node | null | undefined, key: K, value: T[K]): void;`,
|
---|
314 | `export function validate(n: Node, key: string, value: any): void;`
|
---|
315 | );
|
---|
316 |
|
---|
317 | for (const type in t.DEPRECATED_KEYS) {
|
---|
318 | code += `/**
|
---|
319 | * @deprecated Use \`${t.DEPRECATED_KEYS[type]}\`
|
---|
320 | */
|
---|
321 | export type ${type} = ${t.DEPRECATED_KEYS[type]};\n
|
---|
322 | `;
|
---|
323 | }
|
---|
324 |
|
---|
325 | for (const type in t.FLIPPED_ALIAS_KEYS) {
|
---|
326 | const types = t.FLIPPED_ALIAS_KEYS[type];
|
---|
327 | code += `export type ${type} = ${types
|
---|
328 | .map(type => `${type}`)
|
---|
329 | .join(" | ")};\n`;
|
---|
330 | }
|
---|
331 | code += "\n";
|
---|
332 |
|
---|
333 | code += "export interface Aliases {\n";
|
---|
334 | for (const type in t.FLIPPED_ALIAS_KEYS) {
|
---|
335 | code += ` ${type}: ${type};\n`;
|
---|
336 | }
|
---|
337 | code += "}\n\n";
|
---|
338 |
|
---|
339 | code += lines.join("\n") + "\n";
|
---|
340 |
|
---|
341 | //
|
---|
342 |
|
---|
343 | process.stdout.write(code);
|
---|
344 |
|
---|
345 | //
|
---|
346 |
|
---|
347 | function areAllRemainingFieldsNullable(fieldName, fieldNames, fields) {
|
---|
348 | const index = fieldNames.indexOf(fieldName);
|
---|
349 | return fieldNames.slice(index).every(_ => isNullable(fields[_]));
|
---|
350 | }
|
---|
351 |
|
---|
352 | function hasDefault(field) {
|
---|
353 | return field.default != null;
|
---|
354 | }
|
---|
355 |
|
---|
356 | function isNullable(field) {
|
---|
357 | return field.optional || hasDefault(field);
|
---|
358 | }
|
---|
359 |
|
---|
360 | function sortFieldNames(fields, type) {
|
---|
361 | return fields.sort((fieldA, fieldB) => {
|
---|
362 | const indexA = t.BUILDER_KEYS[type].indexOf(fieldA);
|
---|
363 | const indexB = t.BUILDER_KEYS[type].indexOf(fieldB);
|
---|
364 | if (indexA === indexB) return fieldA < fieldB ? -1 : 1;
|
---|
365 | if (indexA === -1) return 1;
|
---|
366 | if (indexB === -1) return -1;
|
---|
367 | return indexA - indexB;
|
---|
368 | });
|
---|
369 | }
|
---|