UNPKG

eslint-codemod-utils

Version:

A collection of AST helper functions for more complex ESLint rule fixes.

83 lines (82 loc) 4.16 kB
import type { TSESTree } from '@typescript-eslint/types'; type BaseNode = TSESTree.Node; /** * Keys on `@typescript-eslint/types` AST nodes that are required at runtime * for nodes produced by a real parser, but which this library does not force * callers to supply when *constructing* a node for a lint fixer. * * Making these optional (recursively) in the library's internal view lets * consumers pass freshly-constructed partial nodes *and* full parser-produced * nodes interchangeably. */ type LocationKeys = 'loc' | 'range'; /** * Bookkeeping fields on TSESTree nodes that every node-factory helper in * this library either already provides a runtime default for, or where a * `false` / `[]` / `null` default is unambiguous. Marking these optional in * the helper input lets callers focus on semantic fields: * * ```ts * // Before: forced to declare every flag every time * property({ key, value, kind: 'init', method: false, computed: false, shorthand: false }) * * // After: the helper fills the defaults in * property({ key, value }) * ``` * * The list is deliberately broad — any field here MUST either: * (a) have a sensible, unambiguous "empty" default the helper can supply, * and the helper MUST actually supply it, or * (b) already be declared optional on the underlying TSESTree node (in * which case marking it optional again is a harmless no-op). * * The runtime-default obligation is the important part — see `nodes.ts` and * `jsx-nodes.ts` for the helpers that honour it. */ type DefaultableFields = 'optional' | 'async' | 'generator' | 'computed' | 'method' | 'shorthand' | 'kind' | 'static' | 'override' | 'declare' | 'readonly' | 'definite' | 'accessibility' | 'importKind' | 'exportKind' | 'assertions' | 'attributes' | 'selfClosing' | 'leadingComments' | 'closingElement' | 'children' | 'decorators' | 'typeParameters'; type LooseKeys = LocationKeys | DefaultableFields; export type EslintCodemodUtilsBaseNode = BaseNode; /** * A recursive transformer that loosens a TSESTree node for use in node-factory * helpers. It: * * 1. Drops `parent` — `TSESTree.BaseNode.parent` is self-referential, which * would cause `Loose<T>` to recurse infinitely. * 2. Makes `loc`/`range` optional at every depth, so constructed nodes don't * need synthetic location info. * 3. Makes every field in `DefaultableFields` optional at every depth, so * callers don't have to pass bookkeeping flags the helpers can default. * * Distributive conditional type semantics mean unions like * `TSESTree.Literal = StringLiteral | NumberLiteral | ...` still distribute * cleanly through `Loose<…>`, so discriminants are preserved. */ export type Loose<T> = T extends null | undefined | boolean | number | string | bigint | RegExp ? T : T extends readonly (infer U)[] ? readonly Loose<U>[] : T extends Array<infer U> ? Loose<U>[] : T extends object ? { [K in keyof T as K extends 'parent' | LooseKeys ? never : K]: Loose<T[K]>; } & { [K in Extract<keyof T, LooseKeys>]?: Loose<T[K]>; } : T; /** * A node shape where `type` is stripped (the helper supplies it) and every * `loc`/`range` is optional at every depth. * * This is the *input* shape accepted by every node helper. */ export type WithoutType<TBaseNode extends EslintCodemodUtilsBaseNode> = Omit<Loose<TBaseNode>, 'type'>; /** * The *output* shape of every node helper: a loosened version of the * underlying TSESTree node plus a `toString()` method for rendering to source. * * Because the output is also loosened, helpers can freely compose — the result * of `identifier(...)` can be passed as a child node to another helper without * forcing callers to supply synthetic `loc`/`range` fields. */ export type StringableASTNode<TBaseNode extends EslintCodemodUtilsBaseNode> = Loose<TBaseNode> & { toString(): string; }; export type StringableASTNodeFn<EstreeNodeType extends EslintCodemodUtilsBaseNode> = (node: WithoutType<EstreeNodeType>) => StringableASTNode<EstreeNodeType>; /** * @public */ export type ESLintNode = EslintCodemodUtilsBaseNode & Pick<TSESTree.BaseNode, 'parent'>; export {};