How to Build Custom TSSLint Plugins Using resolveRules, resolveDiagnostics, and resolveCodeFixes Hooks
TSSLint plugins implement three optional lifecycle hooks—resolveRules to modify rule sets before execution, resolveDiagnostics to filter or transform linting errors, and resolveCodeFixes to inject custom quick-fix actions—enabling deep customization of the linting pipeline.
TSSLint is a TypeScript linter built on the TypeScript compiler API that emphasizes extensibility through plugins. When you build custom TSSLint plugins, you tap into three powerful hooks defined in the PluginInstance interface that let you intercept and modify rules, diagnostics, and code fixes at specific stages of the linting lifecycle.
Understanding the TSSLint Plugin Architecture
A TSSLint plugin is a function that receives a LinterContext and returns a PluginInstance. The PluginInstance interface, defined in packages/types/index.ts, optionally exposes three methods that the core engine invokes at different stages:
resolveRules?: Intercepts the rule map after initial assemblyresolveDiagnostics?: Processes diagnostics after rule executionresolveCodeFixes?: Augments code fixes before presentation
These hooks are declared at lines 30-33 in packages/types/index.ts.
The Three Core Plugin Hooks
resolveRules – Modifying Rules Before Execution
The resolveRules hook runs after the base rule set for a file is assembled but before any rule executes. It receives the source file name and the current rule map, allowing plugins to inject, replace, or remove rules dynamically.
According to the source code in packages/core/index.ts (lines 13-16), the engine iterates over each plugin's resolveRules method and applies the transformed rule map immediately.
resolveDiagnostics – Filtering and Transforming Diagnostics
After all rules execute and produce diagnostics, the resolveDiagnostics hook allows plugins to filter, merge, or enrich the error array before it reaches the editor or CLI. The hook receives the SourceFile and an array of DiagnosticWithLocation objects.
The core engine invokes this hook at lines 171-176 in packages/core/index.ts, passing the accumulated diagnostics through each plugin's implementation in sequence.
resolveCodeFixes – Adding Custom Quick Fixes
When a diagnostic is about to be presented with code-fix suggestions, the resolveCodeFixes hook fires. It receives the SourceFile, the specific Diagnostic being processed, and the array of CodeFixAction objects generated by rules. Plugins can append custom fixes, remove unwanted ones, or modify existing suggestions.
This hook is triggered at lines 347-350 in packages/core/index.ts for every diagnostic that has associated code fixes.
Step-by-Step Implementation Examples
Creating a Plugin That Injects Rules (resolveRules)
To dynamically add a custom rule to every file, implement resolveRules in your plugin:
// plugins/console-plugin.ts
import NoConsoleRule from '../rules/no-console.ts';
import { definePlugin } from '@tsslint/config';
export default definePlugin(() => ({
resolveRules(fileName, rules) {
// Inject no-console rule into the rule map
rules['no-console'] = NoConsoleRule;
return rules;
},
}));
Filtering Diagnostics by Message Content (resolveDiagnostics)
Suppress specific diagnostics based on message content using resolveDiagnostics:
// plugins/ignore-errors.ts
import { definePlugin } from '@tsslint/config';
export default definePlugin(() => ({
resolveDiagnostics(file, diagnostics) {
// Remove diagnostics containing "generated"
return diagnostics.filter(d =>
!d.messageText.toString().includes('generated')
);
},
}));
Adding an Ignore Comment Quick Fix (resolveCodeFixes)
Provide a quick-fix action that inserts an ignore comment:
// plugins/quick-ignore.ts
import { definePlugin } from '@tsslint/config';
export default definePlugin(() => ({
resolveCodeFixes(file, diagnostic, codeFixes) {
if (diagnostic.source !== 'tsslint' || diagnostic.start === undefined) {
return codeFixes;
}
const line = file.getLineAndCharacterOfPosition(diagnostic.start).line;
const comment = `// tsslint-ignore ${diagnostic.code}\n`;
codeFixes.push({
fixName: 'tsslint-ignore',
description: 'Add tsslint-ignore comment',
changes: [{
fileName: file.fileName,
textChanges: [{
newText: comment,
span: {
start: file.getPositionOfLineAndCharacter(line, 0),
length: 0
},
}],
}],
});
return codeFixes;
},
}));
Registering Plugins in tsslint.config.ts
Combine your plugins in the configuration file:
// tsslint.config.ts
import { defineConfig } from '@tsslint/config';
import consolePlugin from './plugins/console-plugin.ts';
import ignorePlugin from './plugins/ignore-errors.ts';
import quickIgnore from './plugins/quick-ignore.ts';
export default defineConfig({
plugins: [
consolePlugin, // Injects rules
ignorePlugin, // Filters diagnostics
quickIgnore, // Adds quick fixes
],
});
When the above configuration is loaded, the core engine at packages/core/index.ts will:
- Call
consolePlugin.resolveRules(lines 13-16) to modify the rule map - Execute rules and collect diagnostics
- Pass diagnostics through
ignorePlugin.resolveDiagnostics(lines 171-176) - For each remaining diagnostic, invoke
quickIgnore.resolveCodeFixes(lines 347-350) to augment available fixes
Using Built-in Helpers for Common Patterns
For the common use case of adding ignore comments, TSSLint provides a built-in helper that encapsulates the resolveDiagnostics and resolveCodeFixes logic:
import { defineConfig, createIgnorePlugin } from '@tsslint/config';
export default defineConfig({
plugins: [
// Automatically adds // @tsslint-ignore comments
createIgnorePlugin(/\/\/\s*@tsslint-ignore/g, true),
],
});
The createIgnorePlugin implementation, located in packages/config/lib/plugins/ignore.ts (lines 48-72), demonstrates production-grade usage of both resolveDiagnostics and resolveCodeFixes hooks to suppress diagnostics and generate the corresponding ignore comments.
Summary
- TSSLint plugins are functions returning a
PluginInstancethat can implement three optional hooks to intercept the linting pipeline. resolveRules(invoked atpackages/core/index.tslines 13-16) allows dynamic injection or modification of rules before execution.resolveDiagnostics(invoked at lines 171-176) enables filtering, merging, or enriching diagnostic messages after rule execution.resolveCodeFixes(invoked at lines 347-350) permits customization of quick-fix actions presented to users.- Plugins are registered via the
pluginsarray intsslint.config.tsusingdefineConfigfrom@tsslint/config.
Frequently Asked Questions
What is the execution order of TSSLint plugin hooks?
The hooks execute sequentially as the linting pipeline progresses. First, resolveRules runs after the base rule set is assembled but before any rules execute. Next, resolveDiagnostics processes the results after all rules have finished. Finally, resolveCodeFixes runs on-demand when the editor or CLI requests fixes for specific diagnostics.
Can a plugin modify diagnostics generated by other plugins?
Yes. Because resolveDiagnostics receives the accumulated diagnostic array after all rules have executed, your plugin can filter, modify, or remove diagnostics regardless of which rule or plugin produced them. The same applies to resolveCodeFixes, where you can augment or replace fixes provided by other plugins.
How do I access the TypeScript compiler API inside a plugin?
The LinterContext passed to your plugin function contains the typescript object (the TypeScript module) and the current program. You can destructure these from the context parameter to access the full compiler API for advanced type checking or AST manipulation.
Is it possible to suppress specific diagnostics using the built-in helpers?
Yes. The @tsslint/config package exports createIgnorePlugin, which encapsulates the logic for both suppressing diagnostics matching a specific pattern and generating the corresponding ignore comments. This helper uses the resolveDiagnostics and resolveCodeFixes hooks internally, as shown in packages/config/lib/plugins/ignore.ts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →