Helper functions for Flow file detection, AST processing, and rule implementation support used internally by the plugin and available for custom rule development.
Functions that determine whether files should be processed by Flow type checking rules.
function checkFlowFileAnnotation(ruleCreate: RuleCreateFunction): RuleCreateFunction;
function isFlowFile(context: ESLintContext): boolean;
function isNoFlowFile(context: ESLintContext): boolean;
function isFlowFileAnnotation(comment: string): boolean;
function isNoFlowFileAnnotation(comment: string, strict?: boolean): boolean;
type RuleCreateFunction = (context: ESLintContext) => RuleListener;Usage Examples:
// checkFlowFileAnnotation - wrapper for rules
const originalRule = {
create: (context) => ({
FunctionDeclaration(node) {
// Rule implementation
}
})
};
// Wrapped rule only runs on Flow files
const wrappedRule = {
...originalRule,
create: checkFlowFileAnnotation(originalRule.create)
};
// isFlowFile - check if current file has Flow annotation
function create(context) {
if (!isFlowFile(context)) {
return {}; // Skip non-Flow files
}
return {
TypeAnnotation(node) {
// Process Flow type annotations
}
};
}
// isFlowFileAnnotation - validate annotation format
isFlowFileAnnotation('// @flow'); // true
isFlowFileAnnotation('/* @flow */'); // true
isFlowFileAnnotation('// @flow strict'); // true
isFlowFileAnnotation('// @noflow'); // false
// isNoFlowFileAnnotation - validate @noflow annotation format
isNoFlowFileAnnotation('// @noflow'); // true
isNoFlowFileAnnotation('/* @noflow */'); // true
isNoFlowFileAnnotation('// @flow'); // false
isNoFlowFileAnnotation('// @noflow', true); // true - strict modeFunctions for navigating and extracting information from Abstract Syntax Tree nodes.
function getParameterName(parameter: Parameter): string;
function getTokenAfterParens(context: ESLintContext, node: ASTNode): Token | null;
function getTokenBeforeParens(context: ESLintContext, node: ASTNode): Token | null;
function iterateFunctionNodes(node: ASTNode, callback: (functionNode: FunctionNode) => void): void;
interface Parameter {
type: string;
name?: string;
left?: Parameter; // For destructuring
key?: ASTNode; // For object patterns
}
interface Token {
type: string;
value: string;
range: [number, number];
}Usage Examples:
// getParameterName - extract parameter names from complex patterns
getParameterName({type: 'Identifier', name: 'user'}); // 'user'
getParameterName({type: 'RestElement', argument: {name: 'args'}}); // 'args'
getParameterName({type: 'ObjectPattern', properties: [...]}); // 'object'
// getTokenAfterParens - find token after parentheses
function processFunction(context, node) {
const tokenAfter = getTokenAfterParens(context, node);
if (tokenAfter && tokenAfter.value === ':') {
// Function has return type annotation
}
}
// iterateFunctionNodes - process all function nodes in AST
function create(context) {
return {
Program(programNode) {
iterateFunctionNodes(programNode, (functionNode) => {
// Process each function in the program
validateFunctionTypeAnnotations(functionNode);
});
}
};
}Functions for string manipulation and matching used in rule implementations.
function fuzzyStringMatch(needle: string, haystack: string): boolean;
function quoteName(name: string): string;
interface StringMatchOptions {
caseSensitive?: boolean;
threshold?: number;
}Usage Examples:
// fuzzyStringMatch - find approximate string matches
fuzzyStringMatch('funcion', 'function'); // true - close match
fuzzyStringMatch('lenght', 'length'); // true - common typo
fuzzyStringMatch('abc', 'xyz'); // false - no match
// Used in rule suggestions:
function create(context) {
return {
Identifier(node) {
if (node.name === 'lenght') {
context.report({
node,
message: `Unknown identifier "${node.name}". Did you mean "length"?`,
fix: (fixer) => fixer.replaceText(node, 'length')
});
}
}
};
}
// quoteName - add quotes around identifiers when needed
quoteName('validName'); // 'validName'
quoteName('invalid-name'); // '"invalid-name"'
quoteName('123invalid'); // '"123invalid"'
quoteName('class'); // '"class"' (reserved word)Utility functions for consistent spacing and formatting fixes.
const spacingFixers = {
stripSpacesBefore: (node: ASTNode, spaces: number) => (fixer: RuleFixer) => Fix;
stripSpacesAfter: (node: ASTNode, spaces: number) => (fixer: RuleFixer) => Fix;
addSpaceBefore: (node: ASTNode) => (fixer: RuleFixer) => Fix;
addSpaceAfter: (node: ASTNode) => (fixer: RuleFixer) => Fix;
replaceWithSpaceBefore: (node: ASTNode, spaces: number) => (fixer: RuleFixer) => Fix;
replaceWithSpaceAfter: (node: ASTNode, spaces: number) => (fixer: RuleFixer) => Fix;
stripSpaces: (direction: 'before' | 'after', node: ASTNode, spaces: number) => (fixer: RuleFixer) => Fix;
addSpace: (direction: 'before' | 'after', node: ASTNode) => (fixer: RuleFixer) => Fix;
replaceWithSpace: (direction: 'before' | 'after', node: ASTNode, spaces: number) => (fixer: RuleFixer) => Fix;
};
interface Fix {
range: [number, number];
text: string;
}Usage Examples:
// spacingFixers - consistent spacing fixes
function create(context) {
return {
TypeAnnotation(node) {
const colonToken = context.getSourceCode().getTokenBefore(node);
if (needsSpaceAfterColon(colonToken)) {
context.report({
node,
message: 'Missing space after type colon',
fix: spacingFixers.addSpaceAfter(colonToken)
});
}
if (hasExtraSpaceBefore(colonToken)) {
context.report({
node,
message: 'Extra space before type colon',
fix: spacingFixers.stripSpacesBefore(colonToken, 1)
});
}
}
};
}Function for accessing built-in ESLint rules for extension or modification.
function getBuiltinRule(ruleName: string): ESLintRule | null;
interface ESLintRule {
create: RuleCreateFunction;
meta?: RuleMeta;
}Usage Examples:
// getBuiltinRule - extend existing ESLint rules
function create(context) {
const baseRule = getBuiltinRule('no-unused-expressions');
if (!baseRule) {
return {};
}
// Create base rule visitor
const baseVisitor = baseRule.create(context);
// Extend with Flow-specific logic
return {
...baseVisitor,
TypeAnnotation(node) {
// Additional Flow-specific validation
if (isUnusedTypeAnnotation(node)) {
context.report({
node,
message: 'Unused type annotation'
});
}
}
};
}How to use utilities when developing custom Flow type checking rules.
interface CustomRuleOptions {
checkFlowFiles?: boolean;
allowPatterns?: string[];
severity?: 'error' | 'warn';
}Usage Examples:
// Complete custom rule using utilities
const customFlowRule = {
meta: {
docs: {
description: 'Custom Flow type validation',
category: 'Possible Errors'
},
fixable: 'code',
schema: [/* options schema */]
},
create: checkFlowFileAnnotation((context) => {
// Only runs on Flow files due to wrapper
return {
FunctionDeclaration(node) {
// Use utilities for processing
iterateFunctionNodes(node, (funcNode) => {
funcNode.params.forEach((param) => {
const paramName = getParameterName(param);
if (needsTypeAnnotation(param)) {
const suggestion = fuzzyStringMatch(paramName, 'string') ?
'string' : 'any';
context.report({
node: param,
message: `Parameter "${paramName}" needs type annotation`,
fix: (fixer) => {
const quotedName = quoteName(paramName);
return fixer.insertTextAfter(param, `: ${suggestion}`);
}
});
}
});
});
}
};
})
};interface UtilityFunction {
(...args: any[]): any;
}
interface ESLintContext {
getFilename(): string;
getSourceCode(): SourceCode;
report(descriptor: ReportDescriptor): void;
options: any[];
settings: {
flowtype?: FlowtypeSettings;
};
}
interface SourceCode {
getText(node?: ASTNode): string;
getTokenBefore(node: ASTNode): Token | null;
getTokenAfter(node: ASTNode): Token | null;
getCommentsBefore(node: ASTNode): Comment[];
getCommentsAfter(node: ASTNode): Comment[];
}
interface Comment {
type: 'Line' | 'Block';
value: string;
range: [number, number];
}
interface RuleListener {
[nodeType: string]: (node: ASTNode) => void;
}
interface FunctionNode extends ASTNode {
id: Identifier | null;
params: Parameter[];
body: BlockStatement;
returnType?: TypeAnnotation;
}