@adguard/aglint
Version:
Universal adblock filter list linter.
287 lines (286 loc) • 9.58 kB
TypeScript
import { type Struct } from 'superstruct';
import { type Value, type AdblockSyntax, type AnyRule, type Node } from '@adguard/agtree';
import { type AnySeverity } from './severity';
import { type CssTreeParsingContext, type CssTreeParsingContextToNode } from './helpers/css-tree-types';
/**
* Represents any linter rule
*/
export type AnyLinterRule = LinterRule<any, any>;
/**
* Type for rule configuration object
*/
export type LinterRuleConfigObject = {
[key: string]: LinterRuleConfig;
};
/**
* Type definition for the linter rule config, which can be:
* - severity itself (number or string): `severity`
* - one-element array with severity as the first element: `[severity]`
* - n-element array with severity as the first element and other options as the rest: `[severity, ...options]`
*/
export type LinterRuleConfig = AnySeverity | LinterRuleConfigArray;
/**
* Type definition for the linter rule config array, which can be:
* - one-element array with severity as the first element: `[severity]`
* - n-element array with severity as the first element and other options as the rest: `[severity, ...options]`
*/
export type LinterRuleConfigArray = [AnySeverity, ...unknown[]];
/**
* Represents the metadata of a linter rule configuration
*/
export interface LinterRuleConfigMeta {
/**
* Default configuration of the rule
*/
default: unknown;
/**
* Superstruct schema for the rule configuration (used for validation)
*/
schema: Struct;
}
/**
* Represents the metadata of a linter rule
*/
export interface LinterRuleMeta {
/**
* Linter rule severity. It can be off, warn, error or fatal.
*/
severity: AnySeverity;
/**
* Configuration metadata (if the rule has any configuration)
*/
config?: LinterRuleConfigMeta;
}
/**
* Represents what events a linter rule can handle
*/
export interface LinterRuleEvents<StorageType = LinterRuleStorage<unknown>, ConfigType = unknown> {
/**
* Called before analyzing a filter list (before any rule is analyzed).
* You can retrieve the filter list and other necessary information from the context.
*
* @param context - Linter context
*/
onStartFilterList?: (context: GenericRuleContext<StorageType, ConfigType>) => void;
/**
* Called after analyzing a filter list (after all rules are analyzed).
* You can retrieve the filter list and other necessary information from the context.
*
* @param context - Linter context
*/
onEndFilterList?: (context: GenericRuleContext<StorageType, ConfigType>) => void;
/**
* Called when analyzing an adblock rule. This event is called for each rule, including comments.
* You can retrieve the adblock rule and other necessary information from the context.
*
* @param context - Linter context
*/
onRule?: (context: SpecificRuleContext<StorageType, ConfigType>) => void;
}
/**
* Represents an AGLint rule
*/
export interface LinterRule<StorageType = LinterRuleStorage<unknown>, ConfigType = unknown> {
/**
* Basic data of the rule (metadata)
*/
meta: LinterRuleMeta;
/**
* Events belonging to the rule
*/
events: LinterRuleEvents<StorageType, ConfigType>;
}
/**
* Represents the core linter configuration
*/
export interface LinterConfig {
/**
* Root configuration flag. If it's value is true, the configuration is root configuration.
*/
root?: boolean;
/**
* Whether to allow inline configuration or not
*
* @example
* ```adblock
* ! aglint-disable-next-line
* ```
*/
allowInlineConfig?: boolean;
/**
* An array of configuration presets to extend
*/
extends?: string[];
/**
* Specific adblock syntaxes to use for network rule modifiers validation.
* Array of strings:
* - `['Common']` (default)
* - or any combination of `['AdGuard', 'uBlockOrigin', 'AdblockPlus']`.
*
* Can be set in the configuration file
* or in the filter list as `Agent` type comment.
*
*/
syntax: AdblockSyntax[];
/**
* A map of rule names to their configuration
*/
rules?: LinterRuleConfigObject;
}
/**
* Type definition for the linter rule context getter function.
*
* @param rawValueNode The raw value node.
* @param context The context, see {@link CssTreeParsingContext}.
*
* @returns The CSS node or `null` if the CSS could not be parsed.
*/
type CssNodeGetter = <T extends CssTreeParsingContext>(rawValueNode: Value<string>, context: T) => CssTreeParsingContextToNode[T] | null;
/**
* Represents a linter context that is passed to the rules when their events are triggered
*/
export interface GenericRuleContext<StorageType = LinterRuleStorage<unknown>, ConfigType = unknown> {
/**
* Returns the clone of the shared linter configuration.
*
* @returns The shared linter configuration
*/
getLinterConfig: () => LinterConfig;
/**
* Returns the raw content of the adblock filter list currently processed by the linter.
*
* @returns The raw adblock filter list content
*/
getFilterListContent: () => string;
/**
* Returns whether a fix was requested from the linter. This is an optimization
* for the linter, so it doesn't have to run the fixer if it's not needed.
*
* @returns `true` if fix is needed, `false` otherwise
*/
fixingEnabled: () => boolean;
/**
* Storage for storing data between events. This storage is only visible to the rule.
*/
storage: StorageType;
/**
* Additional config for the rule. This is unknown at this point, but the concrete
* type is defined by the rule.
*/
config: ConfigType;
/**
* Function for reporting problems to the linter.
*
* @param problem - The problem to report
*/
report: (problem: LinterProblemReport) => void;
/**
* Returns the CSS node for the given raw value node and context.
*
* @param rawValueNode - The raw value node
* @param context - The context, see {@link CssTreeParsingContext}.
* For more information, please check https://github.com/csstree/csstree/blob/master/docs/parsing.md#context
*
* @returns The CSS node or `null` if the CSS could not be parsed
*
* @note When you call this function from a rule and it cannot parse the CSS,
* it will automatically report a problem to the linter and marks your linter rule as the source.
* Reported problem will have the same severity as the rule.
*/
getCssNode: CssNodeGetter;
}
/**
* Specialized linter context for the rule that is triggered when analyzing an adblock rule
*/
export interface SpecificRuleContext<StorageType = LinterRuleStorage<unknown>, ConfigType = unknown> extends GenericRuleContext<StorageType, ConfigType> {
/**
* Returns the AST of the adblock rule currently being iterated by the linter.
*
* @returns The actual adblock rule as AST
*/
getActualAdblockRuleAst: () => AnyRule;
/**
* Returns the raw version of the adblock rule currently being iterated by the linter.
*
* @returns The actual adblock rule as original string
*/
getActualAdblockRuleRaw: () => string;
/**
* Returns the line number that the linter is currently iterating.
*
* @returns The actual line number
*/
getActualLine: () => number;
}
/**
* Represents the location of a problem that detected by the linter
*/
export interface LinterPosition {
/**
* 1-based line number of the problem
*/
startLine: number;
/**
* 0-based column number of the problem
*/
startColumn: number;
/**
* 1-based line number of the problem
*/
endLine: number;
/**
* 0-based column number of the problem
*/
endColumn: number;
}
/**
* Represents a problem report (this must be passed to context.report from the rules)
*/
export interface LinterProblemReport {
/**
* Text description of the problem
*/
message: string;
/**
* Node that caused the problem. If provided, the linter will use its offsets to determine the problem location.
*/
node?: Node;
/**
* Relative start offset to the start of the node that caused the problem.
* Useful when you do not want to mark the whole node as problematic.
*
* @note Only takes effect when `node` is provided.
*/
relativeNodeStartOffset?: number;
/**
* Relative start offset to the start of the node that caused the problem.
* Useful when you do not want to mark the whole node as problematic.
*
* @note Only takes effect when `node` is provided.
*/
relativeNodeEndOffset?: number;
/**
* The location of the problem
*/
position?: LinterPosition;
/**
* Suggested fix for the problem
*/
fix?: AnyRule | AnyRule[];
/**
* Severity of the problem, overrides the rule severity set in the meta property.
* Needed for modifiers validation, e.g. deprecated modifiers which are still supported
* but will not be supported in the future, so they are not recommended to use.
*/
customSeverity?: AnySeverity;
}
/**
* Represents a linter rule storage object that is passed as reference to
* the rules when their events are triggered.
*
* Basically used internally by the linter, so no need to export this.
*/
export interface LinterRuleStorage<T = unknown> {
[key: string]: T;
}
export {};