# How TSSLint’s Related Information Feature Captures Stack Traces for One‑Click Rule Source Navigation

> Discover how TSSLint captures stack traces with DiagnosticRelatedInformation for one-click rule source navigation in your code editor. Improve your debugging workflow.

- Repository: [Johnson Chu/tsslint](https://github.com/johnsoncodehk/tsslint)
- Tags: internals
- Published: 2026-03-04

---

**TSSLint enriches every lint diagnostic with `DiagnosticRelatedInformation` objects that point directly to the source location where a rule was defined, enabling editors to render clickable "Go to rule source" links.**

The `johnsoncodehk/tsslint` repository implements a sophisticated pipeline that transforms runtime JavaScript stack traces into editor-navigable metadata. By leveraging the TypeScript language service’s diagnostic API, TSSLint allows developers to jump from a lint warning immediately to the implementation of the rule that raised it.

## How the TSSLint Related Information Pipeline Works

The flow operates in three distinct stages: capturing the error stack, converting frames to TypeScript’s related-information format, and lazily attaching that data to diagnostics.

### Stage 1: Capturing the Error Stack

When a rule throws an exception or when TSSLint needs to record the rule’s definition site, it instantiates an `Error` and parses its stack trace using the **`error-stack-parser`** library. This extracts structured frames containing file names, line numbers, and column numbers.

In [`packages/typescript-plugin/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/typescript-plugin/index.ts), the plugin parses the error at lines 177–179:

```typescript
const stacks = ErrorStackParser.parse(err);
const relatedInfo = createRelatedInformation(ts, stacks[stackIndex]);

```

The `stackIndex` parameter allows rule authors to specify which frame in the stack represents the relevant rule source—typically skipping internal TSSLint frames to point at the user’s rule implementation.

### Stage 2: Converting Stack Frames to DiagnosticRelatedInformation

The `createRelatedInformation` function (defined at lines 215–255 in [`packages/typescript-plugin/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/typescript-plugin/index.ts)) transforms a single `StackFrame` into a `ts.DiagnosticRelatedInformation` object. This process involves several precise steps:

1. **File path normalization**: Converts `file://` URLs to absolute paths and standardizes separators.
2. **File caching**: Checks the file’s modification time against an in-memory cache (`fsFiles`) to avoid redundant disk reads. If the file changed or is uncached, it reads the content and creates a TypeScript `SourceFile`.
3. **Position calculation**: Uses `SourceFile.getPositionOfLineAndCharacter` to convert the 1-based line and column numbers from the stack trace into the 0-based character offset required by TypeScript diagnostics.

The resulting object contains the `file`, `start`, and `length` properties that editors use to render navigation links.

### Stage 3: Lazy Resolution in the Reporter

To avoid the overhead of stack parsing for diagnostics that editors might never inspect, TSSLint employs **lazy evaluation**. In [`packages/core/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/core/index.ts) (lines 12–14 and 44–46), the `Reporter` class stores the error and stack index, but only invokes `getRelatedInformations` when the diagnostic’s `relatedInformation` getter is accessed.

```typescript
const error: ts.DiagnosticWithLocation = {
    // ... other properties ...
    get relatedInformation() {
        return relatedInformation ??= getRelatedInformations(location[0], location[1]);
    },
};

```

This pattern ensures that the expensive file I/O and source mapping only occur when a user actually opens the diagnostic details in their IDE.

## Code Implementation Details

The integration between the core linter and the TypeScript plugin relies on two key functions. The `createLinter` function in [`packages/core/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/core/index.ts) accepts a `getRelatedInformations` callback, which the `Reporter` invokes lazily. Meanwhile, the TypeScript plugin in [`packages/typescript-plugin/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/typescript-plugin/index.ts) provides the concrete implementation that parses stacks and builds the related information objects.

When diagnostics are transported from the worker process to the editor (in [`packages/cli/lib/worker.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/cli/lib/worker.ts), lines 286–304), the `relatedInformation` array is serialized and reconstructed, preserving the file paths and positions that enable one-click navigation.

## Practical Example: Attaching Stack Information in a Custom Rule

Rule authors can opt into stack trace capture by calling the `.at()` method on the reporter. This example demonstrates how to link a diagnostic directly to the rule implementation:

```typescript
export const noFoo: Rule = (node, ctx) => {
    if (node.getText() === 'foo') {
        ctx.report('Avoid using "foo".')
           .at(new Error('noFoo triggered'), 1)  // Capture stack, use frame index 1
           .withFix('Rename to bar', () => [{ 
               fileName: ctx.file.fileName, 
               textChanges: [{ 
                   span: { start: node.getStart(), length: node.getWidth() }, 
                   newText: 'bar' 
               }] 
           }]);
    }
};

```

The `.at(new Error(), 1)` call stores the error and specifies that the first user frame (index 1, skipping the frame inside TSSLint itself) should be used for the related information. When the diagnostic appears in VS Code, the "Related Information" section displays a clickable link that jumps to the exact line in the rule file where `ctx.report` was invoked.

## Summary

- **TSSLint Related Information** transforms JavaScript stack traces into `DiagnosticRelatedInformation` objects that editors render as clickable navigation links.
- The pipeline uses **`error-stack-parser`** to extract file, line, and column data from `Error` stacks in [`packages/typescript-plugin/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/typescript-plugin/index.ts).
- **`createRelatedInformation`** normalizes paths, caches source files, and converts line/column numbers to character offsets using TypeScript’s `SourceFile` API.
- **Lazy evaluation** in [`packages/core/index.ts`](https://github.com/johnsoncodehk/tsslint/blob/main/packages/core/index.ts) ensures stack parsing only occurs when the editor actually requests `relatedInformation`.
- Rule authors trigger capture via the **`.at(error, stackIndex)`** method on the reporter, enabling one-click jumps from lint warnings to rule implementations.

## Frequently Asked Questions

### How does TSSLint capture the source location of a rule without hardcoding file paths?

TSSLint instantiates a native JavaScript `Error` object at the moment the rule reports a diagnostic. By parsing the error’s stack trace with `error-stack-parser`, it dynamically extracts the file name, line number, and column number from the execution context, eliminating the need for manual path configuration.

### What is the purpose of the stack index parameter in the `.at()` method?

The stack index allows rule authors to specify which frame in the call stack represents the relevant source location. Index 0 typically points to internal TSSLint functions, while index 1 or higher points to the user’s rule implementation. This flexibility ensures the "Related Information" link points to the meaningful rule code rather than framework internals.

### Why does TSSLint use lazy evaluation for related information?

Lazy evaluation defers the expensive work of file system access, source file parsing, and position calculation until the editor actually requests the `relatedInformation` property. This optimization prevents unnecessary overhead during bulk linting operations while still providing rich navigation data when users interact with specific diagnostics in their IDE.

### Which editors support one-click navigation via TSSLint’s related information?

Any editor that implements the Language Server Protocol (LSP) or TypeScript’s native diagnostic API can render `DiagnosticRelatedInformation` as clickable links. VS Code provides native support, displaying related information entries beneath diagnostics with "Go to" functionality that opens the rule source file at the precise line and column captured in the stack trace.