Extensible reporting system with built-in reporters and support for custom reporter modules, template-based output formatting, and color styling for presenting spell checking results.
Get the built-in CLI reporter with comprehensive formatting and color support.
/**
* Get the default CLI reporter with formatting and color support
* @param options - Reporter configuration options
* @param config - Optional additional reporter configuration
* @returns CSpellReporter instance configured for CLI output
*/
function getDefaultReporter(options: ReporterOptions, config?: CSpellReporterConfiguration): CSpellReporter;
interface ReporterOptions {
fileGlobs: string[];
color?: boolean;
debug?: boolean;
issues?: boolean;
issuesSummaryReport?: boolean;
legacy?: boolean;
progress?: boolean;
relative?: boolean;
root?: string;
showContext?: boolean | number;
showPerfSummary?: boolean;
showSuggestions?: boolean;
silent?: boolean;
summary?: boolean;
verbose?: boolean;
wordsOnly?: boolean;
}
interface CSpellReporter {
/** Report when spell checking begins */
onBegin?: (settings: CSpellSettings, emitter: Emitter) => void;
/** Report when a file begins processing */
onFileBegin?: (filename: string) => void;
/** Report when a file completes processing */
onFileEnd?: (filename: string, result: FileResult) => void;
/** Report spelling issues found */
onIssue?: (issue: Issue) => void;
/** Report errors encountered during processing */
onError?: (message: string, error: Error) => void;
/** Report when spell checking completes */
onResult?: (result: RunResult) => void;
}
interface Issue {
/** The misspelled text */
text: string;
/** Start offset in the file */
offset: number;
/** Length of the misspelled text */
length: number;
/** Line number (1-based) */
line: number;
/** Column number (1-based) */
col: number;
/** File path */
filename: string;
/** Issue context */
context: IssueContext;
/** Suggested corrections */
suggestions?: string[];
}
interface FileResult {
/** File path */
filename: string;
/** Number of issues found */
issues: number;
/** Whether file was processed successfully */
success: boolean;
/** Time taken to process file */
elapsedTime?: number;
}Usage Examples:
import { lint, getDefaultReporter } from "cspell";
// Use default reporter
const reporter = getDefaultReporter({
fileGlobs: ["src/**/*.js"],
verbose: true,
showSuggestions: true,
color: true
});
const result = await lint(
["src/**/*.js"],
{ verbose: true },
reporter
);
// Custom reporter extending default behavior
class CustomReporter implements CSpellReporter {
private defaultReporter = getDefaultReporter({
fileGlobs: ["src/**/*.js"],
verbose: true
});
onBegin = (settings: CSpellSettings, emitter: Emitter) => {
console.log("π Starting spell check...");
this.defaultReporter.onBegin?.(settings, emitter);
};
onIssue = (issue: Issue) => {
// Custom issue logging
console.log(`β ${issue.filename}:${issue.line}:${issue.col} - "${issue.text}"`);
// Still use default formatting
this.defaultReporter.onIssue?.(issue);
};
onResult = (result: RunResult) => {
console.log(`β
Checked ${result.files} files, found ${result.issues} issues`);
this.defaultReporter.onResult?.(result);
};
}Configure reporter behavior with comprehensive options for output formatting and styling.
interface CSpellReporterConfiguration extends ReporterConfiguration, LinterCliOptions {
/** Console interface for output */
readonly console?: ReporterConsole;
}
interface ReporterConfiguration {
/** Built-in reporter names or custom reporter module paths */
reporter?: string[];
/** Custom issue output template with placeholder variables */
issueTemplate?: string;
/** Show suggestions for misspelled words */
showSuggestions?: boolean;
/** Show context around issues */
showContext?: boolean | number;
/** Only show words without file/line information */
wordsOnly?: boolean;
/** Show unique errors only */
unique?: boolean;
/** Verbose output mode */
verbose?: boolean;
/** Enable color output */
color?: boolean;
}
interface ReporterConsole {
/** Standard output logging */
log: (text: string) => void;
/** Error output logging */
error: (text: string) => void;
/** Warning output logging */
warn: (text: string) => void;
/** Information output logging */
info: (text: string) => void;
}Usage Examples:
import { lint } from "cspell";
// Built-in reporter configuration
const result = await lint(
["src/**/*.ts"],
{
reporter: ["default"], // Use default reporter explicitly
showSuggestions: true, // Show spelling suggestions
showContext: 10, // Show 10 characters of context
verbose: true, // Verbose output
color: true // Enable color output
}
);
// JSON reporter for CI/CD
const result = await lint(
["**/*.md"],
{
reporter: ["@cspell/cspell-json-reporter"],
verbose: false,
color: false
}
);
// Multiple reporters
const result = await lint(
["docs/**/*.md"],
{
reporter: [
"default", // Console output
"@cspell/cspell-json-reporter", // JSON output
"./custom-reporters/slack-reporter.js" // Custom Slack reporter
]
}
);Create custom reporter modules for specialized output formats and integrations.
interface CSpellReporterModule {
/**
* Get reporter instance with configuration
* @param settings - Reporter settings object
* @param config - Reporter configuration options
* @returns Configured CSpellReporter instance
*/
getReporter: <T>(settings: T, config: CSpellReporterConfiguration) => CSpellReporter;
}
// Example custom reporter module structure
interface CustomReporterSettings {
/** Custom setting for reporter behavior */
customOption?: string;
/** Output file path for custom reporter */
outputFile?: string;
}Usage Examples:
// custom-reporter.js - Custom reporter module
export function getReporter(settings, config) {
return {
issues: [],
onBegin(cspellSettings, emitter) {
console.log(`Starting spell check with ${config.reporter?.length || 1} reporters`);
},
onIssue(issue) {
this.issues.push({
file: issue.filename,
line: issue.line,
column: issue.col,
word: issue.text,
suggestions: issue.suggestions || []
});
},
onResult(result) {
// Generate custom report format
const report = {
summary: {
totalFiles: result.files,
filesWithIssues: result.filesWithIssues.size,
totalIssues: result.issues,
timestamp: new Date().toISOString()
},
issues: this.issues
};
if (settings.outputFile) {
require('fs').writeFileSync(settings.outputFile, JSON.stringify(report, null, 2));
} else {
console.log(JSON.stringify(report, null, 2));
}
}
};
}
// Using the custom reporter
import { lint } from "cspell";
const result = await lint(
["src/**/*.js"],
{
reporter: ["./custom-reporter.js"],
customReporterSettings: {
outputFile: "./spell-check-report.json"
}
}
);Use customizable templates for issue output formatting with placeholder variable substitution.
interface TemplateSubstitutions {
/** Column number */
$col: string;
/** Full context around the issue */
$contextFull: string;
/** Context text before the issue */
$contextLeft: string;
/** Context text after the issue */
$contextRight: string;
/** File name */
$filename: string;
/** Padding for context alignment */
$padContext: string;
/** Padding for row/column alignment */
$padRowCol: string;
/** Row (line) number */
$row: string;
/** Comma-separated suggestions */
$suggestions: string;
/** The misspelled text */
$text: string;
/** Quick fix suggestion (first suggestion) */
$quickFix: string;
/** Issue message */
$message: string;
/** Issue message with color formatting applied */
$messageColored: string;
/** Full URI of the file being checked */
$uri: string;
}
// Predefined templates
type IssueTemplate =
| "default" // Standard format with colors
| "legacy" // Legacy bracket format
| "wordsOnly" // Just the misspelled words
| "withContext" // Include surrounding text context
| "withSuggestions" // Include spelling suggestions
| string; // Custom template stringUsage Examples:
import { lint } from "cspell";
// Custom issue template
const result = await lint(
["src/**/*.ts"],
{
issueTemplate: "{green $filename}:{yellow $row:$col} - {red $text} -> {blue $suggestions}"
}
);
// Context-aware template
const result = await lint(
["docs/**/*.md"],
{
issueTemplate: `
File: $filename (Line $row)
Issue: "$text"
Context: ...$contextLeft{$text}$contextRight...
Suggestions: [$suggestions]
---`,
showContext: 20
}
);
// Minimal template for CI
const result = await lint(
["**/*.js"],
{
issueTemplate: "$filename:$row:$col:$text",
color: false
}
);
// Rich template with colors and suggestions
const result = await lint(
["src/**"],
{
issueTemplate: "{bgRed ERROR } {green $filename}:{yellow $row}:{yellow $col} - {red {underline $text}} {gray (suggestions: {cyan $suggestions})}",
showSuggestions: true
}
);Control color output and styling in reporter output using Chalk color templates.
// Color template syntax (using Chalk template strings)
type ColorTemplate =
| "{green text}" // Green text
| "{red {underline text}}" // Red underlined text
| "{bgBlue {white text}}" // White text on blue background
| "{yellow text}" // Yellow text
| "{gray text}" // Gray text
| "{cyan text}" // Cyan text
| "{magenta text}" // Magenta text
| string; // Plain text
interface ColorConfiguration {
/** Enable/disable color output */
color?: boolean;
/** Color support detection */
colorSupport?: 'auto' | 'always' | 'never';
}Usage Examples:
import { lint } from "cspell";
// Colorful output template
const result = await lint(
["src/**/*.js"],
{
issueTemplate: "{bgRed {white ERROR }} {green $filename}:{yellow $row:$col} - {red {bold $text}}",
color: true
}
);
// Disable colors for CI/automated environments
const result = await lint(
["**/*.ts"],
{
issueTemplate: "ERROR: $filename:$row:$col - $text (suggestions: $suggestions)",
color: false,
showSuggestions: true
}
);
// Conditional color based on environment
const isCI = process.env.CI === 'true';
const result = await lint(
["docs/**/*.md"],
{
issueTemplate: isCI
? "$filename:$row:$col - $text" // Plain for CI
: "{red β} {green $filename}:{yellow $row:$col} - {red $text}", // Colorful for local
color: !isCI
}
);CSpell includes several built-in reporters for different output formats and use cases.
// Built-in reporter identifiers
type BuiltinReporter =
| "default" // Standard CLI reporter with colors
| "@cspell/cspell-json-reporter" // JSON format output
| "@cspell/cspell-junit-reporter" // JUnit XML format
| "@cspell/cspell-github-actions" // GitHub Actions annotations
| string; // Path to custom reporter moduleUsage Examples:
import { lint } from "cspell";
// Default console reporter
const result = await lint(["src/**/*.js"], {
reporter: ["default"]
});
// JSON reporter for programmatic consumption
const result = await lint(["**/*.ts"], {
reporter: ["@cspell/cspell-json-reporter"]
});
// JUnit XML for test result integration
const result = await lint(["tests/**/*.js"], {
reporter: ["@cspell/cspell-junit-reporter"]
});
// GitHub Actions integration
const result = await lint(["**/*.md"], {
reporter: ["@cspell/cspell-github-actions"]
});
// Multiple reporters for comprehensive output
const result = await lint(["src/**"], {
reporter: [
"default", // Console output for developers
"@cspell/cspell-json-reporter", // JSON for automation
"./reporters/slack-reporter.js" // Custom Slack notifications
]
});Handle errors and edge cases in custom reporters with proper error propagation.
interface ReporterErrorHandling {
/** Handle errors during reporting */
onError?: (message: string, error: Error) => void;
/** Handle reporter initialization failures */
onReporterError?: (reporterName: string, error: Error) => void;
}
// Error scenarios in reporters
type ReporterError =
| "REPORTER_NOT_FOUND" // Reporter module not found
| "REPORTER_INVALID" // Reporter doesn't implement interface
| "OUTPUT_WRITE_FAILED" // Failed to write output file
| "TEMPLATE_PARSE_ERROR" // Invalid template syntax
| "COLOR_SUPPORT_ERROR"; // Color support detection failedUsage Examples:
// Robust reporter with error handling
class RobustReporter implements CSpellReporter {
onError = (message: string, error: Error) => {
console.error(`Reporter Error: ${message}`);
console.error(`Details: ${error.message}`);
// Continue processing instead of failing completely
return;
};
onIssue = (issue: Issue) => {
try {
// Custom issue processing
this.processIssue(issue);
} catch (error) {
this.onError?.("Failed to process issue", error as Error);
}
};
private processIssue(issue: Issue) {
// Safe issue processing with fallbacks
const safeFilename = issue.filename || '<unknown>';
const safeText = issue.text || '<empty>';
console.log(`${safeFilename}:${issue.line}:${issue.col} - ${safeText}`);
}
}
// Using reporter with error handling
try {
const result = await lint(["src/**/*.js"], {
reporter: ["./potentially-failing-reporter.js"]
});
} catch (error) {
console.error("Spell check failed:", error.message);
// Fallback to default reporter
const fallbackResult = await lint(["src/**/*.js"], {
reporter: ["default"]
});
}// ci-reporter.js - Optimized for CI environments
export function getReporter(settings, config) {
return {
onResult(result) {
if (result.issues > 0) {
console.log(`::error::Found ${result.issues} spelling issues in ${result.filesWithIssues.size} files`);
process.exitCode = 1;
} else {
console.log(`::notice::Spell check passed - ${result.files} files checked`);
}
}
};
}// slack-reporter.js - Send results to Slack
export function getReporter(settings, config) {
return {
issues: [],
onIssue(issue) {
this.issues.push(issue);
},
async onResult(result) {
if (result.issues > 0 && settings.slackWebhook) {
const message = {
text: `Spell check found ${result.issues} issues`,
attachments: [{
color: 'warning',
fields: [
{ title: 'Files Checked', value: result.files, short: true },
{ title: 'Issues Found', value: result.issues, short: true }
]
}]
};
await fetch(settings.slackWebhook, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message)
});
}
}
};
}