ESLint Plugin TSDoc is an ESLint plugin that validates TypeScript documentation comments against the TSDoc specification. It ensures consistent and correct documentation practices in TypeScript projects by providing automated linting of TSDoc comment syntax including proper use of TSDoc tags, formatting, and structural requirements.
npm install --save-dev eslint-plugin-tsdocThis package uses CommonJS exports with export = syntax:
const plugin = require("eslint-plugin-tsdoc");The plugin object contains ESLint rules. Direct access to individual rules is typically not needed in normal usage, but the plugin structure is:
const plugin = require("eslint-plugin-tsdoc");
// plugin.rules contains the ESLint rule definitionsnpm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-pluginnpm install --save-dev eslint-plugin-tsdoc// .eslintrc.js
module.exports = {
plugins: [
"@typescript-eslint/eslint-plugin",
"eslint-plugin-tsdoc"
],
extends: [
'plugin:@typescript-eslint/recommended'
],
parser: '@typescript-eslint/parser',
parserOptions: {
project: "./tsconfig.json",
tsconfigRootDir: __dirname,
ecmaVersion: 2018,
sourceType: "module"
},
rules: {
"tsdoc/syntax": "warn" // or "error"
}
};npx eslint "src/**/*.{ts,tsx}"The primary capability provided by this plugin is the tsdoc/syntax rule, which validates TypeScript documentation comments against the TSDoc specification.
// Main Plugin Interface (exported via export = plugin)
interface IPlugin {
rules: { [ruleName: string]: eslint.Rule.RuleModule };
}
// The actual exported plugin object
const plugin: IPlugin = {
rules: {
syntax: RuleModule
}
};
// ESLint Rule Module Structure
interface RuleModule {
meta: {
messages: { [messageId: string]: string };
type: 'problem';
docs: {
description: string;
category: string;
recommended: boolean;
url: string;
};
};
create: (context: eslint.Rule.RuleContext) => RuleListener;
}
// Rule Listener (ESLint visitor pattern)
interface RuleListener {
Program?: (node: ESTreeNode) => void;
[nodeType: string]: ((node: ESTreeNode) => void) | undefined;
}
// Rule Context (from ESLint)
interface RuleContext {
filename: string;
parserOptions: {
tsconfigRootDir?: string;
[key: string]: any;
};
getSourceCode(): SourceCode;
report(descriptor: ReportDescriptor): void;
}
// ESLint SourceCode interface
interface SourceCode {
text: string;
getAllComments(): Comment[];
getLocFromIndex(index: number): { line: number; column: number };
}
// ESLint Comment interface
interface Comment {
type: 'Block' | 'Line';
value: string;
range?: [number, number];
}
// ESTree Node interface
interface ESTreeNode {
type: string;
[key: string]: any;
}
// Report Descriptor for TSDoc violations
interface ReportDescriptor {
loc: {
start: { line: number; column: number };
end: { line: number; column: number };
} | { line: number; column: number };
messageId: string;
data?: { [key: string]: string };
}Rule Behavior:
/**)tsdoc.json) in the project hierarchy@typescript-eslint/parserUsage Examples:
Valid TSDoc comments that pass validation:
/**
* A great function that does something useful.
* @param name - The name parameter
* @returns The processed result
*/
function processData(name: string): string {
return `Processed: ${name}`;
}
/**
* A well-documented class.
* @public
*/
class DataProcessor {
/**
* Process the input data.
* @param input - Data to process
* @param options - Processing options
*/
process(input: string[], options?: { verbose: boolean }): void {
// implementation
}
}Invalid TSDoc comments that trigger violations:
/**
* This `is wrong - missing closing backtick
*/
function badExample() {}
/**
* @badtag This uses an undefined tag
*/
function anotherBadExample() {}
/**
* @param missingDescription
*/
function paramWithoutDescription(param: string) {}The plugin reports various TSDoc syntax violations with specific message IDs:
// Configuration and Application Errors
type ConfigErrorMessages = {
'error-loading-config-file': 'Error loading TSDoc config file:\n{{details}}';
'error-applying-config': 'Error applying TSDoc configuration: {{details}}';
};
// TSDoc Syntax Error Messages (dynamically loaded from @microsoft/tsdoc)
// Common examples include:
type TSDocErrorMessages = {
'tsdoc-code-span-missing-delimiter': 'tsdoc-code-span-missing-delimiter: {{unformattedText}}';
'tsdoc-undefined-tag': 'tsdoc-undefined-tag: {{unformattedText}}';
'tsdoc-malformed-inline-tag': 'tsdoc-malformed-inline-tag: {{unformattedText}}';
'tsdoc-missing-reference-target': 'tsdoc-missing-reference-target: {{unformattedText}}';
// ... other TSDoc message IDs from the TSDoc parser
};The plugin automatically discovers and uses TSDoc configuration files:
tsdoc.json files in the project hierarchy starting from each source file@typescript-eslint/parser is configured with tsconfigRootDir, looks for tsdoc.json in that directoryTSDoc Configuration File Support:
// tsdoc.json
{
"extends": ["@microsoft/api-extractor-config"],
"tagDefinitions": [
{
"tagName": "@customTag",
"syntaxKind": "block",
"allowMultiple": false
}
]
}This package depends on:
The plugin uses TypeScript interfaces internally but exports as compiled JavaScript. Key type definitions used throughout the plugin:
// Main plugin interface (export = pattern)
interface IPlugin {
rules: { [ruleName: string]: eslint.Rule.RuleModule };
}
// TSDoc Configuration File (from @microsoft/tsdoc-config)
interface TSDocConfigFile {
fileNotFound: boolean;
hasErrors: boolean;
getErrorSummary(): string;
configureParser(configuration: TSDocConfiguration): void;
checkForModifiedFiles(): boolean;
}
// TSDoc Parser Context (from @microsoft/tsdoc)
interface ParserContext {
log: {
messages: Array<{
messageId: string;
textRange: TextRange;
unformattedText: string;
}>;
};
}
// ESLint Source Code interface
interface SourceCode {
text: string;
getAllComments(): Comment[];
getLocFromIndex(index: number): { line: number; column: number };
}
// ESLint Comment interface
interface Comment {
type: 'Block' | 'Line';
value: string;
range?: [number, number];
}The plugin includes a Debug class for internal logging (typically used for development/troubleshooting).
class Debug {
/**
* Log a debug message (no-op in production builds)
* @param message - Debug message to log
*/
static log(message: string): void;
}The plugin uses a ConfigCache class to efficiently manage TSDoc configuration files with caching.
class ConfigCache {
/**
* Get TSDoc configuration for a specific source file
* @param sourceFilePath - Path to the source file being linted
* @param tsConfigRootDir - Optional TypeScript config root directory
* @returns TSDoc configuration file object
*/
static getForSourceFile(
sourceFilePath: string,
tsConfigRootDir?: string
): TSDocConfigFile;
}
// Cache configuration interface
interface ICachedConfig {
loadTimeMs: number;
lastCheckTimeMs: number;
configFile: TSDocConfigFile;
}Performance Configuration Constants:
CACHE_CHECK_INTERVAL_MS: 3000 (3 seconds)CACHE_EXPIRE_MS: 20000 (20 seconds)CACHE_MAX_SIZE: 100 entries