Files
2026-07-13 13:22:34 +08:00

83 lines
2.9 KiB
TypeScript

import type { TSESTree } from '@typescript-eslint/utils';
import { ASTUtils } from '@typescript-eslint/utils';
import type { RuleContext } from '@typescript-eslint/utils/ts-eslint';
type ReferenceTrackerInstance = InstanceType<typeof ASTUtils.ReferenceTracker>;
type NodeIterator = IterableIterator<TSESTree.Node>;
export type FunctionImport = {
/** Path of the module imported from. e.g. 'react' or './components/MyComponent' */
module: string;
/**
* Name of the specific function imported from the module. e.g. 'useState' from React. Or, if the function
* being called is the default export of the module, use DEFAULT_EXPORT
*/
functionName: string;
};
export function getReferenceTracker(context: RuleContext<string, unknown[]>): ReferenceTrackerInstance {
return new ASTUtils.ReferenceTracker(context.sourceCode.getScope(context.sourceCode.ast));
}
/**
* For the given named import of a function, returns an iterator of all the function call nodes that call that function,
* regardless of they way they were imported or whether they were renamed.
*
* @param tracker A ReferenceTracker instance
* @param importConfig The name of the module and import to track
* @returns An iterator of ESTree Nodes
*/
export function* findFunctionCalls(tracker: ReferenceTrackerInstance, importConfig: FunctionImport): NodeIterator {
const traceMap = {
[importConfig.module]: {
// Specifies that we're looking at an ES Module (all our TypeScript files are ES modules)
[ASTUtils.ReferenceTracker.ESM]: true,
// Named imports: e.g. `import { useState } from 'react'`
[importConfig.functionName]: {
[ASTUtils.ReferenceTracker.CALL]: true,
},
// Default imports: `import React from 'react'`, or namespace imports: `import * as React from 'react'`
default: {
[importConfig.functionName]: {
[ASTUtils.ReferenceTracker.CALL]: true,
},
},
},
} as const;
for (const { node } of tracker.iterateEsmReferences(traceMap)) {
yield node;
}
}
/**
* For the default import of a given module, returns an iterator of all the nodes that call that value.
*
* @param tracker A ReferenceTracker instance
* @param importConfig The name of the imported module
* @returns An iterator of ESTree Nodes
*/
export function* findFunctionCallsForDefaultExport(
tracker: ReferenceTrackerInstance,
importConfig: Omit<FunctionImport, 'functionName'>,
): NodeIterator {
const traceMap = {
[importConfig.module]: {
// Specifies that we're looking at an ES Module (all our TypeScript files are ES modules)
[ASTUtils.ReferenceTracker.ESM]: true,
// Default imports: `import React from 'react'`, or namespace imports: `import * as React from 'react'`
default: {
[ASTUtils.ReferenceTracker.CALL]: true,
},
},
} as const;
for (const { node } of tracker.iterateEsmReferences(traceMap)) {
yield node;
}
}