|
| 1 | +import type { TSESTree as T, TSESLint, TSESTree } from "@typescript-eslint/utils"; |
| 2 | +import { FunctionNode, ProgramOrFunctionNode } from "../../utils"; |
| 3 | + |
| 4 | +type PathSegment = `[${number}]`; |
| 5 | +export type ExprPath = PathSegment; // PathSegment | `${PathSegment}${Path}`; |
| 6 | + |
| 7 | +export interface ReactivityPluginApi { |
| 8 | + /** |
| 9 | + * Mark a node as a function in which reactive variables may be polled (not tracked). Can be |
| 10 | + * async. |
| 11 | + */ |
| 12 | + calledFunction(node: T.Node): void; |
| 13 | + /** |
| 14 | + * Mark a node as a tracked scope, like `createEffect`'s callback or a JSXExpressionContainer. |
| 15 | + */ |
| 16 | + trackedScope(node: T.Node): void; |
| 17 | + /** |
| 18 | + * Mark a node as a function that will be synchronously called in the current scope, like |
| 19 | + * Array#map callbacks. |
| 20 | + */ |
| 21 | + syncCallback(node: FunctionNode): void; |
| 22 | + |
| 23 | + /** |
| 24 | + * Alter the error message within a scope to provide additional details. |
| 25 | + */ |
| 26 | + provideErrorContext(node: T.Node, errorMessage: string): void; |
| 27 | + |
| 28 | + /** |
| 29 | + * Mark that a node evaluates to a signal (or memo, etc.). |
| 30 | + */ |
| 31 | + signal(node: T.Node, path?: ExprPath): void; |
| 32 | + |
| 33 | + /** |
| 34 | + * Mark that a node evaluates to a store or props. |
| 35 | + */ |
| 36 | + store(node: T.Node, path?: ExprPath, options?: { mutable?: boolean }): void; |
| 37 | + |
| 38 | + /** |
| 39 | + * Mark that a node is reactive in some way--either a signal-like or a store-like. |
| 40 | + */ |
| 41 | + reactive(node: T.Node, path?: ExprPath): void; |
| 42 | + |
| 43 | + /** |
| 44 | + * Convenience method for checking if a node is a call expression. If `primitive` is provided, |
| 45 | + * checks that the callee is a Solid import with that name (or one of that name), handling aliases. |
| 46 | + */ |
| 47 | + isCall(node: T.Node, primitive?: string | Array<string>): node is T.CallExpression; |
| 48 | +} |
| 49 | + |
| 50 | +export interface ReactivityPlugin { |
| 51 | + package: string; |
| 52 | + create: (api: ReactivityPluginApi) => TSESLint.RuleListener; |
| 53 | +} |
| 54 | + |
| 55 | +// Defeats type widening |
| 56 | +export function plugin(p: ReactivityPlugin): ReactivityPlugin { |
| 57 | + return p; |
| 58 | +} |
| 59 | + |
| 60 | +// =========================== |
| 61 | + |
| 62 | +class Reactivity implements ReactivityPluginApi { |
| 63 | + trackedScope(node: T.Node): void { |
| 64 | + throw new Error("Method not implemented."); |
| 65 | + } |
| 66 | + calledFunction(node: T.Node): void { |
| 67 | + throw new Error("Method not implemented."); |
| 68 | + } |
| 69 | + trackedFunction(node: T.Node): void { |
| 70 | + throw new Error("Method not implemented."); |
| 71 | + } |
| 72 | + trackedExpression(node: T.Node): void { |
| 73 | + throw new Error("Method not implemented."); |
| 74 | + } |
| 75 | + syncCallback(node: T.Node): void { |
| 76 | + throw new Error("Method not implemented."); |
| 77 | + } |
| 78 | + provideErrorContext(node: T.Node, errorMessage: string): void { |
| 79 | + throw new Error("Method not implemented."); |
| 80 | + } |
| 81 | + signal(node: T.Node, path?: `[${number}]` | undefined): void { |
| 82 | + throw new Error("Method not implemented."); |
| 83 | + } |
| 84 | + store(node: T.Node, path?: `[${number}]` | undefined): void { |
| 85 | + throw new Error("Method not implemented."); |
| 86 | + } |
| 87 | + reactive(node: T.Node, path?: `[${number}]` | undefined): void { |
| 88 | + throw new Error("Method not implemented."); |
| 89 | + } |
| 90 | + isCall(node: T.Node, primitive?: string | string[] | undefined): node is T.CallExpression { |
| 91 | + throw new Error("Method not implemented."); |
| 92 | + } |
| 93 | +} |
0 commit comments