TSSLint `.at()` Method: Advanced Rule Debugging and Stack Trace Customization
The .at() method captures error objects and stack frame indices to attach detailed debug information to diagnostics, enabling precise tracing of rule failures back to their source code location.
The tsslint repository provides a flexible linting framework that prioritizes developer experience when debugging complex rules. The .at() method on the Reporter object allows rule authors to preserve execution context and customize how stack traces appear in diagnostic output, making it an essential tool for advanced rule debugging.
How the .at() Method Works in TSSLint Core
The .at() method is a chainable modifier available on the Reporter object that every rule receives via the report() helper in packages/core/index.ts. When invoked, it stores an error reference and stack depth index that the framework uses to generate rich diagnostic information.
The Reporter Chain Architecture
When a rule reports a diagnostic, the report(message, start, end) function returns a Reporter instance that supports method chaining. The .at(err, stackIndex) modifier accepts two parameters:
err: AnErrorobject (either the original thrown error or a newly created one) whose stack trace will be preservedstackIndex: A number indicating which frame in the stack trace should be treated as the entry point for the diagnostic
This design enables fluent API usage:
report('Avoid using eval()', evalPos, evalPos + 4)
.at(err, 0)
.asError()
.withFix('Remove eval', () => [{ /* edits */ }]);
Internal Storage and Lazy Evaluation
Internally, the Reporter stores the error and stack index as a tuple [Error, number] in a private variable called location. When the diagnostic's relatedInformation property is accessed, it lazily invokes the repository-provided getRelatedInformations(location[0], location[1]) function.
This lazy evaluation, implemented in packages/core/index.ts, ensures that stack processing only occurs when diagnostics are actually rendered, optimizing performance for rules that may generate many reports. The resulting ts.DiagnosticRelatedInformation[] objects are then attached to the diagnostic, surfacing the stack trace in editors and CLI output.
Advanced Debugging Scenarios
The .at() method enables several critical debugging workflows that distinguish TSSLint from simpler linting frameworks.
Capturing Rule Execution Failures
When a rule throws an exception during execution, the core framework catches the error and uses .at() to preserve the original stack trace. In the execution wrapper found in the core implementation:
try {
rule(rulesContext);
} catch (err) {
if (err instanceof Error) {
report(err.stack ?? err.message, 0, 0).at(err, 0);
}
}
This pattern ensures that developers see exactly where a rule failed, rather than receiving a generic error message.
Handling Non-Type-Aware Mode Errors
When rules execute without type information and encounter errors, the framework uses a specific technique to capture full diagnostic context:
report(String(err), 0, 0).at(new Error(), Number.MAX_VALUE);
By passing Number.MAX_VALUE as the stackIndex, the core forces getRelatedInformations to include the complete stack trace. This aids in debugging configuration issues or plugin compatibility problems that only surface in specific execution modes.
TSLint Compatibility Layer Integration
The compatibility shim in packages/config/lib/tslint.ts also leverages .at() to handle failures from legacy TSLint rules:
// In packages/config/lib/tslint.ts
report(failureMessage, 0, 0).at(new Error(), Number.MAX_VALUE);
This ensures consistent stack trace handling across both native TSSLint rules and migrated TSLint codebases.
Key Implementation Files
The .at() functionality spans several packages in the monorepo:
packages/core/index.ts– Defines the Reporter class and the.at()method implementationpackages/types/index.ts– Declares the Reporter interface and type signaturespackages/typescript-plugin/index.ts– Creates the linter instance and forwardsgetRelatedInformationscallbackspackages/cli/lib/worker.ts– Provides the concretegetRelatedInformationsimplementation that parses stack frames into diagnostic objectspackages/config/lib/tslint.ts– Uses.at()for error handling in the TSLint compatibility layer
Practical Code Examples
Creating Diagnostics with Custom Stack Context
Rule authors can manually create error objects to provide specific debugging context:
export default (ctx: RuleContext) => {
const { report } = ctx;
const source = ctx.file.getFullText();
const evalPos = source.indexOf('eval(');
if (evalPos !== -1) {
const err = new Error('Avoid eval');
report('Avoid using eval()', evalPos, evalPos + 4)
.at(err, 0)
.asError()
.withFix('Remove eval', () => [{
span: { start: evalPos, length: 4 },
newText: '',
}]);
}
};
Processing Stack Frames for Display
The getRelatedInformations callback, typically implemented in the CLI worker, transforms captured errors into diagnostic-related information:
function getRelatedInformations(err: Error, stackIndex: number) {
const frames = err.stack?.split('\n').slice(stackIndex + 1) ?? [];
return frames.map(frame => ({
messageText: frame.trim(),
file: { /* file location extraction logic */ },
start: 0,
length: 0,
}));
}
This function extracts frames starting from the specified index, allowing editors to display a clean breadcrumb trail back to the rule's source.
Summary
- The
.at(err, stackIndex)method modifies Reporter instances to capture error objects and specific stack frame indices - Error data is stored as a
[Error, number]tuple and processed lazily whenrelatedInformationis accessed - The method enables chaining with other modifiers like
asError()andwithFix()for fluent diagnostic construction Number.MAX_VALUEas astackIndexforces inclusion of the complete stack trace for deep debugging scenarios- Core implementation resides in
packages/core/index.tswith platform-specificgetRelatedInformationshandling inpackages/cli/lib/worker.ts
Frequently Asked Questions
What parameters does the .at() method accept?
The .at() method accepts two parameters: an Error object containing the stack trace to preserve, and a stackIndex number indicating which frame represents the diagnostic entry point. When the stack index is 0, the error's capture point is used; when set to Number.MAX_VALUE, the entire stack trace is included.
How does .at() interact with relatedInformation?
The .at() method stores the error and stack index internally, but the actual ts.DiagnosticRelatedInformation objects are generated lazily. When an IDE or CLI accesses the diagnostic's relatedInformation property, the core invokes the getRelatedInformations callback with the stored error and index, transforming raw stack frames into structured diagnostic objects.
Can .at() be chained with other Reporter methods?
Yes, .at() returns the same Reporter instance, enabling method chaining with other modifiers like asError(), asWarning(), and withFix(). This fluent API allows concise construction of complex diagnostics: report(msg, start, end).at(err, 0).asError().withFix(title, edits).
When should rule authors use Number.MAX_VALUE as the stack index?
Use Number.MAX_VALUE when you need to capture the complete stack trace without filtering specific frames. This is particularly useful when reporting errors from unexpected exceptions where the exact failure point is unknown, or when debugging configuration issues where every frame provides valuable context for tracing the execution path.
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 →