eslint-plugin-tsdoc
Version:
An ESLint plugin that validates TypeScript doc comments
137 lines • 6.94 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
const utils_1 = require("@typescript-eslint/utils");
const tsdoc_1 = require("@microsoft/tsdoc");
const Debug_1 = require("./Debug");
const ConfigCache_1 = require("./ConfigCache");
const tsdocMessageIds = {};
const defaultTSDocConfiguration = new tsdoc_1.TSDocConfiguration();
defaultTSDocConfiguration.allTsdocMessageIds.forEach((messageId) => {
tsdocMessageIds[messageId] = `${messageId}: {{unformattedText}}`;
});
function getRootDirectoryFromContext(context) {
var _a, _b, _c, _d, _e, _f, _g;
let rootDirectory;
try {
// First attempt to get the root directory from the tsconfig baseUrl, then the program current directory
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const program = ((_b = (_a = context.sourceCode) === null || _a === void 0 ? void 0 : _a.parserServices) !== null && _b !== void 0 ? _b : utils_1.ESLintUtils.getParserServices(context))
.program;
rootDirectory = (_c = program === null || program === void 0 ? void 0 : program.getCompilerOptions().baseUrl) !== null && _c !== void 0 ? _c : program === null || program === void 0 ? void 0 : program.getCurrentDirectory();
}
catch (_h) {
// Ignore the error if we cannot retrieve a TS program
}
// Fall back to the parserOptions.tsconfigRootDir if available, otherwise the eslint working directory
if (!rootDirectory) {
rootDirectory = (_f = (_e = (_d = context.parserOptions) === null || _d === void 0 ? void 0 : _d.tsconfigRootDir) !== null && _e !== void 0 ? _e : context.cwd) !== null && _f !== void 0 ? _f : (_g = context.getCwd) === null || _g === void 0 ? void 0 : _g.call(context);
}
return rootDirectory;
}
const plugin = {
rules: {
// NOTE: The actual ESLint rule name will be "tsdoc/syntax". It is calculated by deleting "eslint-plugin-"
// from the NPM package name, and then appending this string.
syntax: {
meta: {
messages: {
'error-loading-config-file': 'Error loading TSDoc config file:\n{{details}}',
'error-applying-config': 'Error applying TSDoc configuration: {{details}}',
...tsdocMessageIds
},
type: 'problem',
docs: {
description: 'Validates that TypeScript documentation comments conform to the TSDoc standard',
category: 'Stylistic Issues',
// This package is experimental
recommended: false,
url: 'https://tsdoc.org/pages/packages/eslint-plugin-tsdoc'
}
},
create: (context) => {
var _a;
const sourceFilePath = context.filename;
// If eslint is configured with @typescript-eslint/parser, there is a parser option
// to explicitly specify where the tsconfig file is. Use that if available.
const tsConfigDir = getRootDirectoryFromContext(context);
Debug_1.Debug.log(`Linting: "${sourceFilePath}"`);
const tsdocConfiguration = new tsdoc_1.TSDocConfiguration();
try {
const tsdocConfigFile = ConfigCache_1.ConfigCache.getForSourceFile(sourceFilePath, tsConfigDir);
if (!tsdocConfigFile.fileNotFound) {
if (tsdocConfigFile.hasErrors) {
context.report({
loc: { line: 1, column: 1 },
messageId: 'error-loading-config-file',
data: {
details: tsdocConfigFile.getErrorSummary()
}
});
}
try {
tsdocConfigFile.configureParser(tsdocConfiguration);
}
catch (e) {
context.report({
loc: { line: 1, column: 1 },
messageId: 'error-applying-config',
data: {
details: e.message
}
});
}
}
}
catch (e) {
context.report({
loc: { line: 1, column: 1 },
messageId: 'error-loading-config-file',
data: {
details: `Unexpected exception: ${e.message}`
}
});
}
const tsdocParser = new tsdoc_1.TSDocParser(tsdocConfiguration);
const sourceCode = (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode();
function checkCommentBlocks() {
for (const comment of sourceCode.getAllComments()) {
if (comment.type !== 'Block') {
continue;
}
if (!comment.range) {
continue;
}
const textRange = tsdoc_1.TextRange.fromStringRange(sourceCode.text, comment.range[0], comment.range[1]);
// Smallest comment is "/***/"
if (textRange.length < 5) {
continue;
}
// Make sure it starts with "/**"
if (textRange.buffer[textRange.pos + 2] !== '*') {
continue;
}
const parserContext = tsdocParser.parseRange(textRange);
for (const message of parserContext.log.messages) {
context.report({
loc: {
start: sourceCode.getLocFromIndex(message.textRange.pos),
end: sourceCode.getLocFromIndex(message.textRange.end)
},
messageId: message.messageId,
data: {
unformattedText: message.unformattedText
}
});
}
}
}
return {
Program: checkCommentBlocks
};
}
}
}
};
module.exports = plugin;
//# sourceMappingURL=index.js.map