Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const accessLevels = ['package', 'private', 'protected', 'public'];
var _default = exports.default = (0, _iterateJsdoc.default)(({
report,
utils
}) => {
utils.forEachPreferredTag('access', (jsdocParameter, targetTagName) => {
const desc = jsdocParameter.name + ' ' + jsdocParameter.description;
if (!accessLevels.includes(desc.trim())) {
report(`Missing valid JSDoc @${targetTagName} level.`, null, jsdocParameter);
}
});
const accessLength = utils.getTags('access').length;
const individualTagLength = utils.getPresentTags(accessLevels).length;
if (accessLength && individualTagLength) {
report('The @access tag may not be used with specific access-control tags (@package, @private, @protected, or @public).');
}
if (accessLength > 1 || individualTagLength > 1) {
report('At most one access-control tag may be present on a JSDoc block.');
}
}, {
checkPrivate: true,
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Checks that `@access` tags have a valid value.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-access.md#repos-sticky-header'
},
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkAccess.cjs.map

View file

@ -0,0 +1 @@
{"version":3,"file":"checkAccess.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","accessLevels","_default","exports","iterateJsdoc","report","utils","forEachPreferredTag","jsdocParameter","targetTagName","desc","name","description","includes","trim","accessLength","getTags","length","individualTagLength","getPresentTags","checkPrivate","iterateAllJsdocs","meta","docs","url","type","module"],"sources":["../../src/rules/checkAccess.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nconst accessLevels = [\n 'package', 'private', 'protected', 'public',\n];\n\nexport default iterateJsdoc(({\n report,\n utils,\n}) => {\n utils.forEachPreferredTag('access', (jsdocParameter, targetTagName) => {\n const desc = jsdocParameter.name + ' ' + jsdocParameter.description;\n\n if (!accessLevels.includes(desc.trim())) {\n report(\n `Missing valid JSDoc @${targetTagName} level.`,\n null,\n jsdocParameter,\n );\n }\n });\n const accessLength = utils.getTags('access').length;\n const individualTagLength = utils.getPresentTags(accessLevels).length;\n if (accessLength && individualTagLength) {\n report(\n 'The @access tag may not be used with specific access-control tags (@package, @private, @protected, or @public).',\n );\n }\n\n if (accessLength > 1 || individualTagLength > 1) {\n report(\n 'At most one access-control tag may be present on a JSDoc block.',\n );\n }\n}, {\n checkPrivate: true,\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Checks that `@access` tags have a valid value.',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-access.md#repos-sticky-header',\n },\n type: 'suggestion',\n },\n});\n"],"mappings":";;;;;;AAAA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA8C,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAE9C,MAAMG,YAAY,GAAG,CACnB,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,CAC5C;AAAC,IAAAC,QAAA,GAAAC,OAAA,CAAAH,OAAA,GAEa,IAAAI,qBAAY,EAAC,CAAC;EAC3BC,MAAM;EACNC;AACF,CAAC,KAAK;EACJA,KAAK,CAACC,mBAAmB,CAAC,QAAQ,EAAE,CAACC,cAAc,EAAEC,aAAa,KAAK;IACrE,MAAMC,IAAI,GAAGF,cAAc,CAACG,IAAI,GAAG,GAAG,GAAGH,cAAc,CAACI,WAAW;IAEnE,IAAI,CAACX,YAAY,CAACY,QAAQ,CAACH,IAAI,CAACI,IAAI,CAAC,CAAC,CAAC,EAAE;MACvCT,MAAM,CACJ,wBAAwBI,aAAa,SAAS,EAC9C,IAAI,EACJD,cACF,CAAC;IACH;EACF,CAAC,CAAC;EACF,MAAMO,YAAY,GAAGT,KAAK,CAACU,OAAO,CAAC,QAAQ,CAAC,CAACC,MAAM;EACnD,MAAMC,mBAAmB,GAAGZ,KAAK,CAACa,cAAc,CAAClB,YAAY,CAAC,CAACgB,MAAM;EACrE,IAAIF,YAAY,IAAIG,mBAAmB,EAAE;IACvCb,MAAM,CACJ,iHACF,CAAC;EACH;EAEA,IAAIU,YAAY,GAAG,CAAC,IAAIG,mBAAmB,GAAG,CAAC,EAAE;IAC/Cb,MAAM,CACJ,iEACF,CAAC;EACH;AACF,CAAC,EAAE;EACDe,YAAY,EAAE,IAAI;EAClBC,gBAAgB,EAAE,IAAI;EACtBC,IAAI,EAAE;IACJC,IAAI,EAAE;MACJX,WAAW,EAAE,gDAAgD;MAC7DY,GAAG,EAAE;IACP,CAAC;IACDC,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAAAC,MAAA,CAAAvB,OAAA,GAAAA,OAAA,CAAAH,OAAA","ignoreList":[]}

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkAccess.d.ts.map

View file

@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
indent,
jsdocNode,
report,
sourceCode
}) => {
const {
innerIndent = 1
} = context.options[0] || {};
// `indent` is whitespace from line 1 (`/**`), so slice and account for "/".
const indentLevel = indent.length + innerIndent;
const sourceLines = sourceCode.getText(jsdocNode).split('\n').slice(1).map((line, number) => {
return {
line: line.split('*')[0],
number
};
}).filter(({
line
}) => {
return !line.trimStart().length;
});
/** @type {import('eslint').Rule.ReportFixer} */
const fix = fixer => {
const replacement = sourceCode.getText(jsdocNode).split('\n').map((line, index) => {
// Ignore the first line and all lines not starting with `*`
const ignored = !index || line.split('*')[0].trimStart().length;
return ignored ? line : `${indent}${''.padStart(innerIndent, ' ')}${line.trimStart()}`;
}).join('\n');
return fixer.replaceText(jsdocNode, replacement);
};
sourceLines.some(({
line,
number
}) => {
if (line.length !== indentLevel) {
report('Expected JSDoc block to be aligned.', fix, {
line: number + 1
});
return true;
}
return false;
});
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports invalid alignment of JSDoc block asterisks.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-alignment.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
innerIndent: {
default: 1,
description: `Set to 0 if you wish to avoid the normal requirement for an inner indentation of
one space. Defaults to 1 (one space of normal inner indentation).`,
type: 'integer'
}
},
type: 'object'
}],
type: 'layout'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkAlignment.cjs.map

View file

@ -0,0 +1 @@
{"version":3,"file":"checkAlignment.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","_default","exports","iterateJsdoc","context","indent","jsdocNode","report","sourceCode","innerIndent","options","indentLevel","length","sourceLines","getText","split","slice","map","line","number","filter","trimStart","fix","fixer","replacement","index","ignored","padStart","join","replaceText","some","iterateAllJsdocs","meta","docs","description","url","fixable","schema","additionalProperties","properties","type","module"],"sources":["../../src/rules/checkAlignment.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nexport default iterateJsdoc(({\n context,\n indent,\n jsdocNode,\n report,\n sourceCode,\n}) => {\n const {\n innerIndent = 1,\n } = context.options[0] || {};\n\n // `indent` is whitespace from line 1 (`/**`), so slice and account for \"/\".\n const indentLevel = indent.length + innerIndent;\n const sourceLines = sourceCode.getText(jsdocNode).split('\\n')\n .slice(1)\n .map((line, number) => {\n return {\n line: line.split('*')[0],\n number,\n };\n })\n .filter(({\n line,\n }) => {\n return !line.trimStart().length;\n });\n\n /** @type {import('eslint').Rule.ReportFixer} */\n const fix = (fixer) => {\n const replacement = sourceCode.getText(jsdocNode).split('\\n')\n .map((line, index) => {\n // Ignore the first line and all lines not starting with `*`\n const ignored = !index || line.split('*')[0].trimStart().length;\n\n return ignored ? line : `${indent}${''.padStart(innerIndent, ' ')}${line.trimStart()}`;\n })\n .join('\\n');\n\n return fixer.replaceText(jsdocNode, replacement);\n };\n\n sourceLines.some(({\n line,\n number,\n }) => {\n if (line.length !== indentLevel) {\n report('Expected JSDoc block to be aligned.', fix, {\n line: number + 1,\n });\n\n return true;\n }\n\n return false;\n });\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Reports invalid alignment of JSDoc block asterisks.',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-alignment.md#repos-sticky-header',\n },\n fixable: 'code',\n schema: [\n {\n additionalProperties: false,\n properties: {\n innerIndent: {\n default: 1,\n description: `Set to 0 if you wish to avoid the normal requirement for an inner indentation of\none space. Defaults to 1 (one space of normal inner indentation).`,\n type: 'integer',\n },\n },\n type: 'object',\n },\n ],\n type: 'layout',\n },\n});\n"],"mappings":";;;;;;AAAA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA8C,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAE/B,IAAAG,qBAAY,EAAC,CAAC;EAC3BC,OAAO;EACPC,MAAM;EACNC,SAAS;EACTC,MAAM;EACNC;AACF,CAAC,KAAK;EACJ,MAAM;IACJC,WAAW,GAAG;EAChB,CAAC,GAAGL,OAAO,CAACM,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;;EAE5B;EACA,MAAMC,WAAW,GAAGN,MAAM,CAACO,MAAM,GAAGH,WAAW;EAC/C,MAAMI,WAAW,GAAGL,UAAU,CAACM,OAAO,CAACR,SAAS,CAAC,CAACS,KAAK,CAAC,IAAI,CAAC,CAC1DC,KAAK,CAAC,CAAC,CAAC,CACRC,GAAG,CAAC,CAACC,IAAI,EAAEC,MAAM,KAAK;IACrB,OAAO;MACLD,IAAI,EAAEA,IAAI,CAACH,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;MACxBI;IACF,CAAC;EACH,CAAC,CAAC,CACDC,MAAM,CAAC,CAAC;IACPF;EACF,CAAC,KAAK;IACJ,OAAO,CAACA,IAAI,CAACG,SAAS,CAAC,CAAC,CAACT,MAAM;EACjC,CAAC,CAAC;;EAEJ;EACA,MAAMU,GAAG,GAAIC,KAAK,IAAK;IACrB,MAAMC,WAAW,GAAGhB,UAAU,CAACM,OAAO,CAACR,SAAS,CAAC,CAACS,KAAK,CAAC,IAAI,CAAC,CAC1DE,GAAG,CAAC,CAACC,IAAI,EAAEO,KAAK,KAAK;MACpB;MACA,MAAMC,OAAO,GAAG,CAACD,KAAK,IAAIP,IAAI,CAACH,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAACM,SAAS,CAAC,CAAC,CAACT,MAAM;MAE/D,OAAOc,OAAO,GAAGR,IAAI,GAAG,GAAGb,MAAM,GAAG,EAAE,CAACsB,QAAQ,CAAClB,WAAW,EAAE,GAAG,CAAC,GAAGS,IAAI,CAACG,SAAS,CAAC,CAAC,EAAE;IACxF,CAAC,CAAC,CACDO,IAAI,CAAC,IAAI,CAAC;IAEb,OAAOL,KAAK,CAACM,WAAW,CAACvB,SAAS,EAAEkB,WAAW,CAAC;EAClD,CAAC;EAEDX,WAAW,CAACiB,IAAI,CAAC,CAAC;IAChBZ,IAAI;IACJC;EACF,CAAC,KAAK;IACJ,IAAID,IAAI,CAACN,MAAM,KAAKD,WAAW,EAAE;MAC/BJ,MAAM,CAAC,qCAAqC,EAAEe,GAAG,EAAE;QACjDJ,IAAI,EAAEC,MAAM,GAAG;MACjB,CAAC,CAAC;MAEF,OAAO,IAAI;IACb;IAEA,OAAO,KAAK;EACd,CAAC,CAAC;AACJ,CAAC,EAAE;EACDY,gBAAgB,EAAE,IAAI;EACtBC,IAAI,EAAE;IACJC,IAAI,EAAE;MACJC,WAAW,EAAE,qDAAqD;MAClEC,GAAG,EAAE;IACP,CAAC;IACDC,OAAO,EAAE,MAAM;IACfC,MAAM,EAAE,CACN;MACEC,oBAAoB,EAAE,KAAK;MAC3BC,UAAU,EAAE;QACV9B,WAAW,EAAE;UACXT,OAAO,EAAE,CAAC;UACVkC,WAAW,EAAE;AACzB,kEAAkE;UACtDM,IAAI,EAAE;QACR;MACF,CAAC;MACDA,IAAI,EAAE;IACR,CAAC,CACF;IACDA,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAAAC,MAAA,CAAAvC,OAAA,GAAAA,OAAA,CAAAF,OAAA","ignoreList":[]}

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkAlignment.d.ts.map

View file

@ -0,0 +1,520 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _eslint = _interopRequireWildcard(require("eslint"));
var _semver = _interopRequireDefault(require("semver"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const {
// @ts-expect-error Older ESLint
CLIEngine
} = _eslint.default;
const zeroBasedLineIndexAdjust = -1;
const likelyNestedJSDocIndentSpace = 1;
const preTagSpaceLength = 1;
// If a space is present, we should ignore it
const firstLinePrefixLength = preTagSpaceLength;
const hasCaptionRegex = /^\s*<caption>([\s\S]*?)<\/caption>/v;
/**
* @param {string} str
* @returns {string}
*/
const escapeStringRegexp = str => {
return str.replaceAll(/[.*+?^$\{\}\(\)\|\[\]\\]/gv, '\\$&');
};
/**
* @param {string} str
* @param {string} ch
* @returns {import('../iterateJsdoc.js').Integer}
*/
const countChars = (str, ch) => {
return (str.match(new RegExp(escapeStringRegexp(ch), 'gv')) || []).length;
};
/** @type {import('eslint').Linter.RulesRecord} */
const defaultMdRules = {
// "always" newline rule at end unlikely in sample code
'@stylistic/eol-last': 0,
// Often wish to start `@example` code after newline; also may use
// empty lines for spacing
'@stylistic/no-multiple-empty-lines': 0,
// Can generally look nicer to pad a little even if code imposes more stringency
'@stylistic/padded-blocks': 0,
'@typescript-eslint/no-unused-vars': 0,
// "always" newline rule at end unlikely in sample code
'eol-last': 0,
// Wouldn't generally expect example paths to resolve relative to JS file
'import/no-unresolved': 0,
// Snippets likely too short to always include import/export info
'import/unambiguous': 0,
'jsdoc/require-file-overview': 0,
// The end of a multiline comment would end the comment the example is in.
'jsdoc/require-jsdoc': 0,
// See import/no-unresolved
'n/no-missing-import': 0,
'n/no-missing-require': 0,
// Unlikely to have inadvertent debugging within examples
'no-console': 0,
// Often wish to start `@example` code after newline; also may use
// empty lines for spacing
'no-multiple-empty-lines': 0,
// Many variables in examples will be `undefined`
'no-undef': 0,
// Common to define variables for clarity without always using them
'no-unused-vars': 0,
// See import/no-unresolved
'node/no-missing-import': 0,
'node/no-missing-require': 0,
// Can generally look nicer to pad a little even if code imposes more stringency
'padded-blocks': 0
};
/** @type {import('eslint').Linter.RulesRecord} */
const defaultExpressionRules = {
...defaultMdRules,
'@stylistic/quotes': ['error', 'double'],
'@stylistic/semi': ['error', 'never'],
'@typescript-eslint/no-unused-expressions': 'off',
'chai-friendly/no-unused-expressions': 'off',
'no-empty-function': 'off',
'no-new': 'off',
'no-unused-expressions': 'off',
quotes: ['error', 'double'],
semi: ['error', 'never'],
strict: 'off'
};
/**
* @param {string} text
* @returns {[
* import('../iterateJsdoc.js').Integer,
* import('../iterateJsdoc.js').Integer
* ]}
*/
const getLinesCols = text => {
const matchLines = countChars(text, '\n');
const colDelta = matchLines ? text.slice(text.lastIndexOf('\n') + 1).length : text.length;
return [matchLines, colDelta];
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
globalState,
report,
utils
}) => {
if (_semver.default.gte(_eslint.ESLint.version, '8.0.0')) {
report('This rule does not work for ESLint 8+; you should disable this rule and use' + 'the processor mentioned in the docs.', null, {
column: 1,
line: 1
});
return;
}
if (!globalState.has('checkExamples-matchingFileName')) {
globalState.set('checkExamples-matchingFileName', new Map());
}
const matchingFileNameMap = /** @type {Map<string, string>} */
globalState.get('checkExamples-matchingFileName');
const options = context.options[0] || {};
let {
exampleCodeRegex = null,
rejectExampleCodeRegex = null
} = options;
const {
allowInlineConfig = true,
baseConfig = {},
captionRequired = false,
checkDefaults = false,
checkEslintrc = true,
checkParams = false,
checkProperties = false,
configFile,
matchingFileName = null,
matchingFileNameDefaults = null,
matchingFileNameParams = null,
matchingFileNameProperties = null,
noDefaultExampleRules = false,
paddedIndent = 0,
reportUnusedDisableDirectives = true
} = options;
// Make this configurable?
/**
* @type {never[]}
*/
const rulePaths = [];
const mdRules = noDefaultExampleRules ? undefined : defaultMdRules;
const expressionRules = noDefaultExampleRules ? undefined : defaultExpressionRules;
if (exampleCodeRegex) {
exampleCodeRegex = utils.getRegexFromString(exampleCodeRegex);
}
if (rejectExampleCodeRegex) {
rejectExampleCodeRegex = utils.getRegexFromString(rejectExampleCodeRegex);
}
/**
* @param {{
* filename: string,
* defaultFileName: string|undefined,
* source: string,
* targetTagName: string,
* rules?: import('eslint').Linter.RulesRecord|undefined,
* lines?: import('../iterateJsdoc.js').Integer,
* cols?: import('../iterateJsdoc.js').Integer,
* skipInit?: boolean,
* sources?: {
* nonJSPrefacingCols: import('../iterateJsdoc.js').Integer,
* nonJSPrefacingLines: import('../iterateJsdoc.js').Integer,
* string: string,
* }[],
* tag?: import('comment-parser').Spec & {
* line?: import('../iterateJsdoc.js').Integer,
* }|{
* line: import('../iterateJsdoc.js').Integer,
* }
* }} cfg
*/
const checkSource = ({
cols = 0,
defaultFileName,
filename,
lines = 0,
rules = expressionRules,
skipInit,
source,
sources = [],
tag = {
line: 0
},
targetTagName
}) => {
if (!skipInit) {
sources.push({
nonJSPrefacingCols: cols,
nonJSPrefacingLines: lines,
string: source
});
}
/**
* @param {{
* nonJSPrefacingCols: import('../iterateJsdoc.js').Integer,
* nonJSPrefacingLines: import('../iterateJsdoc.js').Integer,
* string: string
* }} cfg
*/
const checkRules = function ({
nonJSPrefacingCols,
nonJSPrefacingLines,
string
}) {
const cliConfig = {
allowInlineConfig,
baseConfig,
configFile,
reportUnusedDisableDirectives,
rulePaths,
rules,
useEslintrc: checkEslintrc
};
const cliConfigStr = JSON.stringify(cliConfig);
const src = paddedIndent ? string.replaceAll(new RegExp(`(^|\n) {${paddedIndent}}(?!$)`, 'gv'), '\n') : string;
// Programmatic ESLint API: https://eslint.org/docs/developer-guide/nodejs-api
const fileNameMapKey = filename ? 'a' + cliConfigStr + filename : 'b' + cliConfigStr + defaultFileName;
const file = filename || defaultFileName;
let cliFile;
if (matchingFileNameMap.has(fileNameMapKey)) {
cliFile = matchingFileNameMap.get(fileNameMapKey);
} else {
const cli = new CLIEngine(cliConfig);
let config;
if (filename || checkEslintrc) {
config = cli.getConfigForFile(file);
}
// We need a new instance to ensure that the rules that may only
// be available to `file` (if it has its own `.eslintrc`),
// will be defined.
cliFile = new CLIEngine({
allowInlineConfig,
baseConfig: {
...baseConfig,
...config
},
configFile,
reportUnusedDisableDirectives,
rulePaths,
rules,
useEslintrc: false
});
matchingFileNameMap.set(fileNameMapKey, cliFile);
}
const {
results: [{
messages
}]
} = cliFile.executeOnText(src);
if (!('line' in tag)) {
tag.line = tag.source[0].number;
}
// NOTE: `tag.line` can be 0 if of form `/** @tag ... */`
const codeStartLine =
/**
* @type {import('comment-parser').Spec & {
* line: import('../iterateJsdoc.js').Integer,
* }}
*/
tag.line + nonJSPrefacingLines;
const codeStartCol = likelyNestedJSDocIndentSpace;
for (const {
column,
line,
message,
ruleId,
severity
} of messages) {
const startLine = codeStartLine + line + zeroBasedLineIndexAdjust;
const startCol = codeStartCol + (
// This might not work for line 0, but line 0 is unlikely for examples
line <= 1 ? nonJSPrefacingCols + firstLinePrefixLength : preTagSpaceLength) + column;
report('@' + targetTagName + ' ' + (severity === 2 ? 'error' : 'warning') + (ruleId ? ' (' + ruleId + ')' : '') + ': ' + message, null, {
column: startCol,
line: startLine
});
}
};
for (const targetSource of sources) {
checkRules(targetSource);
}
};
/**
*
* @param {string} filename
* @param {string} [ext] Since `eslint-plugin-markdown` v2, and
* ESLint 7, this is the default which other JS-fenced rules will used.
* Formerly "md" was the default.
* @returns {{defaultFileName: string|undefined, filename: string}}
*/
const getFilenameInfo = (filename, ext = 'md/*.js') => {
let defaultFileName;
if (!filename) {
const jsFileName = context.getFilename();
if (typeof jsFileName === 'string' && jsFileName.includes('.')) {
defaultFileName = jsFileName.replace(/\.[^.]*$/v, `.${ext}`);
} else {
defaultFileName = `dummy.${ext}`;
}
}
return {
defaultFileName,
filename
};
};
if (checkDefaults) {
const filenameInfo = getFilenameInfo(matchingFileNameDefaults, 'jsdoc-defaults');
utils.forEachPreferredTag('default', (tag, targetTagName) => {
if (!tag.description.trim()) {
return;
}
checkSource({
source: `(${utils.getTagDescription(tag)})`,
targetTagName,
...filenameInfo
});
});
}
if (checkParams) {
const filenameInfo = getFilenameInfo(matchingFileNameParams, 'jsdoc-params');
utils.forEachPreferredTag('param', (tag, targetTagName) => {
if (!tag.default || !tag.default.trim()) {
return;
}
checkSource({
source: `(${tag.default})`,
targetTagName,
...filenameInfo
});
});
}
if (checkProperties) {
const filenameInfo = getFilenameInfo(matchingFileNameProperties, 'jsdoc-properties');
utils.forEachPreferredTag('property', (tag, targetTagName) => {
if (!tag.default || !tag.default.trim()) {
return;
}
checkSource({
source: `(${tag.default})`,
targetTagName,
...filenameInfo
});
});
}
const tagName = /** @type {string} */utils.getPreferredTagName({
tagName: 'example'
});
if (!utils.hasTag(tagName)) {
return;
}
const matchingFilenameInfo = getFilenameInfo(matchingFileName);
utils.forEachPreferredTag('example', (tag, targetTagName) => {
let source = /** @type {string} */utils.getTagDescription(tag);
const match = source.match(hasCaptionRegex);
if (captionRequired && (!match || !match[1].trim())) {
report('Caption is expected for examples.', null, tag);
}
source = source.replace(hasCaptionRegex, '');
const [lines, cols] = match ? getLinesCols(match[0]) : [0, 0];
if (exampleCodeRegex && !exampleCodeRegex.test(source) || rejectExampleCodeRegex && rejectExampleCodeRegex.test(source)) {
return;
}
const sources = [];
let skipInit = false;
if (exampleCodeRegex) {
let nonJSPrefacingCols = 0;
let nonJSPrefacingLines = 0;
let startingIndex = 0;
let lastStringCount = 0;
let exampleCode;
exampleCodeRegex.lastIndex = 0;
while ((exampleCode = exampleCodeRegex.exec(source)) !== null) {
const {
'0': n0,
'1': n1,
index
} = exampleCode;
// Count anything preceding user regex match (can affect line numbering)
const preMatch = source.slice(startingIndex, index);
const [preMatchLines, colDelta] = getLinesCols(preMatch);
let nonJSPreface;
let nonJSPrefaceLineCount;
if (n1) {
const idx = n0.indexOf(n1);
nonJSPreface = n0.slice(0, idx);
nonJSPrefaceLineCount = countChars(nonJSPreface, '\n');
} else {
nonJSPreface = '';
nonJSPrefaceLineCount = 0;
}
nonJSPrefacingLines += lastStringCount + preMatchLines + nonJSPrefaceLineCount;
// Ignore `preMatch` delta if newlines here
if (nonJSPrefaceLineCount) {
const charsInLastLine = nonJSPreface.slice(nonJSPreface.lastIndexOf('\n') + 1).length;
nonJSPrefacingCols += charsInLastLine;
} else {
nonJSPrefacingCols += colDelta + nonJSPreface.length;
}
const string = n1 || n0;
sources.push({
nonJSPrefacingCols,
nonJSPrefacingLines,
string
});
startingIndex = exampleCodeRegex.lastIndex;
lastStringCount = countChars(string, '\n');
if (!exampleCodeRegex.global) {
break;
}
}
skipInit = true;
}
checkSource({
cols,
lines,
rules: mdRules,
skipInit,
source,
sources,
tag,
targetTagName,
...matchingFilenameInfo
});
});
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: '@deprecated - Use `getJsdocProcessorPlugin` processor; ensures that (JavaScript) samples within `@example` tags adhere to ESLint rules.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-examples.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
allowInlineConfig: {
default: true,
type: 'boolean'
},
baseConfig: {
type: 'object'
},
captionRequired: {
default: false,
type: 'boolean'
},
checkDefaults: {
default: false,
type: 'boolean'
},
checkEslintrc: {
default: true,
type: 'boolean'
},
checkParams: {
default: false,
type: 'boolean'
},
checkProperties: {
default: false,
type: 'boolean'
},
configFile: {
type: 'string'
},
exampleCodeRegex: {
type: 'string'
},
matchingFileName: {
type: 'string'
},
matchingFileNameDefaults: {
type: 'string'
},
matchingFileNameParams: {
type: 'string'
},
matchingFileNameProperties: {
type: 'string'
},
noDefaultExampleRules: {
default: false,
type: 'boolean'
},
paddedIndent: {
default: 0,
type: 'integer'
},
rejectExampleCodeRegex: {
type: 'string'
},
reportUnusedDisableDirectives: {
default: true,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkExamples.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,4 @@
declare const _default: eslint.Rule.RuleModule;
export default _default;
import eslint from 'eslint';
//# sourceMappingURL=checkExamples.d.ts.map

View file

@ -0,0 +1,170 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @param {string} str
* @param {string[]} excludeTags
* @returns {string}
*/
const maskExcludedContent = (str, excludeTags) => {
const regContent = new RegExp(`([ \\t]+\\*)[ \\t]@(?:${excludeTags.join('|')})(?=[ \\n])([\\w\\|\\W]*?\\n)(?=[ \\t]*\\*(?:[ \\t]*@\\w+\\s|\\/))`, 'gv');
return str.replace(regContent, (_match, margin, code) => {
return (margin + '\n').repeat(code.match(/\n/gv).length);
});
};
/**
* @param {string} str
* @returns {string}
*/
const maskCodeBlocks = str => {
const regContent = /([ \t]+\*)[ \t]```[^\n]*?([\w\|\W]*?\n)(?=[ \t]*\*(?:[ \t]*(?:```|@\w+\s)|\/))/gv;
return str.replaceAll(regContent, (_match, margin, code) => {
return (margin + '\n').repeat(code.match(/\n/gv).length);
});
};
/**
* @param {string[]} lines
* @param {number} lineIndex
* @returns {number}
*/
const getLineNumber = (lines, lineIndex) => {
const precedingText = lines.slice(0, lineIndex).join('\n');
const lineBreaks = precedingText.match(/\n/gv) || [];
return lineBreaks.length + 1;
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdocNode,
report,
sourceCode
}) => {
const options = context.options[0] || {};
const /** @type {{excludeTags: string[], allowIndentedSections: boolean}} */{
allowIndentedSections = false,
excludeTags = ['example']
} = options;
const textWithoutCodeBlocks = maskCodeBlocks(sourceCode.getText(jsdocNode));
const text = excludeTags.length ? maskExcludedContent(textWithoutCodeBlocks, excludeTags) : textWithoutCodeBlocks;
if (allowIndentedSections) {
// When allowIndentedSections is enabled, only check for indentation on tag lines
// and the very first line of the main description
const lines = text.split('\n');
let hasSeenContent = false;
let currentSectionIndent = null;
for (const [lineIndex, line] of lines.entries()) {
// Check for indentation (two or more spaces after *)
const indentMatch = line.match(/^(?:\/?\**|[\t ]*)\*([\t ]{2,})/v);
if (indentMatch) {
// Check what comes after the indentation
const afterIndent = line.slice(indentMatch[0].length);
const indentAmount = indentMatch[1].length;
// If this is a tag line with indentation, always report
if (/^@\w+/v.test(afterIndent)) {
report('There must be no indentation.', null, {
line: getLineNumber(lines, lineIndex)
});
return;
}
// If we haven't seen any content yet (main description first line) and there's content, report
if (!hasSeenContent && afterIndent.trim().length > 0) {
report('There must be no indentation.', null, {
line: getLineNumber(lines, lineIndex)
});
return;
}
// For continuation lines, check consistency
if (hasSeenContent && afterIndent.trim().length > 0) {
if (currentSectionIndent === null) {
// First indented line in this section, set the indent level
currentSectionIndent = indentAmount;
} else if (indentAmount < currentSectionIndent) {
// Indentation is less than the established level (inconsistent)
report('There must be no indentation.', null, {
line: getLineNumber(lines, lineIndex)
});
return;
}
}
} else if (/^\s*\*\s+\S/v.test(line)) {
// No indentation on this line, reset section indent tracking
// (unless it's just whitespace or a closing comment)
currentSectionIndent = null;
}
// Track if we've seen any content (non-whitespace after the *)
if (/^\s*\*\s+\S/v.test(line)) {
hasSeenContent = true;
}
// Reset section indent when we encounter a tag
if (/@\w+/v.test(line)) {
currentSectionIndent = null;
}
}
} else {
const reg = /^(?:\/?\**|[ \t]*)\*[ \t]{2}/gmv;
if (reg.test(text)) {
const lineBreaks = text.slice(0, reg.lastIndex).match(/\n/gv) || [];
report('There must be no indentation.', null, {
line: lineBreaks.length
});
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports invalid padding inside JSDoc blocks.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-indentation.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
allowIndentedSections: {
description: 'Allows indentation of nested sections on subsequent lines (like bullet lists)',
type: 'boolean'
},
excludeTags: {
description: `Array of tags (e.g., \`['example', 'description']\`) whose content will be
"hidden" from the \`check-indentation\` rule. Defaults to \`['example']\`.
By default, the whole JSDoc block will be checked for invalid padding.
That would include \`@example\` blocks too, which can get in the way
of adding full, readable examples of code without ending up with multiple
linting issues.
When disabled (by passing \`excludeTags: []\` option), the following code *will*
report a padding issue:
\`\`\`js
/**
* @example
* anArray.filter((a) => {
* return a.b;
* });
*/
\`\`\``,
items: {
pattern: '^\\S+$',
type: 'string'
},
type: 'array'
}
},
type: 'object'
}],
type: 'layout'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkIndentation.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkIndentation.d.ts.map

View file

@ -0,0 +1,398 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _alignTransform = _interopRequireDefault(require("../alignTransform.cjs"));
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _commentParser = require("comment-parser");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const {
flow: commentFlow
} = _commentParser.transforms;
/**
* Detects if a line starts with a markdown list marker
* Supports: -, *, numbered lists (1., 2., etc.)
* This explicitly excludes hyphens that are part of JSDoc tag syntax
* @param {string} text - The text to check
* @param {boolean} isFirstLineOfTag - True if this is the first line (tag line)
* @returns {boolean} - True if the text starts with a list marker
*/
const startsWithListMarker = (text, isFirstLineOfTag = false) => {
// On the first line of a tag, the hyphen is typically the JSDoc separator,
// not a list marker
if (isFirstLineOfTag) {
return false;
}
// Match lines that start with optional whitespace, then a list marker
// - or * followed by a space
// or a number followed by . or ) and a space
return /^\s*(?:[\-*]|\d+(?:\.|\)))\s+/v.test(text);
};
/**
* Checks if we should allow extra indentation beyond wrapIndent.
* This is true for list continuation lines (lines with more indent than wrapIndent
* that follow a list item).
* @param {import('comment-parser').Spec} tag - The tag being checked
* @param {import('../iterateJsdoc.js').Integer} idx - Current line index (0-based in tag.source.slice(1))
* @returns {boolean} - True if extra indentation should be allowed
*/
const shouldAllowExtraIndent = (tag, idx) => {
// Check if any previous line in this tag had a list marker
// idx is 0-based in the continuation lines (tag.source.slice(1))
// So tag.source[0] is the tag line, tag.source[idx+1] is current line
let hasSeenListMarker = false;
// Check all lines from the tag line onwards
for (let lineIdx = 0; lineIdx <= idx + 1; lineIdx++) {
const line = tag.source[lineIdx];
const isFirstLine = lineIdx === 0;
if (line?.tokens?.description && startsWithListMarker(line.tokens.description, isFirstLine)) {
hasSeenListMarker = true;
break;
}
}
return hasSeenListMarker;
};
/**
* @typedef {{
* postDelimiter: import('../iterateJsdoc.js').Integer,
* postHyphen: import('../iterateJsdoc.js').Integer,
* postName: import('../iterateJsdoc.js').Integer,
* postTag: import('../iterateJsdoc.js').Integer,
* postType: import('../iterateJsdoc.js').Integer,
* }} CustomSpacings
*/
/**
* @param {import('../iterateJsdoc.js').Utils} utils
* @param {import('comment-parser').Spec & {
* line: import('../iterateJsdoc.js').Integer
* }} tag
* @param {CustomSpacings} customSpacings
*/
const checkNotAlignedPerTag = (utils, tag, customSpacings) => {
/*
start +
delimiter +
postDelimiter +
tag +
postTag +
type +
postType +
name +
postName +
description +
end +
lineEnd
*/
/**
* @typedef {"tag"|"type"|"name"|"description"} ContentProp
*/
/** @type {("postDelimiter"|"postTag"|"postType"|"postName")[]} */
let spacerProps;
/** @type {ContentProp[]} */
let contentProps;
const mightHaveNamepath = utils.tagMightHaveNameOrNamepath(tag.tag);
if (mightHaveNamepath) {
spacerProps = ['postDelimiter', 'postTag', 'postType', 'postName'];
contentProps = ['tag', 'type', 'name', 'description'];
} else {
spacerProps = ['postDelimiter', 'postTag', 'postType'];
contentProps = ['tag', 'type', 'description'];
}
const {
tokens
} = tag.source[0];
/**
* @param {import('../iterateJsdoc.js').Integer} idx
* @param {(notRet: boolean, contentProp: ContentProp) => void} [callbck]
*/
const followedBySpace = (idx, callbck) => {
const nextIndex = idx + 1;
return spacerProps.slice(nextIndex).some((spacerProp, innerIdx) => {
const contentProp = contentProps[nextIndex + innerIdx];
const spacePropVal = tokens[spacerProp];
const ret = spacePropVal;
if (callbck) {
callbck(!ret, contentProp);
}
return ret && (callbck || !contentProp);
});
};
const postHyphenSpacing = customSpacings?.postHyphen ?? 1;
const exactHyphenSpacing = new RegExp(`^\\s*-\\s{${postHyphenSpacing},${postHyphenSpacing}}(?!\\s)`, 'v');
const hasNoHyphen = !/^\s*-(?!$)(?=\s)/v.test(tokens.description);
const hasExactHyphenSpacing = exactHyphenSpacing.test(tokens.description);
// If checking alignment on multiple lines, need to check other `source`
// items
// Go through `post*` spacing properties and exit to indicate problem if
// extra spacing detected
const ok = !spacerProps.some((spacerProp, idx) => {
const contentProp = contentProps[idx];
const contentPropVal = tokens[contentProp];
const spacerPropVal = tokens[spacerProp];
const spacing = customSpacings?.[spacerProp] || 1;
// There will be extra alignment if...
// 1. The spaces don't match the space it should have (1 or custom spacing) OR
return spacerPropVal.length !== spacing && spacerPropVal.length !== 0 ||
// 2. There is a (single) space, no immediate content, and yet another
// space is found subsequently (not separated by intervening content)
spacerPropVal && !contentPropVal && followedBySpace(idx);
}) && (hasNoHyphen || hasExactHyphenSpacing);
if (ok) {
return;
}
const fix = () => {
for (const [idx, spacerProp] of spacerProps.entries()) {
const contentProp = contentProps[idx];
const contentPropVal = tokens[contentProp];
if (contentPropVal) {
const spacing = customSpacings?.[spacerProp] || 1;
tokens[spacerProp] = ''.padStart(spacing, ' ');
followedBySpace(idx, (hasSpace, contentPrp) => {
if (hasSpace) {
tokens[contentPrp] = '';
}
});
} else {
tokens[spacerProp] = '';
}
}
if (!hasExactHyphenSpacing) {
const hyphenSpacing = /^\s*-\s+/v;
tokens.description = tokens.description.replace(hyphenSpacing, '-' + ''.padStart(postHyphenSpacing, ' '));
}
utils.setTag(tag, tokens);
};
utils.reportJSDoc('Expected JSDoc block lines to not be aligned.', tag, fix, true);
};
/**
* @param {object} cfg
* @param {CustomSpacings} cfg.customSpacings
* @param {string} cfg.indent
* @param {import('comment-parser').Block} cfg.jsdoc
* @param {import('eslint').Rule.Node & {
* range: [number, number]
* }} cfg.jsdocNode
* @param {boolean} cfg.preserveMainDescriptionPostDelimiter
* @param {import('../iterateJsdoc.js').Report} cfg.report
* @param {string[]} cfg.tags
* @param {import('../iterateJsdoc.js').Utils} cfg.utils
* @param {string} cfg.wrapIndent
* @param {boolean} cfg.disableWrapIndent
* @returns {void}
*/
const checkAlignment = ({
customSpacings,
disableWrapIndent,
indent,
jsdoc,
jsdocNode,
preserveMainDescriptionPostDelimiter,
report,
tags,
utils,
wrapIndent
}) => {
const transform = commentFlow((0, _alignTransform.default)({
customSpacings,
disableWrapIndent,
indent,
preserveMainDescriptionPostDelimiter,
tags,
wrapIndent
}));
const transformedJsdoc = transform(jsdoc);
const comment = '/*' +
/**
* @type {import('eslint').Rule.Node & {
* range: [number, number], value: string
* }}
*/
jsdocNode.value + '*/';
const formatted = utils.stringify(transformedJsdoc).trimStart();
if (comment !== formatted) {
report('Expected JSDoc block lines to be aligned.', /** @type {import('eslint').Rule.ReportFixer} */fixer => {
return fixer.replaceText(jsdocNode, formatted);
});
}
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
indent,
jsdoc,
jsdocNode,
report,
utils
}) => {
const {
customSpacings,
disableWrapIndent = false,
preserveMainDescriptionPostDelimiter,
tags: applicableTags = ['param', 'arg', 'argument', 'property', 'prop', 'returns', 'return', 'template'],
wrapIndent = ''
} = context.options[1] || {};
if (context.options[0] === 'always') {
// Skip if it contains only a single line.
if (!(
/**
* @type {import('eslint').Rule.Node & {
* range: [number, number], value: string
* }}
*/
jsdocNode.value.includes('\n'))) {
return;
}
checkAlignment({
customSpacings,
disableWrapIndent,
indent,
jsdoc,
jsdocNode,
preserveMainDescriptionPostDelimiter,
report,
tags: applicableTags,
utils,
wrapIndent
});
return;
}
const foundTags = utils.getPresentTags(applicableTags);
if (context.options[0] !== 'any') {
for (const tag of foundTags) {
checkNotAlignedPerTag(utils,
/**
* @type {import('comment-parser').Spec & {
* line: import('../iterateJsdoc.js').Integer
* }}
*/
tag, customSpacings);
}
}
for (const tag of foundTags) {
if (tag.source.length > 1) {
let idx = 0;
for (const {
tokens
// Avoid the tag line
} of tag.source.slice(1)) {
idx++;
if (!tokens.description ||
// Avoid first lines after multiline type
tokens.type || tokens.name) {
continue;
}
// Don't include a single separating space/tab
const actualIndent = tokens.postDelimiter.slice(1);
const hasCorrectWrapIndent = actualIndent === wrapIndent;
// Allow extra indentation if this line or previous lines contain list markers
// This preserves nested list structure
const hasExtraIndent = actualIndent.length > wrapIndent.length && actualIndent.startsWith(wrapIndent);
const isInListContext = shouldAllowExtraIndent(tag, idx - 1);
if (!disableWrapIndent && !hasCorrectWrapIndent && !(hasExtraIndent && isInListContext)) {
utils.reportJSDoc('Expected wrap indent', {
line: tag.source[0].number + idx
}, () => {
tokens.postDelimiter = tokens.postDelimiter.charAt(0) + wrapIndent;
});
return;
}
}
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports invalid alignment of JSDoc block lines.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-line-alignment.md#repos-sticky-header'
},
fixable: 'whitespace',
schema: [{
description: `If the string value is
\`"always"\` then a problem is raised when the lines are not aligned.
If it is \`"never"\` then a problem should be raised when there is more than
one space between each line's parts. If it is \`"any"\`, no alignment is made.
Defaults to \`"never"\`.
Note that in addition to alignment, the "never" and "always" options will both
ensure that at least one space is present after the asterisk delimiter.`,
enum: ['always', 'never', 'any'],
type: 'string'
}, {
additionalProperties: false,
properties: {
customSpacings: {
additionalProperties: false,
description: `An object with any of the following spacing keys set to an integer.
If a spacing is not defined, it defaults to one.`,
properties: {
postDelimiter: {
description: 'Affects spacing after the asterisk (e.g., `* @param`)',
type: 'integer'
},
postHyphen: {
description: 'Affects spacing after any hyphens in the description (e.g., `* @param {someType} name - A description`)',
type: 'integer'
},
postName: {
description: 'Affects spacing after the name (e.g., `* @param {someType} name `)',
type: 'integer'
},
postTag: {
description: 'Affects spacing after the tag (e.g., `* @param `)',
type: 'integer'
},
postType: {
description: 'Affects spacing after the type (e.g., `* @param {someType} `)',
type: 'integer'
}
},
type: 'object'
},
disableWrapIndent: {
description: 'Disables `wrapIndent`; existing wrap indentation is preserved without changes.',
type: 'boolean'
},
preserveMainDescriptionPostDelimiter: {
default: false,
description: `A boolean to determine whether to preserve the post-delimiter spacing of the
main description. If \`false\` or unset, will be set to a single space.`,
type: 'boolean'
},
tags: {
description: `Use this to change the tags which are sought for alignment changes. Defaults to an array of
\`['param', 'arg', 'argument', 'property', 'prop', 'returns', 'return', 'template']\`.`,
items: {
type: 'string'
},
type: 'array'
},
wrapIndent: {
description: `The indent that will be applied for tag text after the first line.
Default to the empty string (no indent).`,
type: 'string'
}
},
type: 'object'
}],
type: 'layout'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkLineAlignment.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,10 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
export type CustomSpacings = {
postDelimiter: import("../iterateJsdoc.js").Integer;
postHyphen: import("../iterateJsdoc.js").Integer;
postName: import("../iterateJsdoc.js").Integer;
postTag: import("../iterateJsdoc.js").Integer;
postType: import("../iterateJsdoc.js").Integer;
};
//# sourceMappingURL=checkLineAlignment.d.ts.map

View file

@ -0,0 +1,406 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @param {string} targetTagName
* @param {boolean} allowExtraTrailingParamDocs
* @param {boolean} checkDestructured
* @param {boolean} checkRestProperty
* @param {RegExp} checkTypesRegex
* @param {boolean} disableExtraPropertyReporting
* @param {boolean} disableMissingParamChecks
* @param {boolean} enableFixer
* @param {import('../jsdocUtils.js').ParamNameInfo[]} functionParameterNames
* @param {import('comment-parser').Block} jsdoc
* @param {import('../iterateJsdoc.js').Utils} utils
* @param {import('../iterateJsdoc.js').Report} report
* @returns {boolean}
*/
const validateParameterNames = (targetTagName, allowExtraTrailingParamDocs, checkDestructured, checkRestProperty, checkTypesRegex, disableExtraPropertyReporting, disableMissingParamChecks, enableFixer, functionParameterNames, jsdoc, utils, report) => {
const paramTags = Object.entries(jsdoc.tags).filter(([, tag]) => {
return tag.tag === targetTagName;
});
const paramTagsNonNested = paramTags.filter(([, tag]) => {
return !tag.name.includes('.');
});
let dotted = 0;
let thisOffset = 0;
return paramTags.some(([, tag
// eslint-disable-next-line complexity
], index) => {
/** @type {import('../iterateJsdoc.js').Integer} */
let tagsIndex;
const dupeTagInfo = paramTags.find(([tgsIndex, tg], idx) => {
tagsIndex = Number(tgsIndex);
return tg.name === tag.name && idx !== index;
});
if (dupeTagInfo) {
utils.reportJSDoc(`Duplicate @${targetTagName} "${tag.name}"`, dupeTagInfo[1], enableFixer ? () => {
utils.removeTag(tagsIndex);
} : null);
return true;
}
if (tag.name.includes('.')) {
dotted++;
return false;
}
let functionParameterName = functionParameterNames[index - dotted + thisOffset];
if (functionParameterName === 'this' && tag.name.trim() !== 'this') {
++thisOffset;
functionParameterName = functionParameterNames[index - dotted + thisOffset];
}
if (!functionParameterName) {
if (allowExtraTrailingParamDocs) {
return false;
}
report(`@${targetTagName} "${tag.name}" does not match an existing function parameter.`, null, tag);
return true;
}
if (typeof functionParameterName === 'object' && 'name' in functionParameterName && Array.isArray(functionParameterName.name)) {
const actualName = tag.name.trim();
const expectedName = functionParameterName.name[index];
if (actualName === expectedName) {
thisOffset--;
return false;
}
report(`Expected @${targetTagName} name to be "${expectedName}". Got "${actualName}".`, null, tag);
return true;
}
if (Array.isArray(functionParameterName)) {
if (!checkDestructured) {
return false;
}
if (tag.type && tag.type.search(checkTypesRegex) === -1) {
return false;
}
const [parameterName, {
annotationParamName,
hasPropertyRest,
names: properties,
rests
}] =
/**
* @type {[string | undefined, import('../jsdocUtils.js').FlattendRootInfo & {
* annotationParamName?: string | undefined;
}]} */
functionParameterName;
if (annotationParamName !== undefined) {
const name = tag.name.trim();
if (name !== annotationParamName) {
report(`@${targetTagName} "${name}" does not match parameter name "${annotationParamName}"`, null, tag);
}
}
const tagName = parameterName === undefined ? tag.name.trim() : parameterName;
const expectedNames = properties.map(name => {
return `${tagName}.${name}`;
});
const actualNames = paramTags.map(([, paramTag]) => {
return paramTag.name.trim();
});
const actualTypes = paramTags.map(([, paramTag]) => {
return paramTag.type;
});
const missingProperties = [];
/** @type {string[]} */
const notCheckingNames = [];
for (const [idx, name] of expectedNames.entries()) {
if (notCheckingNames.some(notCheckingName => {
return name.startsWith(notCheckingName);
})) {
continue;
}
const actualNameIdx = actualNames.findIndex(actualName => {
return utils.comparePaths(name)(actualName);
});
if (actualNameIdx === -1) {
if (!checkRestProperty && rests[idx]) {
continue;
}
const missingIndex = actualNames.findIndex(actualName => {
return utils.pathDoesNotBeginWith(name, actualName);
});
const line = tag.source[0].number - 1 + (missingIndex > -1 ? missingIndex : actualNames.length);
missingProperties.push({
name,
tagPlacement: {
line: line === 0 ? 1 : line
}
});
} else if (actualTypes[actualNameIdx].search(checkTypesRegex) === -1 && actualTypes[actualNameIdx] !== '') {
notCheckingNames.push(name);
}
}
const hasMissing = missingProperties.length;
if (hasMissing) {
for (const {
name: missingProperty,
tagPlacement
} of missingProperties) {
report(`Missing @${targetTagName} "${missingProperty}"`, null, tagPlacement);
}
}
if (!hasPropertyRest || checkRestProperty) {
/** @type {[string, import('comment-parser').Spec][]} */
const extraProperties = [];
for (const [idx, name] of actualNames.entries()) {
const match = name.startsWith(tag.name.trim() + '.');
if (match && !expectedNames.some(utils.comparePaths(name)) && !utils.comparePaths(name)(tag.name) && (!disableExtraPropertyReporting || properties.some(prop => {
return prop.split('.').length >= name.split('.').length - 1;
}))) {
extraProperties.push([name, paramTags[idx][1]]);
}
}
if (extraProperties.length) {
for (const [extraProperty, tg] of extraProperties) {
report(`@${targetTagName} "${extraProperty}" does not exist on ${tag.name}`, null, tg);
}
return true;
}
}
return hasMissing;
}
let funcParamName;
if (typeof functionParameterName === 'object') {
const {
name
} = functionParameterName;
funcParamName = name;
} else {
funcParamName = functionParameterName;
}
if (funcParamName !== tag.name.trim()) {
// Todo: Improve for array or object child items
const actualNames = paramTagsNonNested.map(([, {
name
}]) => {
return name.trim();
});
const expectedNames = functionParameterNames.map((item, idx) => {
if (
/**
* @type {[string|undefined, (import('../jsdocUtils.js').FlattendRootInfo & {
* annotationParamName?: string,
})]} */
item?.[1]?.names) {
return actualNames[idx];
}
return item;
}).filter(item => {
return item !== 'this';
});
// When disableMissingParamChecks is true tag names can be omitted.
// Report when the tag names do not match the expected names or they are used out of order.
if (disableMissingParamChecks) {
const usedExpectedNames = expectedNames.map(a => {
return a?.toString();
}).filter(expectedName => {
return expectedName && actualNames.includes(expectedName);
});
const usedInOrder = actualNames.every((actualName, idx) => {
return actualName === usedExpectedNames[idx];
});
if (usedInOrder) {
return false;
}
}
report(`Expected @${targetTagName} names to be "${expectedNames.map(expectedName => {
return typeof expectedName === 'object' && 'name' in expectedName && expectedName.restElement ? '...' + expectedName.name : expectedName;
}).join(', ')}". Got "${actualNames.join(', ')}".`, null, tag);
return true;
}
return false;
});
};
/**
* @param {string} targetTagName
* @param {boolean} _allowExtraTrailingParamDocs
* @param {{
* name: string,
* idx: import('../iterateJsdoc.js').Integer
* }[]} jsdocParameterNames
* @param {import('comment-parser').Block} jsdoc
* @param {import('../iterateJsdoc.js').Report} report
* @returns {boolean}
*/
const validateParameterNamesDeep = (targetTagName, _allowExtraTrailingParamDocs, jsdocParameterNames, jsdoc, report) => {
/** @type {string} */
let lastRealParameter;
return jsdocParameterNames.some(({
idx,
name: jsdocParameterName
}) => {
const isPropertyPath = jsdocParameterName.includes('.');
if (isPropertyPath) {
if (!lastRealParameter) {
report(`@${targetTagName} path declaration ("${jsdocParameterName}") appears before any real parameter.`, null, jsdoc.tags[idx]);
return true;
}
let pathRootNodeName = jsdocParameterName.slice(0, jsdocParameterName.indexOf('.'));
if (pathRootNodeName.endsWith('[]')) {
pathRootNodeName = pathRootNodeName.slice(0, -2);
}
if (pathRootNodeName !== lastRealParameter) {
report(`@${targetTagName} path declaration ("${jsdocParameterName}") root node name ("${pathRootNodeName}") ` + `does not match previous real parameter name ("${lastRealParameter}").`, null, jsdoc.tags[idx]);
return true;
}
} else {
lastRealParameter = jsdocParameterName;
}
return false;
});
};
const allowedNodes = ['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression', 'TSDeclareFunction',
// Add this to above defaults
'TSMethodSignature'];
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
node,
report,
utils
}) => {
const {
allowExtraTrailingParamDocs,
checkDestructured = true,
checkRestProperty = false,
checkTypesPattern = '/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/',
disableExtraPropertyReporting = false,
disableMissingParamChecks = false,
enableFixer = false,
useDefaultObjectProperties = false
} = context.options[0] || {};
// Although we might just remove global settings contexts from applying to
// this rule (as they can cause problems with `getFunctionParameterNames`
// checks if they are not functions but say variables), the user may
// instead wish to narrow contexts in those settings, so this check
// is still useful
if (!allowedNodes.includes(/** @type {import('estree').Node} */node.type)) {
return;
}
const checkTypesRegex = utils.getRegexFromString(checkTypesPattern);
const jsdocParameterNamesDeep = utils.getJsdocTagsDeep('param');
if (!jsdocParameterNamesDeep || !jsdocParameterNamesDeep.length) {
return;
}
const functionParameterNames = utils.getFunctionParameterNames(useDefaultObjectProperties);
const targetTagName = /** @type {string} */utils.getPreferredTagName({
tagName: 'param'
});
const isError = validateParameterNames(targetTagName, allowExtraTrailingParamDocs, checkDestructured, checkRestProperty, checkTypesRegex, disableExtraPropertyReporting, disableMissingParamChecks, enableFixer, functionParameterNames, jsdoc, utils, report);
if (isError || !checkDestructured) {
return;
}
validateParameterNamesDeep(targetTagName, allowExtraTrailingParamDocs, jsdocParameterNamesDeep, jsdoc, report);
}, {
contextDefaults: allowedNodes,
meta: {
docs: {
description: 'Checks for dupe `@param` names, that nested param names have roots, and that parameter names in function declarations match JSDoc param names.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-param-names.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
allowExtraTrailingParamDocs: {
description: `If set to \`true\`, this option will allow extra \`@param\` definitions (e.g.,
representing future expected or virtual params) to be present without needing
their presence within the function signature. Other inconsistencies between
\`@param\`'s and present function parameters will still be reported.`,
type: 'boolean'
},
checkDestructured: {
description: 'Whether to check destructured properties. Defaults to `true`.',
type: 'boolean'
},
checkRestProperty: {
description: `If set to \`true\`, will require that rest properties are documented and
that any extraneous properties (which may have been within the rest property)
are documented. Defaults to \`false\`.`,
type: 'boolean'
},
checkTypesPattern: {
description: `Defines a regular expression pattern to indicate which types should be
checked for destructured content (and that those not matched should not
be checked).
When one specifies a type, unless it is of a generic type, like \`object\`
or \`array\`, it may be considered unnecessary to have that object's
destructured components required, especially where generated docs will
link back to the specified type. For example:
\`\`\`js
/**
* @param {SVGRect} bbox - a SVGRect
*/
export const bboxToObj = function ({x, y, width, height}) {
return {x, y, width, height};
};
\`\`\`
By default \`checkTypesPattern\` is set to
\`/^(?:[oO]bject|[aA]rray|PlainObject|Generic(?:Object|Array))$/v\`,
meaning that destructuring will be required only if the type of the \`@param\`
(the text between curly brackets) is a match for "Object" or "Array" (with or
without initial caps), "PlainObject", or "GenericObject", "GenericArray" (or
if no type is present). So in the above example, the lack of a match will
mean that no complaint will be given about the undocumented destructured
parameters.
Note that the \`/\` delimiters are optional, but necessary to add flags.
Defaults to using (only) the \`v\` flag, so to add your own flags, encapsulate
your expression as a string, but like a literal, e.g., \`/^object$/vi\`.
You could set this regular expression to a more expansive list, or you
could restrict it such that even types matching those strings would not
need destructuring.`,
type: 'string'
},
disableExtraPropertyReporting: {
description: `Whether to check for extra destructured properties. Defaults to \`false\`. Change
to \`true\` if you want to be able to document properties which are not actually
destructured. Keep as \`false\` if you expect properties to be documented in
their own types. Note that extra properties will always be reported if another
item at the same level is destructured as destructuring will prevent other
access and this option is only intended to permit documenting extra properties
that are available and actually used in the function.`,
type: 'boolean'
},
disableMissingParamChecks: {
description: 'Whether to avoid checks for missing `@param` definitions. Defaults to `false`. Change to `true` if you want to be able to omit properties.',
type: 'boolean'
},
enableFixer: {
description: `Set to \`true\` to auto-remove \`@param\` duplicates (based on identical
names).
Note that this option will remove duplicates of the same name even if
the definitions do not match in other ways (e.g., the second param will
be removed even if it has a different type or description).`,
type: 'boolean'
},
useDefaultObjectProperties: {
description: `Set to \`true\` if you wish to avoid reporting of child property documentation
where instead of destructuring, a whole plain object is supplied as default
value but you wish its keys to be considered as signalling that the properties
are present and can therefore be documented. Defaults to \`false\`.`,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkParamNames.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkParamNames.d.ts.map

View file

@ -0,0 +1,135 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @param {string} targetTagName
* @param {boolean} enableFixer
* @param {import('comment-parser').Block} jsdoc
* @param {import('../iterateJsdoc.js').Utils} utils
* @returns {boolean}
*/
const validatePropertyNames = (targetTagName, enableFixer, jsdoc, utils) => {
const jsdocTypedefs = utils.getJsdocTagsDeep('typedef');
let propertyTagGroups;
if (jsdocTypedefs && jsdocTypedefs.length > 1) {
propertyTagGroups = jsdocTypedefs.map(({
idx
}, index) => {
return Object.entries(jsdoc.tags).slice(idx, jsdocTypedefs[index + 1]?.idx);
});
} else {
propertyTagGroups = [Object.entries(jsdoc.tags)];
}
return propertyTagGroups.some(propertyTagGroup => {
const propertyTags = propertyTagGroup.filter(([, tag]) => {
return tag.tag === targetTagName;
});
return propertyTags.some(([, tag], index) => {
/** @type {import('../iterateJsdoc.js').Integer} */
let tagsIndex;
const dupeTagInfo = propertyTags.find(([tgsIndex, tg], idx) => {
tagsIndex = Number(tgsIndex);
return tg.name === tag.name && idx !== index;
});
if (dupeTagInfo) {
utils.reportJSDoc(`Duplicate @${targetTagName} "${tag.name}"`, dupeTagInfo[1], enableFixer ? () => {
utils.removeTag(tagsIndex);
} : null);
return true;
}
return false;
});
});
};
/**
* @param {string} targetTagName
* @param {{
* idx: number;
* name: string;
* type: string;
* }[]} jsdocPropertyNames
* @param {import('comment-parser').Block} jsdoc
* @param {import('../iterateJsdoc.js').Report} report
*/
const validatePropertyNamesDeep = (targetTagName, jsdocPropertyNames, jsdoc, report) => {
/** @type {string} */
let lastRealProperty;
return jsdocPropertyNames.some(({
idx,
name: jsdocPropertyName
}) => {
const isPropertyPath = jsdocPropertyName.includes('.');
if (isPropertyPath) {
if (!lastRealProperty) {
report(`@${targetTagName} path declaration ("${jsdocPropertyName}") appears before any real property.`, null, jsdoc.tags[idx]);
return true;
}
let pathRootNodeName = jsdocPropertyName.slice(0, jsdocPropertyName.indexOf('.'));
if (pathRootNodeName.endsWith('[]')) {
pathRootNodeName = pathRootNodeName.slice(0, -2);
}
if (pathRootNodeName !== lastRealProperty) {
report(`@${targetTagName} path declaration ("${jsdocPropertyName}") root node name ("${pathRootNodeName}") ` + `does not match previous real property name ("${lastRealProperty}").`, null, jsdoc.tags[idx]);
return true;
}
} else {
lastRealProperty = jsdocPropertyName;
}
return false;
});
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
report,
utils
}) => {
const {
enableFixer = false
} = context.options[0] || {};
const jsdocPropertyNamesDeep = utils.getJsdocTagsDeep('property');
if (!jsdocPropertyNamesDeep || !jsdocPropertyNamesDeep.length) {
return;
}
const targetTagName = /** @type {string} */utils.getPreferredTagName({
tagName: 'property'
});
const isError = validatePropertyNames(targetTagName, enableFixer, jsdoc, utils);
if (isError) {
return;
}
validatePropertyNamesDeep(targetTagName, jsdocPropertyNamesDeep, jsdoc, report);
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Ensures that property names in JSDoc are not duplicated on the same block and that nested properties have defined roots.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-property-names.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
enableFixer: {
description: `Set to \`true\` to auto-remove \`@property\` duplicates (based on
identical names).
Note that this option will remove duplicates of the same name even if
the definitions do not match in other ways (e.g., the second property will
be removed even if it has a different type or description).`,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkPropertyNames.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkPropertyNames.d.ts.map

View file

@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
jsdoc,
report,
settings
}) => {
const {
mode
} = settings;
// Don't check for "permissive" and "closure"
if (mode === 'jsdoc' || mode === 'typescript') {
for (const tag of jsdoc.tags) {
if (tag.type.slice(-1) === '=') {
report('Syntax should not be Google Closure Compiler style.', null, tag);
break;
}
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode).',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-syntax.md#repos-sticky-header'
},
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkSyntax.cjs.map

View file

@ -0,0 +1 @@
{"version":3,"file":"checkSyntax.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","_default","exports","iterateJsdoc","jsdoc","report","settings","mode","tag","tags","type","slice","iterateAllJsdocs","meta","docs","description","url","module"],"sources":["../../src/rules/checkSyntax.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nexport default iterateJsdoc(({\n jsdoc,\n report,\n settings,\n}) => {\n const {\n mode,\n } = settings;\n\n // Don't check for \"permissive\" and \"closure\"\n if (mode === 'jsdoc' || mode === 'typescript') {\n for (const tag of jsdoc.tags) {\n if (tag.type.slice(-1) === '=') {\n report('Syntax should not be Google Closure Compiler style.', null, tag);\n break;\n }\n }\n }\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Reports against syntax not valid for the mode (e.g., Google Closure Compiler in non-Closure mode).',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-syntax.md#repos-sticky-header',\n },\n type: 'suggestion',\n },\n});\n"],"mappings":";;;;;;AAAA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA8C,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAE/B,IAAAG,qBAAY,EAAC,CAAC;EAC3BC,KAAK;EACLC,MAAM;EACNC;AACF,CAAC,KAAK;EACJ,MAAM;IACJC;EACF,CAAC,GAAGD,QAAQ;;EAEZ;EACA,IAAIC,IAAI,KAAK,OAAO,IAAIA,IAAI,KAAK,YAAY,EAAE;IAC7C,KAAK,MAAMC,GAAG,IAAIJ,KAAK,CAACK,IAAI,EAAE;MAC5B,IAAID,GAAG,CAACE,IAAI,CAACC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;QAC9BN,MAAM,CAAC,qDAAqD,EAAE,IAAI,EAAEG,GAAG,CAAC;QACxE;MACF;IACF;EACF;AACF,CAAC,EAAE;EACDI,gBAAgB,EAAE,IAAI;EACtBC,IAAI,EAAE;IACJC,IAAI,EAAE;MACJC,WAAW,EAAE,oGAAoG;MACjHC,GAAG,EAAE;IACP,CAAC;IACDN,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAAAO,MAAA,CAAAf,OAAA,GAAAA,OAAA,CAAAF,OAAA","ignoreList":[]}

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkSyntax.d.ts.map

View file

@ -0,0 +1,304 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _escapeStringRegexp = _interopRequireDefault(require("escape-string-regexp"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
// https://babeljs.io/docs/en/babel-plugin-transform-react-jsx/
const jsxTagNames = new Set(['jsx', 'jsxFrag', 'jsxImportSource', 'jsxRuntime']);
const typedTagsAlwaysUnnecessary = new Set(['augments', 'callback', 'class', 'enum', 'implements', 'private', 'property', 'protected', 'public', 'readonly', 'this', 'type', 'typedef']);
const typedTagsNeedingName = new Set(['template']);
const typedTagsUnnecessaryOutsideDeclare = new Set(['abstract', 'access', 'class', 'constant', 'constructs', 'default', 'enum', 'export', 'exports', 'function', 'global', 'inherits', 'instance', 'interface', 'member', 'memberof', 'memberOf', 'method', 'mixes', 'mixin', 'module', 'name', 'namespace', 'override', 'property', 'requires', 'static', 'this']);
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
jsdocNode,
node,
report,
settings,
sourceCode,
utils
}) => {
const
/**
* @type {{
* definedTags: string[],
* enableFixer: boolean,
* inlineTags: string[],
* jsxTags: boolean,
* typed: boolean
}} */
{
definedTags = [],
enableFixer = true,
inlineTags = ['link', 'linkcode', 'linkplain', 'tutorial'],
jsxTags,
typed
} = context.options[0] || {};
/** @type {(string|undefined)[]} */
let definedPreferredTags = [];
const {
structuredTags,
tagNamePreference
} = settings;
const definedStructuredTags = Object.keys(structuredTags);
const definedNonPreferredTags = Object.keys(tagNamePreference);
if (definedNonPreferredTags.length) {
definedPreferredTags = Object.values(tagNamePreference).map(preferredTag => {
if (typeof preferredTag === 'string') {
// May become an empty string but will be filtered out below
return preferredTag;
}
if (!preferredTag) {
return undefined;
}
if (typeof preferredTag !== 'object') {
utils.reportSettings('Invalid `settings.jsdoc.tagNamePreference`. Values must be falsy, a string, or an object.');
}
return preferredTag.replacement;
}).filter(Boolean);
}
/**
* @param {import('eslint').Rule.Node} subNode
* @returns {boolean}
*/
const isInAmbientContext = subNode => {
return subNode.type === 'Program' ? /* c8 ignore next -- Support old ESLint */
(context.filename ?? context.getFilename()).endsWith('.d.ts') : Boolean(/** @type {import('@typescript-eslint/types').TSESTree.VariableDeclaration} */subNode.declare) || isInAmbientContext(subNode.parent);
};
/**
* @param {import('comment-parser').Spec} jsdocTag
* @returns {boolean}
*/
const tagIsRedundantWhenTyped = jsdocTag => {
if (!typedTagsUnnecessaryOutsideDeclare.has(jsdocTag.tag)) {
return false;
}
if (jsdocTag.tag === 'default') {
return false;
}
if (node === null) {
return false;
}
/* c8 ignore next -- Support old ESLint */
if ((context.filename ?? context.getFilename()).endsWith('.d.ts') && [null, 'Program', undefined].includes(node?.parent?.type)) {
return false;
}
if (isInAmbientContext(/** @type {import('eslint').Rule.Node} */node)) {
return false;
}
return true;
};
/**
* @param {string} message
* @param {import('comment-parser').Spec} jsdocTag
* @param {import('../iterateJsdoc.js').Integer} tagIndex
* @param {Partial<import('comment-parser').Tokens>} [additionalTagChanges]
* @returns {void}
*/
const reportWithTagRemovalFixer = (message, jsdocTag, tagIndex, additionalTagChanges) => {
utils.reportJSDoc(message, jsdocTag, enableFixer ? () => {
if (jsdocTag.description.trim()) {
utils.changeTag(jsdocTag, {
postType: '',
type: '',
...additionalTagChanges
});
} else {
utils.removeTag(tagIndex, {
removeEmptyBlock: true
});
}
} : null, true);
};
/**
* @param {import('comment-parser').Spec} jsdocTag
* @param {import('../iterateJsdoc.js').Integer} tagIndex
* @returns {boolean}
*/
const checkTagForTypedValidity = (jsdocTag, tagIndex) => {
if (typedTagsAlwaysUnnecessary.has(jsdocTag.tag)) {
reportWithTagRemovalFixer(`'@${jsdocTag.tag}' is redundant when using a type system.`, jsdocTag, tagIndex, {
postTag: '',
tag: ''
});
return true;
}
if (tagIsRedundantWhenTyped(jsdocTag)) {
reportWithTagRemovalFixer(`'@${jsdocTag.tag}' is redundant outside of ambient (\`declare\`/\`.d.ts\`) contexts when using a type system.`, jsdocTag, tagIndex);
return true;
}
if (typedTagsNeedingName.has(jsdocTag.tag) && !jsdocTag.name) {
reportWithTagRemovalFixer(`'@${jsdocTag.tag}' without a name is redundant when using a type system.`, jsdocTag, tagIndex);
return true;
}
return false;
};
for (let tagIndex = 0; tagIndex < jsdoc.tags.length; tagIndex += 1) {
const jsdocTag = jsdoc.tags[tagIndex];
const tagName = jsdocTag.tag;
if (jsxTags && jsxTagNames.has(tagName)) {
continue;
}
if (typed && checkTagForTypedValidity(jsdocTag, tagIndex)) {
continue;
}
const validTags = [...definedTags, ...(/** @type {string[]} */definedPreferredTags), ...definedNonPreferredTags, ...definedStructuredTags, ...(typed ? typedTagsNeedingName : [])];
if (utils.isValidTag(tagName, validTags)) {
let preferredTagName = utils.getPreferredTagName({
allowObjectReturn: true,
defaultMessage: `Blacklisted tag found (\`@${tagName}\`)`,
tagName
});
if (!preferredTagName) {
continue;
}
let message;
if (typeof preferredTagName === 'object') {
({
message,
replacement: preferredTagName
} = /** @type {{message: string; replacement?: string | undefined;}} */
preferredTagName);
}
if (!message) {
message = `Invalid JSDoc tag (preference). Replace "${tagName}" JSDoc tag with "${preferredTagName}".`;
}
if (preferredTagName !== tagName) {
report(message, fixer => {
const replacement = sourceCode.getText(jsdocNode).replace(new RegExp(`@${(0, _escapeStringRegexp.default)(tagName)}\\b`, 'v'), `@${preferredTagName}`);
return fixer.replaceText(jsdocNode, replacement);
}, jsdocTag);
}
} else {
report(`Invalid JSDoc tag name "${tagName}".`, null, jsdocTag);
}
}
for (const inlineTag of utils.getInlineTags()) {
if (!inlineTags.includes(inlineTag.tag)) {
report(`Invalid JSDoc inline tag name "${inlineTag.tag}"`, null, inlineTag);
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports invalid block tag names.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-tag-names.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
definedTags: {
description: `Use an array of \`definedTags\` strings to configure additional, allowed tags.
The format is as follows:
\`\`\`json
{
"definedTags": ["note", "record"]
}
\`\`\``,
items: {
type: 'string'
},
type: 'array'
},
enableFixer: {
description: 'Set to `false` to disable auto-removal of types that are redundant with the [`typed` option](#typed).',
type: 'boolean'
},
inlineTags: {
description: `List of tags to allow inline.
Defaults to array of \`'link', 'linkcode', 'linkplain', 'tutorial'\``,
items: {
type: 'string'
},
type: 'array'
},
jsxTags: {
description: `If this is set to \`true\`, all of the following tags used to control JSX output are allowed:
\`\`\`
jsx
jsxFrag
jsxImportSource
jsxRuntime
\`\`\`
For more information, see the [babel documentation](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx).`,
type: 'boolean'
},
typed: {
description: `If this is set to \`true\`, additionally checks for tag names that are redundant when using a type checker such as TypeScript.
These tags are always unnecessary when using TypeScript or similar:
\`\`\`
augments
callback
class
enum
implements
private
property
protected
public
readonly
this
type
typedef
\`\`\`
These tags are unnecessary except when inside a TypeScript \`declare\` context:
\`\`\`
abstract
access
class
constant
constructs
default
enum
export
exports
function
global
inherits
instance
interface
member
memberof
memberOf
method
mixes
mixin
module
name
namespace
override
property
requires
static
this
\`\`\``,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkTagNames.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkTagNames.d.ts.map

View file

@ -0,0 +1,185 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireWildcard(require("../iterateJsdoc.cjs"));
var _jsdocUtils = require("../jsdocUtils.cjs");
var _jsdoccomment = require("@es-joy/jsdoccomment");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
var _default = exports.default = (0, _iterateJsdoc.default)(({
jsdoc,
node,
report,
settings,
sourceCode,
utils
}) => {
const {
mode
} = settings;
const tgName = /** @type {string} */utils.getPreferredTagName({
tagName: 'template'
});
if (!tgName) {
return;
}
const templateTags = utils.getTags(tgName);
const usedNames = new Set();
/**
* @param {string} potentialType
*/
const checkForUsedTypes = potentialType => {
let parsedType;
try {
parsedType = mode === 'permissive' ? (0, _jsdoccomment.tryParse)(/** @type {string} */potentialType) : (0, _jsdoccomment.parse)(/** @type {string} */potentialType, mode);
} catch {
return;
}
(0, _jsdoccomment.traverse)(parsedType, nde => {
const {
type,
value
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */nde;
if (type === 'JsdocTypeName') {
usedNames.add(value);
}
});
};
const checkParamsAndReturnsTags = (jsdc = jsdoc) => {
const paramName = /** @type {string} */utils.getPreferredTagName({
tagName: 'param'
});
const paramTags = (0, _jsdocUtils.getTags)(jsdc, paramName);
for (const paramTag of paramTags) {
checkForUsedTypes(paramTag.type);
}
const returnsName = /** @type {string} */utils.getPreferredTagName({
tagName: 'returns'
});
const returnsTags = (0, _jsdocUtils.getTags)(jsdc, returnsName);
for (const returnsTag of returnsTags) {
checkForUsedTypes(returnsTag.type);
}
};
const checkTemplateTags = () => {
for (const tag of templateTags) {
const names = utils.parseClosureTemplateTag(tag);
for (const nme of names) {
if (!usedNames.has(nme)) {
report(`@${tgName} ${nme} not in use`, null, tag);
}
}
}
};
/**
* @param {import('@typescript-eslint/types').TSESTree.FunctionDeclaration|
* import('@typescript-eslint/types').TSESTree.ClassDeclaration|
* import('@typescript-eslint/types').TSESTree.TSInterfaceDeclaration|
* import('@typescript-eslint/types').TSESTree.TSTypeAliasDeclaration} aliasDeclaration
* @param {boolean} [checkParamsAndReturns]
*/
const checkParameters = (aliasDeclaration, checkParamsAndReturns) => {
/* c8 ignore next -- Guard */
const {
params
} = aliasDeclaration.typeParameters ?? {
params: []
};
for (const {
name: {
name
}
} of params) {
usedNames.add(name);
}
if (checkParamsAndReturns) {
checkParamsAndReturnsTags();
} else if (aliasDeclaration.type === 'ClassDeclaration') {
/* c8 ignore next -- TS */
for (const nde of aliasDeclaration?.body?.body ?? []) {
// @ts-expect-error Should be ok
const commentNode = (0, _jsdoccomment.getJSDocComment)(sourceCode, nde, settings);
if (!commentNode) {
continue;
}
const innerJsdoc = (0, _iterateJsdoc.parseComment)(commentNode, '');
checkParamsAndReturnsTags(innerJsdoc);
const typeName = /** @type {string} */utils.getPreferredTagName({
tagName: 'type'
});
const typeTags = (0, _jsdocUtils.getTags)(innerJsdoc, typeName);
for (const typeTag of typeTags) {
checkForUsedTypes(typeTag.type);
}
}
}
checkTemplateTags();
};
const handleTypeAliases = () => {
const nde = /** @type {import('@typescript-eslint/types').TSESTree.Node} */
node;
if (!nde) {
return;
}
switch (nde.type) {
case 'ClassDeclaration':
case 'TSInterfaceDeclaration':
case 'TSTypeAliasDeclaration':
checkParameters(nde);
break;
case 'ExportDefaultDeclaration':
case 'ExportNamedDeclaration':
switch (nde.declaration?.type) {
case 'ClassDeclaration':
case 'TSInterfaceDeclaration':
case 'TSTypeAliasDeclaration':
checkParameters(nde.declaration);
break;
case 'FunctionDeclaration':
checkParameters(nde.declaration, true);
break;
}
break;
case 'FunctionDeclaration':
checkParameters(nde, true);
break;
}
};
const callbackTags = utils.getTags('callback');
const functionTags = utils.getTags('function');
if (callbackTags.length || functionTags.length) {
checkParamsAndReturnsTags();
checkTemplateTags();
return;
}
const typedefTags = utils.getTags('typedef');
if (!typedefTags.length || typedefTags.length >= 2) {
handleTypeAliases();
return;
}
const potentialTypedefType = typedefTags[0].type;
checkForUsedTypes(potentialTypedefType);
const propertyName = /** @type {string} */utils.getPreferredTagName({
tagName: 'property'
});
const propertyTags = utils.getTags(propertyName);
for (const propertyTag of propertyTags) {
checkForUsedTypes(propertyTag.type);
}
checkTemplateTags();
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Checks that any `@template` names are actually used in the connected `@typedef` or type alias.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-template-names.md#repos-sticky-header'
},
schema: [],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkTemplateNames.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkTemplateNames.d.ts.map

View file

@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _buildRejectOrPreferRuleDefinition = require("../buildRejectOrPreferRuleDefinition.cjs");
var _jsdocUtils = require("../jsdocUtils.cjs");
/**
* @callback CheckNativeTypes
* Iterates strict types to see if any should be added to `invalidTypes` (and
* the the relevant strict type returned as the new preferred type).
* @param {import('../iterateJsdoc.js').PreferredTypes} preferredTypes
* @param {string} typeNodeName
* @param {string|undefined} preferred
* @param {import('jsdoc-type-pratt-parser').NonRootResult|undefined} parentNode
* @param {(string|false|undefined)[][]} invalidTypes
* @returns {string|undefined} The `preferred` type string, optionally changed
*/
/** @type {CheckNativeTypes} */
const checkNativeTypes = (preferredTypes, typeNodeName, preferred, parentNode, invalidTypes) => {
let changedPreferred = preferred;
for (const strictNativeType of _jsdocUtils.strictNativeTypes) {
if (strictNativeType === 'object' && (
// This is not set to remap with exact type match (e.g.,
// `object: 'Object'`), so can ignore (including if circular)
!preferredTypes?.[typeNodeName] ||
// Although present on `preferredTypes` for remapping, this is a
// parent object without a parent match (and not
// `unifyParentAndChildTypeChecks`) and we don't want
// `object<>` given TypeScript issue https://github.com/microsoft/TypeScript/issues/20555
/**
* @type {import('jsdoc-type-pratt-parser').GenericResult}
*/
parentNode?.elements?.length && (
/**
* @type {import('jsdoc-type-pratt-parser').GenericResult}
*/
parentNode?.left?.type === 'JsdocTypeName' &&
/**
* @type {import('jsdoc-type-pratt-parser').GenericResult}
*/
parentNode?.left?.value === 'Object'))) {
continue;
}
if (strictNativeType !== typeNodeName && strictNativeType.toLowerCase() === typeNodeName.toLowerCase() && (
// Don't report if user has own map for a strict native type
!preferredTypes || preferredTypes?.[strictNativeType] === undefined)) {
changedPreferred = strictNativeType;
invalidTypes.push([typeNodeName, changedPreferred]);
break;
}
}
return changedPreferred;
};
var _default = exports.default = (0, _buildRejectOrPreferRuleDefinition.buildRejectOrPreferRuleDefinition)({
checkNativeTypes,
schema: [{
additionalProperties: false,
properties: {
exemptTagContexts: {
description: 'Avoids reporting when a bad type is found on a specified tag.',
items: {
additionalProperties: false,
properties: {
tag: {
description: 'Set a key `tag` to the tag to exempt',
type: 'string'
},
types: {
description: `Set to \`true\` to indicate that any types on that tag will be allowed,
or to an array of strings which will only allow specific bad types.
If an array of strings is given, these must match the type exactly,
e.g., if you only allow \`"object"\`, it will not allow
\`"object<string, string>"\`. Note that this is different from the
behavior of \`settings.jsdoc.preferredTypes\`. This option is useful
for normally restricting generic types like \`object\` with
\`preferredTypes\`, but allowing \`typedef\` to indicate that its base
type is \`object\`.`,
oneOf: [{
type: 'boolean'
}, {
items: {
type: 'string'
},
type: 'array'
}]
}
},
type: 'object'
},
type: 'array'
},
noDefaults: {
description: `Insists that only the supplied option type
map is to be used, and that the default preferences (such as "string"
over "String") will not be enforced. The option's default is \`false\`.`,
type: 'boolean'
},
unifyParentAndChildTypeChecks: {
description: `@deprecated Use the \`preferredTypes[preferredType]\` setting of the same name instead.
If this option is \`true\`, will currently override \`unifyParentAndChildTypeChecks\` on the \`preferredTypes\` setting.`,
type: 'boolean'
}
},
type: 'object'
}]
});
module.exports = exports.default;
//# sourceMappingURL=checkTypes.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,8 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
/**
* Iterates strict types to see if any should be added to `invalidTypes` (and
* the the relevant strict type returned as the new preferred type).
*/
export type CheckNativeTypes = (preferredTypes: import("../iterateJsdoc.js").PreferredTypes, typeNodeName: string, preferred: string | undefined, parentNode: import("jsdoc-type-pratt-parser").NonRootResult | undefined, invalidTypes: (string | false | undefined)[][]) => string | undefined;
//# sourceMappingURL=checkTypes.d.ts.map

View file

@ -0,0 +1,166 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _parseImportsExports = require("parse-imports-exports");
var _semver = _interopRequireDefault(require("semver"));
var _spdxExpressionParse = _interopRequireDefault(require("spdx-expression-parse"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const allowedKinds = new Set(['class', 'constant', 'event', 'external', 'file', 'function', 'member', 'mixin', 'module', 'namespace', 'typedef']);
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
report,
settings,
utils
}) => {
const options = context.options[0] || {};
const {
allowedAuthors = null,
allowedLicenses = null,
licensePattern = '/([^\n\r]*)/gv',
numericOnlyVariation = false
} = options;
utils.forEachPreferredTag('version', (jsdocParameter, targetTagName) => {
const version = /** @type {string} */utils.getTagDescription(jsdocParameter).trim();
if (!version) {
report(`Missing JSDoc @${targetTagName} value.`, null, jsdocParameter);
} else if (!_semver.default.valid(version)) {
report(`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}".`, null, jsdocParameter);
}
});
utils.forEachPreferredTag('kind', (jsdocParameter, targetTagName) => {
const kind = /** @type {string} */utils.getTagDescription(jsdocParameter).trim();
if (!kind) {
report(`Missing JSDoc @${targetTagName} value.`, null, jsdocParameter);
} else if (!allowedKinds.has(kind)) {
report(`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}"; ` + `must be one of: ${[...allowedKinds].join(', ')}.`, null, jsdocParameter);
}
});
if (numericOnlyVariation) {
utils.forEachPreferredTag('variation', (jsdocParameter, targetTagName) => {
const variation = /** @type {string} */utils.getTagDescription(jsdocParameter).trim();
if (!variation) {
report(`Missing JSDoc @${targetTagName} value.`, null, jsdocParameter);
} else if (!Number.isInteger(Number(variation)) || Number(variation) <= 0) {
report(`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}".`, null, jsdocParameter);
}
});
}
utils.forEachPreferredTag('since', (jsdocParameter, targetTagName) => {
const version = /** @type {string} */utils.getTagDescription(jsdocParameter).trim();
if (!version) {
report(`Missing JSDoc @${targetTagName} value.`, null, jsdocParameter);
} else if (!_semver.default.valid(version)) {
report(`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}".`, null, jsdocParameter);
}
});
utils.forEachPreferredTag('license', (jsdocParameter, targetTagName) => {
const licenseRegex = utils.getRegexFromString(licensePattern, 'g');
const matches = /** @type {string} */utils.getTagDescription(jsdocParameter).matchAll(licenseRegex);
let positiveMatch = false;
for (const match of matches) {
const license = match[1] || match[0];
if (license) {
positiveMatch = true;
}
if (!license.trim()) {
// Avoid reporting again as empty match
if (positiveMatch) {
return;
}
report(`Missing JSDoc @${targetTagName} value.`, null, jsdocParameter);
} else if (allowedLicenses) {
if (allowedLicenses !== true && !allowedLicenses.includes(license)) {
report(`Invalid JSDoc @${targetTagName}: "${license}"; expected one of ${allowedLicenses.join(', ')}.`, null, jsdocParameter);
}
} else {
try {
(0, _spdxExpressionParse.default)(license);
} catch {
report(`Invalid JSDoc @${targetTagName}: "${license}"; expected SPDX expression: https://spdx.org/licenses/.`, null, jsdocParameter);
}
}
}
});
if (settings.mode === 'typescript') {
utils.forEachPreferredTag('import', tag => {
const {
description,
name,
type
} = tag;
const typePart = type ? `{${type}} ` : '';
const imprt = 'import ' + (description ? `${typePart}${name} ${description}` : `${typePart}${name}`);
const importsExports = (0, _parseImportsExports.parseImportsExports)(imprt.trim());
if (importsExports.errors) {
report('Bad @import tag', null, tag);
}
});
}
utils.forEachPreferredTag('author', (jsdocParameter, targetTagName) => {
const author = /** @type {string} */utils.getTagDescription(jsdocParameter).trim();
if (!author) {
report(`Missing JSDoc @${targetTagName} value.`, null, jsdocParameter);
} else if (allowedAuthors && !allowedAuthors.includes(author)) {
report(`Invalid JSDoc @${targetTagName}: "${utils.getTagDescription(jsdocParameter)}"; expected one of ${allowedAuthors.join(', ')}.`, null, jsdocParameter);
}
});
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'This rule checks the values for a handful of tags: `@version`, `@since`, `@license` and `@author`.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/check-values.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
allowedAuthors: {
description: `An array of allowable author values. If absent, only non-whitespace will
be checked for.`,
items: {
type: 'string'
},
type: 'array'
},
allowedLicenses: {
anyOf: [{
items: {
type: 'string'
},
type: 'array'
}, {
type: 'boolean'
}],
description: `An array of allowable license values or \`true\` to allow any license text.
If present as an array, will be used in place of [SPDX identifiers](https://spdx.org/licenses/).`
},
licensePattern: {
description: `A string to be converted into a \`RegExp\` (with \`v\` flag) and whose first
parenthetical grouping, if present, will match the portion of the license
description to check (if no grouping is present, then the whole portion
matched will be used). Defaults to \`/([^\\n\\r]*)/gv\`, i.e., the SPDX expression
is expected before any line breaks.
Note that the \`/\` delimiters are optional, but necessary to add flags.
Defaults to using the \`v\` flag, so to add your own flags, encapsulate
your expression as a string, but like a literal, e.g., \`/^mit$/vi\`.`,
type: 'string'
},
numericOnlyVariation: {
description: `Whether to enable validation that \`@variation\` must be a number. Defaults to
\`false\`.`,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=checkValues.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=checkValues.d.ts.map

View file

@ -0,0 +1,355 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = require("../iterateJsdoc.cjs");
var _jsdocUtils = require("../jsdocUtils.cjs");
var _jsdoccomment = require("@es-joy/jsdoccomment");
/** @type {import('eslint').Rule.RuleModule} */
var _default = exports.default = {
create(context) {
/**
* @typedef {import('eslint').AST.Token | import('estree').Comment | {
* type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
* range: [number, number],
* value: string
* }} Token
*/
/**
* @callback AddComment
* @param {boolean|undefined} inlineCommentBlock
* @param {Token} comment
* @param {string} indent
* @param {number} lines
* @param {import('eslint').Rule.RuleFixer} fixer
*/
/* c8 ignore next -- Fallback to deprecated method */
const {
sourceCode = context.getSourceCode()
} = context;
const settings = (0, _iterateJsdoc.getSettings)(context);
if (!settings) {
return {};
}
const {
allowedPrefixes = ['@ts-', 'istanbul ', 'c8 ', 'v8 ', 'eslint', 'prettier-'],
contexts = settings.contexts || [],
contextsAfter = (/** @type {string[]} */[]),
contextsBeforeAndAfter = ['VariableDeclarator', 'TSPropertySignature', 'PropertyDefinition'],
enableFixer = true,
enforceJsdocLineStyle = 'multi',
lineOrBlockStyle = 'both'
} = context.options[0] ?? {};
let reportingNonJsdoc = false;
/**
* @param {string} messageId
* @param {import('estree').Comment|Token} comment
* @param {import('eslint').Rule.Node} node
* @param {import('eslint').Rule.ReportFixer} fixer
*/
const report = (messageId, comment, node, fixer) => {
const loc = {
end: {
column: 0,
/* c8 ignore next 2 -- Guard */
// @ts-expect-error Ok
line: comment.loc?.start?.line ?? 1
},
start: {
column: 0,
/* c8 ignore next 2 -- Guard */
// @ts-expect-error Ok
line: comment.loc?.start?.line ?? 1
}
};
context.report({
fix: enableFixer ? fixer : null,
loc,
messageId,
node
});
};
/**
* @param {import('eslint').Rule.Node} node
* @param {import('eslint').AST.Token | import('estree').Comment | {
* type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
* range: [number, number],
* value: string
* }} comment
* @param {AddComment} addComment
* @param {import('../iterateJsdoc.js').Context[]} ctxts
*/
const getFixer = (node, comment, addComment, ctxts) => {
return /** @type {import('eslint').Rule.ReportFixer} */fixer => {
// Default to one line break if the `minLines`/`maxLines` settings allow
const lines = settings.minLines === 0 && settings.maxLines >= 1 ? 1 : settings.minLines;
let baseNode =
/**
* @type {import('@typescript-eslint/types').TSESTree.Node|import('eslint').Rule.Node}
*/
(0, _jsdoccomment.getReducedASTNode)(node, sourceCode);
const decorator = (0, _jsdoccomment.getDecorator)(/** @type {import('eslint').Rule.Node} */
baseNode);
if (decorator) {
baseNode = /** @type {import('@typescript-eslint/types').TSESTree.Decorator} */
decorator;
}
const indent = (0, _jsdocUtils.getIndent)({
text: sourceCode.getText(/** @type {import('eslint').Rule.Node} */baseNode, /** @type {import('eslint').AST.SourceLocation} */
(/** @type {import('eslint').Rule.Node} */baseNode.loc).start.column)
});
const {
inlineCommentBlock
} =
/**
* @type {{
* context: string,
* inlineCommentBlock: boolean,
* minLineCount: import('../iterateJsdoc.js').Integer
* }[]}
*/
ctxts.find(contxt => {
if (typeof contxt === 'string') {
return false;
}
const {
context: ctxt
} = contxt;
return ctxt === node.type;
}) || {};
return addComment(inlineCommentBlock, comment, indent, lines, fixer);
};
};
/**
* @param {import('eslint').AST.Token | import('estree').Comment | {
* type: import('eslint').AST.TokenType|"Line"|"Block"|"Shebang",
* range: [number, number],
* value: string
* }} comment
* @param {import('eslint').Rule.Node} node
* @param {AddComment} addComment
* @param {import('../iterateJsdoc.js').Context[]} ctxts
*/
const reportings = (comment, node, addComment, ctxts) => {
const fixer = getFixer(node, comment, addComment, ctxts);
if (comment.type === 'Block') {
if (lineOrBlockStyle === 'line') {
return;
}
report('blockCommentsJsdocStyle', comment, node, fixer);
return;
}
if (comment.type === 'Line') {
if (lineOrBlockStyle === 'block') {
return;
}
report('lineCommentsJsdocStyle', comment, node, fixer);
}
};
/**
* @type {import('../iterateJsdoc.js').CheckJsdoc}
*/
const checkNonJsdoc = (_info, _handler, node) => {
const comment = (0, _jsdoccomment.getNonJsdocComment)(sourceCode, node, settings);
if (!comment || /** @type {string[]} */
allowedPrefixes.some(prefix => {
return comment.value.trimStart().startsWith(prefix);
})) {
return;
}
reportingNonJsdoc = true;
/** @type {AddComment} */
const addComment = (inlineCommentBlock, commentToAdd, indent, lines, fixer) => {
const insertion = (inlineCommentBlock || enforceJsdocLineStyle === 'single' ? `/** ${commentToAdd.value.trim()} ` : `/**\n${indent}*${commentToAdd.value.trimEnd()}\n${indent}`) + `*/${'\n'.repeat((lines || 1) - 1)}`;
return fixer.replaceText(/** @type {import('eslint').AST.Token} */
commentToAdd, insertion);
};
reportings(comment, node, addComment, contexts);
};
/**
* @param {import('eslint').Rule.Node} node
* @param {import('../iterateJsdoc.js').Context[]} ctxts
*/
const checkNonJsdocAfter = (node, ctxts) => {
const comment = (0, _jsdoccomment.getFollowingComment)(sourceCode, node);
if (!comment || comment.value.startsWith('*') || /** @type {string[]} */
allowedPrefixes.some(prefix => {
return comment.value.trimStart().startsWith(prefix);
})) {
return;
}
/** @type {AddComment} */
const addComment = (inlineCommentBlock, commentToAdd, indent, lines, fixer) => {
const insertion = (inlineCommentBlock || enforceJsdocLineStyle === 'single' ? `/** ${commentToAdd.value.trim()} ` : `/**\n${indent}*${commentToAdd.value.trimEnd()}\n${indent}`) + `*/${'\n'.repeat((lines || 1) - 1)}${lines ? `\n${indent.slice(1)}` : ' '}`;
return [fixer.remove(/** @type {import('eslint').AST.Token} */
commentToAdd), fixer.insertTextBefore(node.type === 'VariableDeclarator' ? node.parent : node, insertion)];
};
reportings(comment, node, addComment, ctxts);
};
// Todo: add contexts to check after (and handle if want both before and after)
return {
...(0, _jsdocUtils.getContextObject)((0, _jsdocUtils.enforcedContexts)(context, true, settings), checkNonJsdoc),
...(0, _jsdocUtils.getContextObject)(contextsAfter, (_info, _handler, node) => {
checkNonJsdocAfter(node, contextsAfter);
}),
...(0, _jsdocUtils.getContextObject)(contextsBeforeAndAfter, (_info, _handler, node) => {
checkNonJsdoc({}, null, node);
if (!reportingNonJsdoc) {
checkNonJsdocAfter(node, contextsBeforeAndAfter);
}
})
};
},
meta: {
docs: {
description: 'Converts non-JSDoc comments preceding or following nodes into JSDoc ones',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/convert-to-jsdoc-comments.md#repos-sticky-header'
},
fixable: 'code',
messages: {
blockCommentsJsdocStyle: 'Block comments should be JSDoc-style.',
lineCommentsJsdocStyle: 'Line comments should be JSDoc-style.'
},
schema: [{
additionalProperties: false,
properties: {
allowedPrefixes: {
description: `An array of prefixes to allow at the beginning of a comment.
Defaults to \`['@ts-', 'istanbul ', 'c8 ', 'v8 ', 'eslint', 'prettier-']\`.
Supplying your own value overrides the defaults.`,
items: {
type: 'string'
},
type: 'array'
},
contexts: {
description: `The contexts array which will be checked for preceding content.
Can either be strings or an object with a \`context\` string and an optional, default \`false\` \`inlineCommentBlock\` boolean.
Defaults to \`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
\`FunctionExpression\`, \`TSDeclareFunction\`.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
context: {
type: 'string'
},
inlineCommentBlock: {
type: 'boolean'
}
},
type: 'object'
}]
},
type: 'array'
},
contextsAfter: {
description: `The contexts array which will be checked for content on the same line after.
Can either be strings or an object with a \`context\` string and an optional, default \`false\` \`inlineCommentBlock\` boolean.
Defaults to an empty array.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
context: {
type: 'string'
},
inlineCommentBlock: {
type: 'boolean'
}
},
type: 'object'
}]
},
type: 'array'
},
contextsBeforeAndAfter: {
description: `The contexts array which will be checked for content before and on the same
line after.
Can either be strings or an object with a \`context\` string and an optional, default \`false\` \`inlineCommentBlock\` boolean.
Defaults to \`VariableDeclarator\`, \`TSPropertySignature\`, \`PropertyDefinition\`.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
context: {
type: 'string'
},
inlineCommentBlock: {
type: 'boolean'
}
},
type: 'object'
}]
},
type: 'array'
},
enableFixer: {
description: 'Set to `false` to disable fixing.',
type: 'boolean'
},
enforceJsdocLineStyle: {
description: `What policy to enforce on the conversion of non-JSDoc comments without
line breaks. (Non-JSDoc (mulitline) comments with line breaks will always
be converted to \`multi\` style JSDoc comments.)
- \`multi\` - Convert to multi-line style
\`\`\`js
/**
* Some text
*/
\`\`\`
- \`single\` - Convert to single-line style
\`\`\`js
/** Some text */
\`\`\`
Defaults to \`multi\`.`,
enum: ['multi', 'single'],
type: 'string'
},
lineOrBlockStyle: {
description: `What style of comments to which to apply JSDoc conversion.
- \`block\` - Applies to block-style comments (\`/* ... */\`)
- \`line\` - Applies to line-style comments (\`// ...\`)
- \`both\` - Applies to both block and line-style comments
Defaults to \`both\`.`,
enum: ['block', 'line', 'both'],
type: 'string'
}
},
type: 'object'
}],
type: 'suggestion'
}
};
module.exports = exports.default;
//# sourceMappingURL=convertToJsdocComments.cjs.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,85 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const defaultEmptyTags = new Set(['abstract', 'async', 'generator', 'global', 'hideconstructor',
// jsdoc doesn't use this form in its docs, but allow for compatibility with
// TypeScript which allows and Closure which requires
'ignore',
// jsdoc doesn't use but allow for TypeScript
'inheritDoc', 'inner', 'instance', 'internal', 'overload', 'override', 'readonly']);
const emptyIfNotClosure = new Set([
// Closure doesn't allow with this casing
'inheritdoc', 'package', 'private', 'protected', 'public', 'static']);
const emptyIfClosure = new Set(['interface']);
var _default = exports.default = (0, _iterateJsdoc.default)(({
jsdoc,
settings,
utils
}) => {
const emptyTags = utils.filterTags(({
tag: tagName
}) => {
return defaultEmptyTags.has(tagName) || utils.hasOptionTag(tagName) && jsdoc.tags.some(({
tag
}) => {
return tag === tagName;
}) || settings.mode === 'closure' && emptyIfClosure.has(tagName) || settings.mode !== 'closure' && emptyIfNotClosure.has(tagName);
});
for (const [key, tag] of emptyTags.entries()) {
const content = tag.name || tag.description || tag.type;
if (content.trim() && (
// Allow for JSDoc-block final asterisks
key !== emptyTags.length - 1 || !/^\s*\*+$/v.test(content))) {
const fix = () => {
// By time of call in fixer, `tag` will have `line` added
utils.setTag(
/**
* @type {import('comment-parser').Spec & {
* line: import('../iterateJsdoc.js').Integer
* }}
*/
tag);
};
utils.reportJSDoc(`@${tag.tag} should be empty.`, tag, fix, true);
}
}
}, {
checkInternal: true,
checkPrivate: true,
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Checks tags that are expected to be empty (e.g., `@abstract` or `@async`), reporting if they have content',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/empty-tags.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
tags: {
description: `If you want additional tags to be checked for their descriptions, you may
add them within this option.
\`\`\`js
{
'jsdoc/empty-tags': ['error', {tags: ['event']}]
}
\`\`\``,
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=emptyTags.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=emptyTags.d.ts.map

View file

@ -0,0 +1,149 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
settings,
utils
}) => {
const {
mode
} = settings;
if (mode !== 'typescript') {
return;
}
const {
allowedInlineTags = [],
enableFixer = false,
fixType = 'backslash'
} = context.options[0] || {};
const {
description
} = utils.getDescription();
/** @type {string[]} */
const tagNames = [];
/** @type {number[]} */
const indexes = [];
const unescapedInlineTagRegex = /(?:^|\s)@(\w+)/gv;
for (const [idx, descLine] of (description.startsWith('\n') ? description.slice(1) : description).split('\n').entries()) {
descLine.replaceAll(unescapedInlineTagRegex, (_, tagName) => {
if (allowedInlineTags.includes(tagName)) {
return _;
}
tagNames.push(tagName);
indexes.push(idx);
return _;
});
}
for (const [idx, tagName] of tagNames.entries()) {
utils.reportJSDoc(`Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\@${tagName}, or \`@${tagName}\`?`, {
line: indexes[idx] + 1
}, enableFixer ? () => {
utils.setBlockDescription((info, seedTokens, descLines) => {
return descLines.map(desc => {
const newDesc = desc.replaceAll(new RegExp(`(^|\\s)@${
// No need to escape, as contains only safe characters
tagName}`, 'gv'), fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName);
return {
number: 0,
source: '',
tokens: seedTokens({
...info,
description: newDesc,
postDelimiter: newDesc.trim() ? ' ' : ''
})
};
});
});
} : null);
}
/**
* @param {string} tagName
* @returns {[
* RegExp,
* (description: string) => string
* ]}
*/
const escapeInlineTags = tagName => {
const regex = new RegExp(`(^|\\s)@${
// No need to escape, as contains only safe characters
tagName}`, 'gv');
return [regex,
/**
* @param {string} desc
*/
desc => {
return desc.replaceAll(regex, fixType === 'backticks' ? '$1`@' + tagName + '`' : '$1\\@' + tagName);
}];
};
for (const tag of jsdoc.tags) {
if (tag.tag === 'example') {
continue;
}
/** @type {string} */
let tagName = '';
while (/** @type {string[]} */utils.getTagDescription(tag, true)
// eslint-disable-next-line no-loop-func -- Safe
.some(desc => {
tagName = unescapedInlineTagRegex.exec(desc)?.[1] ?? '';
if (allowedInlineTags.includes(tagName)) {
return false;
}
return tagName;
})) {
const line = utils.setTagDescription(tag, ...escapeInlineTags(tagName)) + tag.source[0].number;
utils.reportJSDoc(`Unexpected inline JSDoc tag. Did you mean to use {@${tagName}}, \\@${tagName}, or \`@${tagName}\`?`, {
line
}, enableFixer ? () => {} : null, true);
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports use of JSDoc tags in non-tag positions (in the default "typescript" mode).',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/escape-inline-tags.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
allowedInlineTags: {
description: 'A listing of tags you wish to allow unescaped. Defaults to an empty array.',
items: {
type: 'string'
},
type: 'array'
},
enableFixer: {
description: 'Whether to enable the fixer. Defaults to `false`.',
type: 'boolean'
},
fixType: {
description: `How to escape the inline tag.
May be "backticks" to enclose tags in backticks (treating as code segments), or
"backslash" to escape tags with a backslash, i.e., \`\\@\`
Defaults to "backslash".`,
enum: ['backticks', 'backslash'],
type: 'string'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=escapeInlineTags.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=escapeInlineTags.d.ts.map

View file

@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
report,
utils
}) => {
const iteratingFunction = utils.isIteratingFunctionOrVariable();
if (iteratingFunction) {
if (utils.hasATag(['class', 'constructor']) || utils.isConstructor()) {
return;
}
} else if (!utils.isVirtualFunction()) {
return;
}
utils.forEachPreferredTag('implements', tag => {
report('@implements used on a non-constructor function', null, tag);
});
}, {
contextDefaults: true,
meta: {
docs: {
description: 'Prohibits use of `@implements` on non-constructor functions (to enforce the tag only being used on classes/constructors).',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/implements-on-classes.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
contexts: {
description: `Set this to an array of strings representing the AST context (or an object with
\`context\` and \`comment\` properties) where you wish the rule to be applied.
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
\`FunctionExpression\`). Set to \`"any"\` if you want
the rule to apply to any JSDoc block throughout your files (as is necessary
for finding function blocks not attached to a function declaration or
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
\`@method\`) (including those associated with an \`@interface\`).
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
comment: {
type: 'string'
},
context: {
type: 'string'
}
},
type: 'object'
}]
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=implementsOnClasses.cjs.map

View file

@ -0,0 +1 @@
{"version":3,"file":"implementsOnClasses.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","_default","exports","iterateJsdoc","report","utils","iteratingFunction","isIteratingFunctionOrVariable","hasATag","isConstructor","isVirtualFunction","forEachPreferredTag","tag","contextDefaults","meta","docs","description","url","schema","additionalProperties","properties","contexts","items","anyOf","type","comment","context","module"],"sources":["../../src/rules/implementsOnClasses.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nexport default iterateJsdoc(({\n report,\n utils,\n}) => {\n const iteratingFunction = utils.isIteratingFunctionOrVariable();\n\n if (iteratingFunction) {\n if (utils.hasATag([\n 'class',\n 'constructor',\n ]) ||\n utils.isConstructor()\n ) {\n return;\n }\n } else if (!utils.isVirtualFunction()) {\n return;\n }\n\n utils.forEachPreferredTag('implements', (tag) => {\n report('@implements used on a non-constructor function', null, tag);\n });\n}, {\n contextDefaults: true,\n meta: {\n docs: {\n description: 'Prohibits use of `@implements` on non-constructor functions (to enforce the tag only being used on classes/constructors).',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/implements-on-classes.md#repos-sticky-header',\n },\n schema: [\n {\n additionalProperties: false,\n properties: {\n contexts: {\n description: `Set this to an array of strings representing the AST context (or an object with\n\\`context\\` and \\`comment\\` properties) where you wish the rule to be applied.\n\n\\`context\\` defaults to \\`any\\` and \\`comment\\` defaults to no specific comment context.\n\nOverrides the default contexts (\\`ArrowFunctionExpression\\`, \\`FunctionDeclaration\\`,\n\\`FunctionExpression\\`). Set to \\`\"any\"\\` if you want\nthe rule to apply to any JSDoc block throughout your files (as is necessary\nfor finding function blocks not attached to a function declaration or\nexpression, i.e., \\`@callback\\` or \\`@function\\` (or its aliases \\`@func\\` or\n\\`@method\\`) (including those associated with an \\`@interface\\`).\n\nSee the [\"AST and Selectors\"](../#advanced-ast-and-selectors)\nsection of our Advanced docs for more on the expected format.`,\n items: {\n anyOf: [\n {\n type: 'string',\n },\n {\n additionalProperties: false,\n properties: {\n comment: {\n type: 'string',\n },\n context: {\n type: 'string',\n },\n },\n type: 'object',\n },\n ],\n },\n type: 'array',\n },\n },\n type: 'object',\n },\n ],\n type: 'suggestion',\n },\n});\n"],"mappings":";;;;;;AAAA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA8C,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAE/B,IAAAG,qBAAY,EAAC,CAAC;EAC3BC,MAAM;EACNC;AACF,CAAC,KAAK;EACJ,MAAMC,iBAAiB,GAAGD,KAAK,CAACE,6BAA6B,CAAC,CAAC;EAE/D,IAAID,iBAAiB,EAAE;IACrB,IAAID,KAAK,CAACG,OAAO,CAAC,CAChB,OAAO,EACP,aAAa,CACd,CAAC,IACAH,KAAK,CAACI,aAAa,CAAC,CAAC,EACrB;MACA;IACF;EACF,CAAC,MAAM,IAAI,CAACJ,KAAK,CAACK,iBAAiB,CAAC,CAAC,EAAE;IACrC;EACF;EAEAL,KAAK,CAACM,mBAAmB,CAAC,YAAY,EAAGC,GAAG,IAAK;IAC/CR,MAAM,CAAC,gDAAgD,EAAE,IAAI,EAAEQ,GAAG,CAAC;EACrE,CAAC,CAAC;AACJ,CAAC,EAAE;EACDC,eAAe,EAAE,IAAI;EACrBC,IAAI,EAAE;IACJC,IAAI,EAAE;MACJC,WAAW,EAAE,2HAA2H;MACxIC,GAAG,EAAE;IACP,CAAC;IACDC,MAAM,EAAE,CACN;MACEC,oBAAoB,EAAE,KAAK;MAC3BC,UAAU,EAAE;QACVC,QAAQ,EAAE;UACRL,WAAW,EAAE;AACzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,8DAA8D;UAClDM,KAAK,EAAE;YACLC,KAAK,EAAE,CACL;cACEC,IAAI,EAAE;YACR,CAAC,EACD;cACEL,oBAAoB,EAAE,KAAK;cAC3BC,UAAU,EAAE;gBACVK,OAAO,EAAE;kBACPD,IAAI,EAAE;gBACR,CAAC;gBACDE,OAAO,EAAE;kBACPF,IAAI,EAAE;gBACR;cACF,CAAC;cACDA,IAAI,EAAE;YACR,CAAC;UAEL,CAAC;UACDA,IAAI,EAAE;QACR;MACF,CAAC;MACDA,IAAI,EAAE;IACR,CAAC,CACF;IACDA,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAAAG,MAAA,CAAAzB,OAAA,GAAAA,OAAA,CAAAF,OAAA","ignoreList":[]}

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=implementsOnClasses.d.ts.map

View file

@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _jsdoccomment = require("@es-joy/jsdoccomment");
var resolve = _interopRequireWildcard(require("@es-joy/resolve.exports"));
var _nodeFs = require("node:fs");
var _nodeModule = require("node:module");
var _nodePath = require("node:path");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @type {Set<string>|null}
*/
let deps;
const setDeps = function () {
try {
const pkg = JSON.parse((0, _nodeFs.readFileSync)((0, _nodePath.join)(process.cwd(), './package.json'), 'utf8'));
deps = new Set([...(pkg.dependencies ? /* c8 ignore next 2 */
Object.keys(pkg.dependencies) : []), ...(pkg.devDependencies ? /* c8 ignore next 2 */
Object.keys(pkg.devDependencies) : [])]);
/* c8 ignore next -- our package.json exists */
} catch (error) {
/* c8 ignore next -- our package.json exists */
deps = null;
/* c8 ignore next 4 -- our package.json exists */
/* eslint-disable no-console -- Inform user */
console.log(error);
/* eslint-enable no-console -- Inform user */
}
};
const moduleCheck = new Map();
var _default = exports.default = (0, _iterateJsdoc.default)(({
jsdoc,
settings,
utils
}) => {
if (deps === undefined) {
setDeps();
}
/* c8 ignore next 3 -- our package.json exists */
if (deps === null) {
return;
}
const {
mode
} = settings;
for (const tag of jsdoc.tags) {
let typeAst;
try {
typeAst = mode === 'permissive' ? (0, _jsdoccomment.tryParse)(tag.type) : (0, _jsdoccomment.parse)(tag.type, mode);
} catch {
continue;
}
// eslint-disable-next-line no-loop-func -- Safe
(0, _jsdoccomment.traverse)(typeAst, nde => {
/* c8 ignore next 3 -- TS guard */
if (deps === null) {
return;
}
if (nde.type === 'JsdocTypeImport') {
let mod = nde.element.value.replace(/^(@[^\/]+\/[^\/]+|[^\/]+).*$/v, '$1');
if (/^[.\/]/v.test(mod)) {
return;
}
if ((0, _nodeModule.isBuiltin)(mod)) {
// mod = '@types/node';
// moduleCheck.set(mod, !deps.has(mod));
return;
} else if (!moduleCheck.has(mod)) {
let pkg;
try {
pkg = JSON.parse((0, _nodeFs.readFileSync)((0, _nodePath.join)(process.cwd(), 'node_modules', mod, './package.json'), 'utf8'));
} catch {
// Ignore
}
if (!pkg || !pkg.types && !pkg.typings && !resolve.types(pkg)) {
mod = `@types/${mod}`;
}
moduleCheck.set(mod, !deps.has(mod));
}
if (moduleCheck.get(mod)) {
utils.reportJSDoc('import points to package which is not found in dependencies', tag);
}
}
});
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Reports if JSDoc `import()` statements point to a package which is not listed in `dependencies` or `devDependencies`',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/imports-as-dependencies.md#repos-sticky-header'
},
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=importsAsDependencies.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=importsAsDependencies.d.ts.map

View file

@ -0,0 +1,187 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _areDocsInformative = require("are-docs-informative");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const defaultAliases = {
a: ['an', 'our']
};
const defaultUselessWords = ['a', 'an', 'i', 'in', 'of', 's', 'the'];
/**
* @param {import('eslint').Rule.Node|import('@typescript-eslint/types').TSESTree.Node|null|undefined} node
* @returns {string[]}
*/
const getNamesFromNode = node => {
switch (node?.type) {
case 'AccessorProperty':
case 'MethodDefinition':
case 'PropertyDefinition':
case 'TSAbstractAccessorProperty':
case 'TSAbstractMethodDefinition':
case 'TSAbstractPropertyDefinition':
return [...getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.Node} */node.parent.parent), ...getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.Node} */
node.key)];
case 'ClassDeclaration':
case 'ClassExpression':
case 'FunctionDeclaration':
case 'FunctionExpression':
case 'TSDeclareFunction':
case 'TSEnumDeclaration':
case 'TSEnumMember':
case 'TSInterfaceDeclaration':
case 'TSMethodSignature':
case 'TSModuleDeclaration':
case 'TSTypeAliasDeclaration':
return getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.ClassDeclaration} */
node.id);
case 'ExportDefaultDeclaration':
case 'ExportNamedDeclaration':
return getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.ExportNamedDeclaration} */
node.declaration);
case 'Identifier':
return [node.name];
case 'Property':
return getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.Node} */
node.key);
case 'VariableDeclaration':
return getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.Node} */
node.declarations[0]);
case 'VariableDeclarator':
return [...getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.Node} */
node.id), ...getNamesFromNode(/** @type {import('@typescript-eslint/types').TSESTree.Node} */
node.init)].filter(Boolean);
default:
return [];
}
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
node,
report,
utils
}) => {
const /** @type {{aliases: {[key: string]: string[]}, excludedTags: string[], uselessWords: string[]}} */{
aliases = defaultAliases,
excludedTags = [],
uselessWords = defaultUselessWords
} = context.options[0] || {};
const nodeNames = getNamesFromNode(node);
/**
* @param {string} text
* @param {string} extraName
* @returns {boolean}
*/
const descriptionIsRedundant = (text, extraName = '') => {
const textTrimmed = text.trim();
return Boolean(textTrimmed) && !(0, _areDocsInformative.areDocsInformative)(textTrimmed, [extraName, nodeNames].filter(Boolean).join(' '), {
aliases,
uselessWords
});
};
const {
description,
lastDescriptionLine
} = utils.getDescription();
let descriptionReported = false;
for (const tag of jsdoc.tags) {
if (excludedTags.includes(tag.tag)) {
continue;
}
if (descriptionIsRedundant(tag.description, tag.name)) {
utils.reportJSDoc('This tag description only repeats the name it describes.', tag);
}
descriptionReported ||= tag.description === description && /** @type {import('comment-parser').Spec & {line: import('../iterateJsdoc.js').Integer}} */
tag.line === lastDescriptionLine;
}
if (!descriptionReported && descriptionIsRedundant(description)) {
report('This description only repeats the name it describes.');
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'This rule reports doc comments that only restate their attached name.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
aliases: {
description: `The \`aliases\` option allows indicating words as synonyms (aliases) of each other.
For example, with \`{ aliases: { emoji: ["smiley", "winkey"] } }\`, the following comment would be considered uninformative:
\`\`\`js
/** A smiley/winkey. */
let emoji;
\`\`\`
The default \`aliases\` option is:
\`\`\`json
{
"a": ["an", "our"]
}
\`\`\``,
patternProperties: {
'.*': {
items: {
type: 'string'
},
type: 'array'
}
}
},
excludedTags: {
description: `Tags that should not be checked for valid contents.
For example, with \`{ excludedTags: ["category"] }\`, the following comment would not be considered uninformative:
\`\`\`js
/** @category Types */
function computeTypes(node) {
// ...
}
\`\`\`
No tags are excluded by default.`,
items: {
type: 'string'
},
type: 'array'
},
uselessWords: {
description: `Words that are ignored when searching for one that adds meaning.
For example, with \`{ uselessWords: ["our"] }\`, the following comment would be considered uninformative:
\`\`\`js
/** Our text. */
let text;
\`\`\`
The default \`uselessWords\` option is:
\`\`\`json
["a", "an", "i", "in", "of", "s", "the"]
\`\`\``,
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=informativeDocs.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=informativeDocs.d.ts.map

View file

@ -0,0 +1,120 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* Punctuators that begin a logical group should not require a line before it skipped. Specifically
* `[` starts an array, `{` starts an object or block, `(` starts a grouping, and `=` starts a
* declaration (like a variable or a type alias).
*/
const startPunctuators = new Set(['(', '=', '[', '{']);
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdocNode,
report,
sourceCode,
utils
}) => {
const {
checkBlockStarts,
excludedTags = ['type'],
ignoreSameLine = true,
ignoreSingleLines = true,
lines = 1
} = context.options[0] || {};
if (utils.hasATag(excludedTags)) {
return;
}
const tokensBefore = sourceCode.getTokensBefore(jsdocNode, {
includeComments: true
});
const tokenBefore = tokensBefore.at(-1);
if (!tokenBefore || tokenBefore.type === 'Punctuator' && !checkBlockStarts && startPunctuators.has(tokenBefore.value)) {
return;
}
if (tokenBefore.loc?.end?.line + lines >= (/** @type {number} */
jsdocNode.loc?.start?.line)) {
const startLine = jsdocNode.loc?.start?.line;
const sameLine = tokenBefore.loc?.end?.line === startLine;
if (sameLine && ignoreSameLine) {
return;
}
if (ignoreSingleLines && jsdocNode.loc?.start.line === jsdocNode.loc?.end.line) {
return;
}
/** @type {import('eslint').Rule.ReportFixer} */
const fix = fixer => {
let indent = '';
if (sameLine) {
const spaceDiff = /** @type {number} */jsdocNode.loc?.start?.column - (/** @type {number} */tokenBefore.loc?.end?.column);
// @ts-expect-error Should be a comment
indent = /** @type {import('estree').Comment} */jsdocNode.value.match(/^\*\n([\t ]*) \*/v)?.[1]?.slice(spaceDiff);
if (!indent) {
/** @type {import('eslint').AST.Token|import('estree').Comment|undefined} */
let tokenPrior = tokenBefore;
let startColumn;
while (tokenPrior && tokenPrior?.loc?.start?.line === startLine) {
startColumn = tokenPrior.loc?.start?.column;
tokenPrior = tokensBefore.pop();
}
indent = ' '.repeat(/* c8 ignore next */
/** @type {number} */startColumn ? startColumn - 1 : 0);
}
}
return fixer.insertTextAfter(/** @type {import('eslint').AST.Token} */
tokenBefore, '\n'.repeat(lines) + (sameLine ? '\n' + indent : ''));
};
report(`Required ${lines} line(s) before JSDoc block`, fix);
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Enforces minimum number of newlines before JSDoc comment blocks',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/lines-before-block.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
checkBlockStarts: {
description: `Whether to additionally check the start of blocks, such as classes or functions.
Defaults to \`false\`.`,
type: 'boolean'
},
excludedTags: {
description: `An array of tags whose presence in the JSDoc block will prevent the
application of the rule. Defaults to \`['type']\` (i.e., if \`@type\` is present,
lines before the block will not be added).`,
items: {
type: 'string'
},
type: 'array'
},
ignoreSameLine: {
description: `This option excludes cases where the JSDoc block occurs on the same line as a
preceding code or comment. Defaults to \`true\`.`,
type: 'boolean'
},
ignoreSingleLines: {
description: `This option excludes cases where the JSDoc block is only one line long.
Defaults to \`true\`.`,
type: 'boolean'
},
lines: {
description: 'The minimum number of lines to require. Defaults to 1.',
type: 'integer'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=linesBeforeBlock.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=linesBeforeBlock.d.ts.map

View file

@ -0,0 +1,356 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
// If supporting Node >= 10, we could loosen the default to this for the
// initial letter: \\p{Upper}
const matchDescriptionDefault = '^\n?([A-Z`\\d_][\\s\\S]*[.?!`\\p{RGI_Emoji}]\\s*)?$';
/**
* @param {string} value
* @param {string} userDefault
* @returns {string}
*/
const stringOrDefault = (value, userDefault) => {
return typeof value === 'string' ? value : userDefault || matchDescriptionDefault;
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
report,
utils
}) => {
const {
mainDescription,
matchDescription,
message,
nonemptyTags = true,
tags = {}
} = context.options[0] || {};
/**
* @param {string} desc
* @param {import('comment-parser').Spec} [tag]
* @returns {void}
*/
const validateDescription = (desc, tag) => {
let mainDescriptionMatch = mainDescription;
let errorMessage = message;
if (typeof mainDescription === 'object') {
mainDescriptionMatch = mainDescription.match;
errorMessage = mainDescription.message;
}
if (mainDescriptionMatch === false && (!tag || !Object.hasOwn(tags, tag.tag))) {
return;
}
let tagValue = mainDescriptionMatch;
if (tag) {
const tagName = tag.tag;
if (typeof tags[tagName] === 'object') {
tagValue = tags[tagName].match;
errorMessage = tags[tagName].message;
} else {
tagValue = tags[tagName];
}
}
const regex = utils.getRegexFromString(stringOrDefault(tagValue, matchDescription));
if (!regex.test(desc)) {
report(errorMessage || 'JSDoc description does not satisfy the regex pattern.', null, tag || {
// Add one as description would typically be into block
line: jsdoc.source[0].number + 1
});
}
};
const {
description
} = utils.getDescription();
if (description) {
validateDescription(description);
}
/**
* @param {string} tagName
* @returns {boolean}
*/
const hasNoTag = tagName => {
return !tags[tagName];
};
for (const tag of ['description', 'summary', 'file', 'classdesc']) {
utils.forEachPreferredTag(tag, (matchingJsdocTag, targetTagName) => {
const desc = (matchingJsdocTag.name + ' ' + utils.getTagDescription(matchingJsdocTag)).trim();
if (hasNoTag(targetTagName)) {
validateDescription(desc, matchingJsdocTag);
}
}, true);
}
if (nonemptyTags) {
for (const tag of ['copyright', 'example', 'see', 'todo']) {
utils.forEachPreferredTag(tag, (matchingJsdocTag, targetTagName) => {
const desc = (matchingJsdocTag.name + ' ' + utils.getTagDescription(matchingJsdocTag)).trim();
if (hasNoTag(targetTagName) && !/.+/v.test(desc)) {
report('JSDoc description must not be empty.', null, matchingJsdocTag);
}
});
}
}
if (!Object.keys(tags).length) {
return;
}
/**
* @param {string} tagName
* @returns {boolean}
*/
const hasOptionTag = tagName => {
return Boolean(tags[tagName]);
};
const whitelistedTags = utils.filterTags(({
tag: tagName
}) => {
return hasOptionTag(tagName);
});
const {
tagsWithNames,
tagsWithoutNames
} = utils.getTagsByType(whitelistedTags);
tagsWithNames.some(tag => {
const desc = /** @type {string} */utils.getTagDescription(tag).replace(/^[\- ]*/v, '').trim();
return validateDescription(desc, tag);
});
tagsWithoutNames.some(tag => {
const desc = (tag.name + ' ' + utils.getTagDescription(tag)).trim();
return validateDescription(desc, tag);
});
}, {
contextDefaults: true,
meta: {
docs: {
description: 'Enforces a regular expression pattern on descriptions.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/match-description.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
contexts: {
description: `Set this to an array of strings representing the AST context (or an object with
optional \`context\` and \`comment\` properties) where you wish the rule to be applied (e.g.,
\`ClassDeclaration\` for ES6 classes).
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
\`FunctionExpression\`). Set to \`"any"\` if you want the rule to apply to any
JSDoc block throughout your files.
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
comment: {
type: 'string'
},
context: {
type: 'string'
}
},
type: 'object'
}]
},
type: 'array'
},
mainDescription: {
description: `If you wish to override the main block description without changing the
default \`match-description\` (which can cascade to the \`tags\` with \`true\`),
you may use \`mainDescription\`:
\`\`\`js
{
'jsdoc/match-description': ['error', {
mainDescription: '[A-Z].*\\\\.',
tags: {
param: true,
returns: true
}
}]
}
\`\`\`
There is no need to add \`mainDescription: true\`, as by default, the main
block description (and only the main block description) is linted, though you
may disable checking it by setting it to \`false\`.
You may also provide an object with \`message\`:
\`\`\`js
{
'jsdoc/match-description': ['error', {
mainDescription: {
message: 'Capitalize first word of JSDoc block descriptions',
match: '[A-Z].*\\\\.'
},
tags: {
param: true,
returns: true
}
}]
}
\`\`\``,
oneOf: [{
format: 'regex',
type: 'string'
}, {
type: 'boolean'
}, {
additionalProperties: false,
properties: {
match: {
oneOf: [{
format: 'regex',
type: 'string'
}, {
type: 'boolean'
}]
},
message: {
type: 'string'
}
},
type: 'object'
}]
},
matchDescription: {
description: `You can supply your own expression to override the default, passing a
\`matchDescription\` string on the options object.
Defaults to using (only) the \`v\` flag, so
to add your own flags, encapsulate your expression as a string, but like a
literal, e.g., \`/[A-Z].*\\./vi\`.
\`\`\`js
{
'jsdoc/match-description': ['error', {matchDescription: '[A-Z].*\\\\.'}]
}
\`\`\``,
format: 'regex',
type: 'string'
},
message: {
description: `You may provide a custom default message by using the following format:
\`\`\`js
{
'jsdoc/match-description': ['error', {
message: 'The default description should begin with a capital letter.'
}]
}
\`\`\`
This can be overridden per tag or for the main block description by setting
\`message\` within \`tags\` or \`mainDescription\`, respectively.`,
type: 'string'
},
nonemptyTags: {
description: `If not set to \`false\`, will enforce that the following tags have at least
some content:
- \`@copyright\`
- \`@example\`
- \`@see\`
- \`@todo\`
If you supply your own tag description for any of the above tags in \`tags\`,
your description will take precedence.`,
type: 'boolean'
},
tags: {
description: `If you want different regular expressions to apply to tags, you may use
the \`tags\` option object:
\`\`\`js
{
'jsdoc/match-description': ['error', {tags: {
param: '\\\\- [A-Z].*\\\\.',
returns: '[A-Z].*\\\\.'
}}]
}
\`\`\`
In place of a string, you can also add \`true\` to indicate that a particular
tag should be linted with the \`matchDescription\` value (or the default).
\`\`\`js
{
'jsdoc/match-description': ['error', {tags: {
param: true,
returns: true
}}]
}
\`\`\`
Alternatively, you may supply an object with a \`message\` property to indicate
the error message for that tag.
\`\`\`js
{
'jsdoc/match-description': ['error', {tags: {
param: {message: 'Begin with a hyphen', match: '\\\\- [A-Z].*\\\\.'},
returns: {message: 'Capitalize for returns (the default)', match: true}
}}]
}
\`\`\`
The tags \`@param\`/\`@arg\`/\`@argument\` and \`@property\`/\`@prop\` will be properly
parsed to ensure that the matched "description" text includes only the text
after the name.
All other tags will treat the text following the tag name, a space, and
an optional curly-bracketed type expression (and another space) as part of
its "description" (e.g., for \`@returns {someType} some description\`, the
description is \`some description\` while for \`@some-tag xyz\`, the description
is \`xyz\`).`,
patternProperties: {
'.*': {
oneOf: [{
format: 'regex',
type: 'string'
}, {
enum: [true],
type: 'boolean'
}, {
additionalProperties: false,
properties: {
match: {
oneOf: [{
format: 'regex',
type: 'string'
}, {
enum: [true],
type: 'boolean'
}]
},
message: {
type: 'string'
}
},
type: 'object'
}]
}
},
type: 'object'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=matchDescription.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=matchDescription.d.ts.map

View file

@ -0,0 +1,160 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
info: {
lastIndex
},
jsdoc,
report,
utils
}) => {
const {
match
} = context.options[0] || {};
if (!match) {
report('Rule `no-restricted-syntax` is missing a `match` option.');
return;
}
const {
allowName,
disallowName,
replacement,
tags = ['*']
} = match[(/** @type {import('../iterateJsdoc.js').Integer} */lastIndex)];
const allowNameRegex = allowName && utils.getRegexFromString(allowName);
const disallowNameRegex = disallowName && utils.getRegexFromString(disallowName);
let applicableTags = jsdoc.tags;
if (!tags.includes('*')) {
applicableTags = utils.getPresentTags(tags);
}
let reported = false;
for (const tag of applicableTags) {
const tagName = tag.name.replace(/^\[/v, '').replace(/(=.*)?\]$/v, '');
const allowed = !allowNameRegex || allowNameRegex.test(tagName);
const disallowed = disallowNameRegex && disallowNameRegex.test(tagName);
const hasRegex = allowNameRegex || disallowNameRegex;
if (hasRegex && allowed && !disallowed) {
continue;
}
if (!hasRegex && reported) {
continue;
}
const fixer = () => {
for (const src of tag.source) {
if (src.tokens.name) {
src.tokens.name = src.tokens.name.replace(disallowNameRegex, replacement);
break;
}
}
};
let {
message
} = match[(/** @type {import('../iterateJsdoc.js').Integer} */lastIndex)];
if (!message) {
if (hasRegex) {
message = disallowed ? `Only allowing names not matching \`${disallowNameRegex}\` but found "${tagName}".` : `Only allowing names matching \`${allowNameRegex}\` but found "${tagName}".`;
} else {
message = `Prohibited context for "${tagName}".`;
}
}
utils.reportJSDoc(message, hasRegex ? tag : null,
// We could match up
disallowNameRegex && replacement !== undefined ? fixer : null, false, {
// Could also supply `context`, `comment`, `tags`
allowName,
disallowName,
name: tagName
});
if (!hasRegex) {
reported = true;
}
}
}, {
matchContext: true,
meta: {
docs: {
description: 'Reports the name portion of a JSDoc tag if matching or not matching a given regular expression.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/match-name.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
match: {
description: `\`match\` is a required option containing an array of objects which determine
the conditions whereby a name is reported as being problematic.
These objects can have any combination of the following groups of optional
properties, all of which act to confine one another.
Note that \`comment\`, even if targeting a specific tag, is used to match the
whole block. So if a \`comment\` finds its specific tag, it may still apply
fixes found by the likes of \`disallowName\` even when a different tag has the
disallowed name. An alternative is to ensure that \`comment\` finds the specific
tag of the desired tag and/or name and no \`disallowName\` (or \`allowName\`) is
supplied. In such a case, only one error will be reported, but no fixer will
be applied, however.`,
items: {
additionalProperties: false,
properties: {
allowName: {
description: `Indicates which names are allowed for the given tag (or \`*\`).
Accepts a string regular expression (optionally wrapped between two
\`/\` delimiters followed by optional flags) used to match the name.`,
type: 'string'
},
comment: {
description: 'As with `context` but AST for the JSDoc block comment and types.',
type: 'string'
},
context: {
description: `AST to confine the allowing or disallowing to JSDoc blocks
associated with a particular context. See the
["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
type: 'string'
},
disallowName: {
description: 'As with `allowName` but indicates names that are not allowed.',
type: 'string'
},
message: {
description: 'An optional custom message to use when there is a match.',
type: 'string'
},
replacement: {
description: `If \`disallowName\` is supplied and this value is present, it
will replace the matched \`disallowName\` text.`,
type: 'string'
},
tags: {
description: `This array should include tag names or \`*\` to indicate the
match will apply for all tags (except as confined by any context
properties). If \`*\` is not used, then these rules will only apply to
the specified tags. If \`tags\` is omitted, then \`*\` is assumed.`,
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
},
type: 'array'
}
},
required: ['match'],
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=matchName.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=matchName.d.ts.map

View file

@ -0,0 +1,431 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
* @param {import('../iterateJsdoc.js').Utils} utils
* @param {number} requireSingleLineUnderCount
*/
const checkForShortTags = (jsdoc, utils, requireSingleLineUnderCount) => {
if (!requireSingleLineUnderCount || !jsdoc.tags.length) {
return false;
}
let lastLineWithTag = 0;
let isUnderCountLimit = false;
let hasMultiDescOrType = false;
const tagLines = jsdoc.source.reduce((acc, {
tokens: {
delimiter,
description: desc,
name,
postDelimiter,
postName,
postTag,
postType,
start,
tag,
type
}
}, idx) => {
if (tag.length) {
lastLineWithTag = idx;
if (start.length + delimiter.length + postDelimiter.length + type.length + postType.length + name.length + postName.length + tag.length + postTag.length + desc.length < requireSingleLineUnderCount) {
isUnderCountLimit = true;
}
return acc + 1;
} else if (desc.length || type.length) {
hasMultiDescOrType = true;
return acc;
}
return acc;
}, 0);
// Could be tagLines > 1
if (!hasMultiDescOrType && isUnderCountLimit && tagLines === 1) {
const fixer = () => {
const tokens = jsdoc.source[lastLineWithTag].tokens;
jsdoc.source = [{
number: 0,
source: '',
tokens: utils.seedTokens({
delimiter: '/**',
description: tokens.description.trimEnd() + ' ',
end: '*/',
name: tokens.name,
postDelimiter: ' ',
postName: tokens.postName,
postTag: tokens.postTag,
postType: tokens.postType,
start: jsdoc.source[0].tokens.start,
tag: tokens.tag,
type: tokens.type
})
}];
};
utils.reportJSDoc('Description is too short to be multi-line.', null, fixer);
return true;
}
return false;
};
/**
* @param {import('@es-joy/jsdoccomment').JsdocBlockWithInline} jsdoc
* @param {import('../iterateJsdoc.js').Utils} utils
* @param {number} requireSingleLineUnderCount
*/
const checkForShortDescriptions = (jsdoc, utils, requireSingleLineUnderCount) => {
if (!requireSingleLineUnderCount || jsdoc.tags.length) {
return false;
}
let lastLineWithDesc = 0;
let isUnderCountLimit = false;
const descLines = jsdoc.source.reduce((acc, {
tokens: {
delimiter,
description: desc,
postDelimiter,
start
}
}, idx) => {
if (desc.length) {
lastLineWithDesc = idx;
if (start.length + delimiter.length + postDelimiter.length + desc.length < requireSingleLineUnderCount) {
isUnderCountLimit = true;
}
return acc + 1;
}
return acc;
}, 0);
// Could be descLines > 1
if (isUnderCountLimit && descLines === 1) {
const fixer = () => {
const desc = jsdoc.source[lastLineWithDesc].tokens.description;
jsdoc.source = [{
number: 0,
source: '',
tokens: utils.seedTokens({
delimiter: '/**',
description: desc.trimEnd() + ' ',
end: '*/',
postDelimiter: ' ',
start: jsdoc.source[0].tokens.start
})
}];
};
utils.reportJSDoc('Description is too short to be multi-line.', null, fixer);
return true;
}
return false;
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
utils
}) => {
const {
allowMultipleTags = true,
minimumLengthForMultiline = Number.POSITIVE_INFINITY,
multilineTags = ['*'],
noFinalLineText = true,
noMultilineBlocks = false,
noSingleLineBlocks = false,
noZeroLineText = true,
requireSingleLineUnderCount = null,
singleLineTags = ['lends', 'type']
} = context.options[0] || {};
const {
source: [{
tokens
}]
} = jsdoc;
const {
description,
tag
} = tokens;
const sourceLength = jsdoc.source.length;
/**
* @param {string} tagName
* @returns {boolean}
*/
const isInvalidSingleLine = tagName => {
return noSingleLineBlocks && (!tagName || !singleLineTags.includes(tagName) && !singleLineTags.includes('*'));
};
if (sourceLength === 1) {
if (!isInvalidSingleLine(tag.slice(1))) {
return;
}
const fixer = () => {
utils.makeMultiline();
};
utils.reportJSDoc('Single line blocks are not permitted by your configuration.', null, fixer, true);
return;
}
if (checkForShortDescriptions(jsdoc, utils, requireSingleLineUnderCount)) {
return;
}
if (checkForShortTags(jsdoc, utils, requireSingleLineUnderCount)) {
return;
}
const lineChecks = () => {
if (noZeroLineText && (tag || description)) {
const fixer = () => {
const line = {
...tokens
};
utils.emptyTokens(tokens);
const {
tokens: {
delimiter,
start
}
} = jsdoc.source[1];
utils.addLine(1, {
...line,
delimiter,
start
});
};
utils.reportJSDoc('Should have no text on the "0th" line (after the `/**`).', null, fixer);
return;
}
const finalLine = jsdoc.source[jsdoc.source.length - 1];
const finalLineTokens = finalLine.tokens;
if (noFinalLineText && finalLineTokens.description.trim()) {
const fixer = () => {
const line = {
...finalLineTokens
};
line.description = line.description.trimEnd();
const {
delimiter
} = line;
for (const prop of ['delimiter', 'postDelimiter', 'tag', 'type', 'lineEnd', 'postType', 'postTag', 'name', 'postName', 'description']) {
finalLineTokens[(
/**
* @type {"delimiter"|"postDelimiter"|"tag"|"type"|
* "lineEnd"|"postType"|"postTag"|"name"|
* "postName"|"description"}
*/
prop)] = '';
}
utils.addLine(jsdoc.source.length - 1, {
...line,
delimiter,
end: ''
});
};
utils.reportJSDoc('Should have no text on the final line (before the `*/`).', null, fixer);
}
};
if (noMultilineBlocks) {
if (jsdoc.tags.length && (multilineTags.includes('*') || utils.hasATag(multilineTags))) {
lineChecks();
return;
}
if (jsdoc.description.length >= minimumLengthForMultiline) {
lineChecks();
return;
}
if (noSingleLineBlocks && (!jsdoc.tags.length || !utils.filterTags(({
tag: tg
}) => {
return !isInvalidSingleLine(tg);
}).length)) {
utils.reportJSDoc('Multiline JSDoc blocks are prohibited by ' + 'your configuration but fixing would result in a single ' + 'line block which you have prohibited with `noSingleLineBlocks`.');
return;
}
if (jsdoc.tags.length > 1) {
if (!allowMultipleTags) {
utils.reportJSDoc('Multiline JSDoc blocks are prohibited by ' + 'your configuration but the block has multiple tags.');
return;
}
} else if (jsdoc.tags.length === 1 && jsdoc.description.trim()) {
if (!allowMultipleTags) {
utils.reportJSDoc('Multiline JSDoc blocks are prohibited by ' + 'your configuration but the block has a description with a tag.');
return;
}
} else {
const fixer = () => {
jsdoc.source = [{
number: 1,
source: '',
tokens: jsdoc.source.reduce((obj, {
tokens: {
description: desc,
lineEnd,
name: nme,
postName,
postTag,
postType,
tag: tg,
type: typ
}
}) => {
if (typ) {
obj.type = typ;
}
if (tg && typ && nme) {
obj.postType = postType;
}
if (nme) {
obj.name += nme;
}
if (nme && desc) {
obj.postName = postName;
}
obj.description += desc;
const nameOrDescription = obj.description || obj.name;
if (nameOrDescription && nameOrDescription.slice(-1) !== ' ') {
obj.description += ' ';
}
obj.lineEnd = lineEnd;
// Already filtered for multiple tags
obj.tag += tg;
if (tg) {
obj.postTag = postTag || ' ';
}
return obj;
}, utils.seedTokens({
delimiter: '/**',
end: '*/',
postDelimiter: ' '
}))
}];
};
utils.reportJSDoc('Multiline JSDoc blocks are prohibited by ' + 'your configuration.', null, fixer);
return;
}
}
lineChecks();
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Controls how and whether JSDoc blocks can be expressed as single or multiple line blocks.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/multiline-blocks.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
allowMultipleTags: {
description: `If \`noMultilineBlocks\` is set to \`true\` with this option and multiple tags are
found in a block, an error will not be reported.
Since multiple-tagged lines cannot be collapsed into a single line, this option
prevents them from being reported. Set to \`false\` if you really want to report
any blocks.
This option will also be applied when there is a block description and a single
tag (since a description cannot precede a tag on a single line, and also
cannot be reliably added after the tag either).
Defaults to \`true\`.`,
type: 'boolean'
},
minimumLengthForMultiline: {
description: `If \`noMultilineBlocks\` is set with this numeric option, multiline blocks will
be permitted if containing at least the given amount of text.
If not set, multiline blocks will not be permitted regardless of length unless
a relevant tag is present and \`multilineTags\` is set.
Defaults to not being in effect.`,
type: 'integer'
},
multilineTags: {
anyOf: [{
enum: ['*'],
type: 'string'
}, {
items: {
type: 'string'
},
type: 'array'
}],
description: `If \`noMultilineBlocks\` is set with this option, multiline blocks may be allowed
regardless of length as long as a tag or a tag of a certain type is present.
If \`*\` is included in the array, the presence of a tags will allow for
multiline blocks (but not when without any tags unless the amount of text is
over an amount specified by \`minimumLengthForMultiline\`).
If the array does not include \`*\` but lists certain tags, the presence of
such a tag will cause multiline blocks to be allowed.
You may set this to an empty array to prevent any tag from permitting multiple
lines.
Defaults to \`['*']\`.`
},
noFinalLineText: {
description: `For multiline blocks, any non-whitespace text preceding the \`*/\` on the final
line will be reported. (Text preceding a newline is not reported.)
\`noMultilineBlocks\` will have priority over this rule if it applies.
Defaults to \`true\`.`,
type: 'boolean'
},
noMultilineBlocks: {
description: `Requires that JSDoc blocks are restricted to single lines only unless impacted
by the options \`minimumLengthForMultiline\`, \`multilineTags\`, or
\`allowMultipleTags\`.
Defaults to \`false\`.`,
type: 'boolean'
},
noSingleLineBlocks: {
description: `If this is \`true\`, any single line blocks will be reported, except those which
are whitelisted in \`singleLineTags\`.
Defaults to \`false\`.`,
type: 'boolean'
},
noZeroLineText: {
description: `For multiline blocks, any non-whitespace text immediately after the \`/**\` and
space will be reported. (Text after a newline is not reported.)
\`noMultilineBlocks\` will have priority over this rule if it applies.
Defaults to \`true\`.`,
type: 'boolean'
},
requireSingleLineUnderCount: {
description: `If this number is set, it indicates a minimum line width for a single line of
JSDoc content spread over a multi-line comment block. If a single line is under
the minimum length, it will be reported so as to enforce single line JSDoc blocks
for such cases. Blocks are not reported which have multi-line descriptions,
multiple tags, a block description and tag, or tags with multi-line types or
descriptions.
Defaults to \`null\`.`,
type: 'number'
},
singleLineTags: {
description: `An array of tags which can nevertheless be allowed as single line blocks when
\`noSingleLineBlocks\` is set. You may set this to a empty array to
cause all single line blocks to be reported. If \`'*'\` is present, then
the presence of a tag will allow single line blocks (but not if a tag is
missing).
Defaults to \`['lends', 'type']\`.`,
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=multilineBlocks.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=multilineBlocks.d.ts.map

View file

@ -0,0 +1,100 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _commentParser = require("comment-parser");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
// Neither a single nor 3+ asterisks are valid JSDoc per
// https://jsdoc.app/about-getting-started.html#adding-documentation-comments-to-your-code
const commentRegexp = /^\/\*(?!\*)/v;
const extraAsteriskCommentRegexp = /^\/\*{3,}/v;
var _default = exports.default = (0, _iterateJsdoc.default)(({
allComments,
context,
makeReport,
sourceCode
}) => {
const [{
ignore = ['ts-check', 'ts-expect-error', 'ts-ignore', 'ts-nocheck'],
preventAllMultiAsteriskBlocks = false
} = {}] = context.options;
let extraAsterisks = false;
const nonJsdocNodes = /** @type {import('estree').Node[]} */allComments.filter(comment => {
const commentText = sourceCode.getText(comment);
const initialText = commentText.replace(commentRegexp, '').trimStart();
if (['eslint'].some(directive => {
return initialText.startsWith(directive);
})) {
return false;
}
let sliceIndex = 2;
if (!commentRegexp.test(commentText)) {
const multiline = extraAsteriskCommentRegexp.exec(commentText)?.[0];
if (!multiline) {
return false;
}
sliceIndex = multiline.length;
extraAsterisks = true;
if (preventAllMultiAsteriskBlocks) {
return true;
}
}
const tags = ((0, _commentParser.parse)(`${commentText.slice(0, 2)}*${commentText.slice(sliceIndex)}`)[0] || {}).tags ?? [];
return tags.length && !tags.some(({
tag
}) => {
return ignore.includes(tag);
});
});
if (!nonJsdocNodes.length) {
return;
}
for (const node of nonJsdocNodes) {
const report = /** @type {import('../iterateJsdoc.js').MakeReport} */makeReport(context, node);
// eslint-disable-next-line no-loop-func
const fix = /** @type {import('eslint').Rule.ReportFixer} */fixer => {
const text = sourceCode.getText(node);
return fixer.replaceText(node, extraAsterisks ? text.replace(extraAsteriskCommentRegexp, '/**') : text.replace('/*', '/**'));
};
report('Expected JSDoc-like comment to begin with two asterisks.', fix);
}
}, {
checkFile: true,
meta: {
docs: {
description: 'This rule checks for multi-line-style comments which fail to meet the criteria of a JSDoc block.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-bad-blocks.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
ignore: {
description: `An array of directives that will not be reported if present at the beginning of
a multi-comment block and at-sign \`/* @\`.
Defaults to \`['ts-check', 'ts-expect-error', 'ts-ignore', 'ts-nocheck']\`
(some directives [used by TypeScript](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html#ts-check)).`,
items: {
type: 'string'
},
type: 'array'
},
preventAllMultiAsteriskBlocks: {
description: `A boolean (defaulting to \`false\`) which if \`true\` will prevent all
JSDoc-like blocks with more than two initial asterisks even those without
apparent tag content.`,
type: 'boolean'
}
},
type: 'object'
}],
type: 'layout'
}
});
module.exports = exports.default;
//# sourceMappingURL=noBadBlocks.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noBadBlocks.d.ts.map

View file

@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const anyWhitespaceLines = /^\s*$/v;
const atLeastTwoLinesWhitespace = /^[ \t]*\n[ \t]*\n\s*$/v;
var _default = exports.default = (0, _iterateJsdoc.default)(({
jsdoc,
utils
}) => {
const {
description,
descriptions,
lastDescriptionLine
} = utils.getDescription();
const regex = jsdoc.tags.length ? anyWhitespaceLines : atLeastTwoLinesWhitespace;
if (descriptions.length && regex.test(description)) {
if (jsdoc.tags.length) {
utils.reportJSDoc('There should be no blank lines in block descriptions followed by tags.', {
line: lastDescriptionLine
}, () => {
utils.setBlockDescription(() => {
// Remove all lines
return [];
});
});
} else {
utils.reportJSDoc('There should be no extra blank lines in block descriptions not followed by tags.', {
line: lastDescriptionLine
}, () => {
utils.setBlockDescription((info, seedTokens) => {
return [
// Keep the starting line
{
number: 0,
source: '',
tokens: seedTokens({
...info,
description: ''
})
}];
});
});
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'If tags are present, this rule will prevent empty lines in the block description. If no tags are present, this rule will prevent extra empty lines in the block description.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-block-descriptions.md#repos-sticky-header'
},
fixable: 'whitespace',
schema: [],
type: 'layout'
}
});
module.exports = exports.default;
//# sourceMappingURL=noBlankBlockDescriptions.cjs.map

View file

@ -0,0 +1 @@
{"version":3,"file":"noBlankBlockDescriptions.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","anyWhitespaceLines","atLeastTwoLinesWhitespace","_default","exports","iterateJsdoc","jsdoc","utils","description","descriptions","lastDescriptionLine","getDescription","regex","tags","length","test","reportJSDoc","line","setBlockDescription","info","seedTokens","number","source","tokens","iterateAllJsdocs","meta","docs","url","fixable","schema","type","module"],"sources":["../../src/rules/noBlankBlockDescriptions.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nconst anyWhitespaceLines = /^\\s*$/v;\nconst atLeastTwoLinesWhitespace = /^[ \\t]*\\n[ \\t]*\\n\\s*$/v;\n\nexport default iterateJsdoc(({\n jsdoc,\n utils,\n}) => {\n const {\n description,\n descriptions,\n lastDescriptionLine,\n } = utils.getDescription();\n\n const regex = jsdoc.tags.length ?\n anyWhitespaceLines :\n atLeastTwoLinesWhitespace;\n\n if (descriptions.length && regex.test(description)) {\n if (jsdoc.tags.length) {\n utils.reportJSDoc(\n 'There should be no blank lines in block descriptions followed by tags.',\n {\n line: lastDescriptionLine,\n },\n () => {\n utils.setBlockDescription(() => {\n // Remove all lines\n return [];\n });\n },\n );\n } else {\n utils.reportJSDoc(\n 'There should be no extra blank lines in block descriptions not followed by tags.',\n {\n line: lastDescriptionLine,\n },\n () => {\n utils.setBlockDescription((info, seedTokens) => {\n return [\n // Keep the starting line\n {\n number: 0,\n source: '',\n tokens: seedTokens({\n ...info,\n description: '',\n }),\n },\n ];\n });\n },\n );\n }\n }\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'If tags are present, this rule will prevent empty lines in the block description. If no tags are present, this rule will prevent extra empty lines in the block description.',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-block-descriptions.md#repos-sticky-header',\n },\n fixable: 'whitespace',\n schema: [],\n type: 'layout',\n },\n});\n"],"mappings":";;;;;;AAAA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA8C,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAE9C,MAAMG,kBAAkB,GAAG,QAAQ;AACnC,MAAMC,yBAAyB,GAAG,wBAAwB;AAAC,IAAAC,QAAA,GAAAC,OAAA,CAAAJ,OAAA,GAE5C,IAAAK,qBAAY,EAAC,CAAC;EAC3BC,KAAK;EACLC;AACF,CAAC,KAAK;EACJ,MAAM;IACJC,WAAW;IACXC,YAAY;IACZC;EACF,CAAC,GAAGH,KAAK,CAACI,cAAc,CAAC,CAAC;EAE1B,MAAMC,KAAK,GAAGN,KAAK,CAACO,IAAI,CAACC,MAAM,GAC7Bb,kBAAkB,GAClBC,yBAAyB;EAE3B,IAAIO,YAAY,CAACK,MAAM,IAAIF,KAAK,CAACG,IAAI,CAACP,WAAW,CAAC,EAAE;IAClD,IAAIF,KAAK,CAACO,IAAI,CAACC,MAAM,EAAE;MACrBP,KAAK,CAACS,WAAW,CACf,wEAAwE,EACxE;QACEC,IAAI,EAAEP;MACR,CAAC,EACD,MAAM;QACJH,KAAK,CAACW,mBAAmB,CAAC,MAAM;UAC9B;UACA,OAAO,EAAE;QACX,CAAC,CAAC;MACJ,CACF,CAAC;IACH,CAAC,MAAM;MACLX,KAAK,CAACS,WAAW,CACf,kFAAkF,EAClF;QACEC,IAAI,EAAEP;MACR,CAAC,EACD,MAAM;QACJH,KAAK,CAACW,mBAAmB,CAAC,CAACC,IAAI,EAAEC,UAAU,KAAK;UAC9C,OAAO;UACL;UACA;YACEC,MAAM,EAAE,CAAC;YACTC,MAAM,EAAE,EAAE;YACVC,MAAM,EAAEH,UAAU,CAAC;cACjB,GAAGD,IAAI;cACPX,WAAW,EAAE;YACf,CAAC;UACH,CAAC,CACF;QACH,CAAC,CAAC;MACJ,CACF,CAAC;IACH;EACF;AACF,CAAC,EAAE;EACDgB,gBAAgB,EAAE,IAAI;EACtBC,IAAI,EAAE;IACJC,IAAI,EAAE;MACJlB,WAAW,EAAE,8KAA8K;MAC3LmB,GAAG,EAAE;IACP,CAAC;IACDC,OAAO,EAAE,YAAY;IACrBC,MAAM,EAAE,EAAE;IACVC,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAAAC,MAAA,CAAA3B,OAAA,GAAAA,OAAA,CAAAJ,OAAA","ignoreList":[]}

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noBlankBlockDescriptions.d.ts.map

View file

@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
utils
}) => {
if (jsdoc.tags.length) {
return;
}
const {
description,
lastDescriptionLine
} = utils.getDescription();
if (description.trim()) {
return;
}
const {
enableFixer
} = context.options[0] || {};
utils.reportJSDoc('No empty blocks', {
line: lastDescriptionLine
}, enableFixer ? () => {
jsdoc.source.splice(0);
} : null);
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Removes empty blocks with nothing but possibly line breaks',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-blocks.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
enableFixer: {
description: 'Whether or not to auto-remove the blank block. Defaults to `false`.',
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=noBlankBlocks.cjs.map

View file

@ -0,0 +1 @@
{"version":3,"file":"noBlankBlocks.cjs","names":["_iterateJsdoc","_interopRequireDefault","require","e","__esModule","default","_default","exports","iterateJsdoc","context","jsdoc","utils","tags","length","description","lastDescriptionLine","getDescription","trim","enableFixer","options","reportJSDoc","line","source","splice","iterateAllJsdocs","meta","docs","url","fixable","schema","additionalProperties","properties","type","module"],"sources":["../../src/rules/noBlankBlocks.js"],"sourcesContent":["import iterateJsdoc from '../iterateJsdoc.js';\n\nexport default iterateJsdoc(({\n context,\n jsdoc,\n utils,\n}) => {\n if (jsdoc.tags.length) {\n return;\n }\n\n const {\n description,\n lastDescriptionLine,\n } = utils.getDescription();\n if (description.trim()) {\n return;\n }\n\n const {\n enableFixer,\n } = context.options[0] || {};\n\n utils.reportJSDoc(\n 'No empty blocks',\n {\n line: lastDescriptionLine,\n },\n enableFixer ? () => {\n jsdoc.source.splice(0);\n } : null,\n );\n}, {\n iterateAllJsdocs: true,\n meta: {\n docs: {\n description: 'Removes empty blocks with nothing but possibly line breaks',\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-blank-blocks.md#repos-sticky-header',\n },\n fixable: 'code',\n schema: [\n {\n additionalProperties: false,\n properties: {\n enableFixer: {\n description: 'Whether or not to auto-remove the blank block. Defaults to `false`.',\n type: 'boolean',\n },\n },\n type: 'object',\n },\n ],\n type: 'suggestion',\n },\n});\n"],"mappings":";;;;;;AAAA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AAA8C,SAAAD,uBAAAE,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAAA,IAAAG,QAAA,GAAAC,OAAA,CAAAF,OAAA,GAE/B,IAAAG,qBAAY,EAAC,CAAC;EAC3BC,OAAO;EACPC,KAAK;EACLC;AACF,CAAC,KAAK;EACJ,IAAID,KAAK,CAACE,IAAI,CAACC,MAAM,EAAE;IACrB;EACF;EAEA,MAAM;IACJC,WAAW;IACXC;EACF,CAAC,GAAGJ,KAAK,CAACK,cAAc,CAAC,CAAC;EAC1B,IAAIF,WAAW,CAACG,IAAI,CAAC,CAAC,EAAE;IACtB;EACF;EAEA,MAAM;IACJC;EACF,CAAC,GAAGT,OAAO,CAACU,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;EAE5BR,KAAK,CAACS,WAAW,CACf,iBAAiB,EACjB;IACEC,IAAI,EAAEN;EACR,CAAC,EACDG,WAAW,GAAG,MAAM;IAClBR,KAAK,CAACY,MAAM,CAACC,MAAM,CAAC,CAAC,CAAC;EACxB,CAAC,GAAG,IACN,CAAC;AACH,CAAC,EAAE;EACDC,gBAAgB,EAAE,IAAI;EACtBC,IAAI,EAAE;IACJC,IAAI,EAAE;MACJZ,WAAW,EAAE,4DAA4D;MACzEa,GAAG,EAAE;IACP,CAAC;IACDC,OAAO,EAAE,MAAM;IACfC,MAAM,EAAE,CACN;MACEC,oBAAoB,EAAE,KAAK;MAC3BC,UAAU,EAAE;QACVb,WAAW,EAAE;UACXJ,WAAW,EAAE,qEAAqE;UAClFkB,IAAI,EAAE;QACR;MACF,CAAC;MACDA,IAAI,EAAE;IACR,CAAC,CACF;IACDA,IAAI,EAAE;EACR;AACF,CAAC,CAAC;AAAAC,MAAA,CAAA1B,OAAA,GAAAA,OAAA,CAAAF,OAAA","ignoreList":[]}

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noBlankBlocks.d.ts.map

View file

@ -0,0 +1,102 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
utils
}) => {
const {
noOptionalParamNames
} = context.options[0] || {};
const paramTags = utils.getPresentTags(['param', 'arg', 'argument']);
for (const tag of paramTags) {
if (noOptionalParamNames && tag.optional) {
utils.reportJSDoc(`Optional param names are not permitted on @${tag.tag}.`, tag, () => {
utils.changeTag(tag, {
name: tag.name.replace(/([^=]*)(=.+)?/v, '$1')
});
});
} else if (tag.default) {
utils.reportJSDoc(`Defaults are not permitted on @${tag.tag}.`, tag, () => {
utils.changeTag(tag, {
name: tag.name.replace(/([^=]*)(=.+)?/v, '[$1]')
});
});
}
}
const defaultTags = utils.getPresentTags(['default', 'defaultvalue']);
for (const tag of defaultTags) {
if (tag.description.trim()) {
utils.reportJSDoc(`Default values are not permitted on @${tag.tag}.`, tag, () => {
utils.changeTag(tag, {
description: '',
postTag: ''
});
});
}
}
}, {
contextDefaults: true,
meta: {
docs: {
description: 'This rule reports defaults being used on the relevant portion of `@param` or `@default`.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-defaults.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
contexts: {
description: `Set this to an array of strings representing the AST context (or an object with
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
\`FunctionExpression\`). Set to \`"any"\` if you want
the rule to apply to any JSDoc block throughout your files (as is necessary
for finding function blocks not attached to a function declaration or
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
\`@method\`) (including those associated with an \`@interface\`).
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
comment: {
type: 'string'
},
context: {
type: 'string'
}
},
type: 'object'
}]
},
type: 'array'
},
noOptionalParamNames: {
description: `Set this to \`true\` to report the presence of optional parameters. May be
used if the project is insisting on optionality being indicated by
the presence of ES6 default parameters (bearing in mind that such
"defaults" are only applied when the supplied value is missing or
\`undefined\` but not for \`null\` or other "falsey" values).`,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=noDefaults.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noDefaults.d.ts.map

View file

@ -0,0 +1,196 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @typedef {{
* comment: string,
* context: string,
* message: string,
* minimum: import('../iterateJsdoc.js').Integer
* }} ContextObject
*/
/**
* @typedef {string|ContextObject} Context
*/
/**
* @param {import('../iterateJsdoc.js').StateObject} state
* @returns {void}
*/
const setDefaults = state => {
if (!state.selectorMap) {
state.selectorMap = {};
}
};
/**
* @param {import('../iterateJsdoc.js').StateObject} state
* @param {string} selector
* @param {string} comment
* @returns {void}
*/
const incrementSelector = (state, selector, comment) => {
if (!state.selectorMap[selector]) {
state.selectorMap[selector] = {};
}
if (!state.selectorMap[selector][comment]) {
state.selectorMap[selector][comment] = 0;
}
state.selectorMap[selector][comment]++;
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
info: {
comment
},
state,
utils
}) => {
if (!context.options[0]) {
// Handle error later
return;
}
/**
* @type {Context[]}
*/
const contexts = context.options[0].contexts;
const {
contextStr
} = utils.findContext(contexts, comment);
setDefaults(state);
incrementSelector(state, contextStr, String(comment));
}, {
contextSelected: true,
exit({
context,
settings,
state
}) {
if (!context.options.length && !settings.contexts) {
context.report({
loc: {
end: {
column: 1,
line: 1
},
start: {
column: 1,
line: 1
}
},
message: 'Rule `no-missing-syntax` is missing a `contexts` option.'
});
return;
}
setDefaults(state);
/**
* @type {Context[]}
*/
const contexts = (context.options[0] ?? {}).contexts ?? settings?.contexts;
// Report when MISSING
contexts.some(cntxt => {
const contextStr = typeof cntxt === 'object' ? cntxt.context ?? 'any' : cntxt;
const comment = typeof cntxt === 'string' ? '' : cntxt?.comment ?? '';
const contextKey = contextStr === 'any' ? 'undefined' : contextStr;
if ((!state.selectorMap[contextKey] || !state.selectorMap[contextKey][comment] || state.selectorMap[contextKey][comment] < (
// @ts-expect-error comment would need an object, not string
cntxt?.minimum ?? 1)) && (contextStr !== 'any' || Object.values(state.selectorMap).every(cmmnt => {
return !cmmnt[comment] || cmmnt[comment] < (
// @ts-expect-error comment would need an object, not string
cntxt?.minimum ?? 1);
}))) {
const message = typeof cntxt === 'string' ? 'Syntax is required: {{context}}' : cntxt?.message ?? 'Syntax is required: {{context}}' + (comment ? ' with {{comment}}' : '');
context.report({
data: {
comment,
context: contextStr
},
loc: {
end: {
column: 1,
line: 1
},
start: {
column: 1,
line: 1
}
},
message
});
return true;
}
return false;
});
},
matchContext: true,
meta: {
docs: {
description: 'Reports when certain comment structures are always expected.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-missing-syntax.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
contexts: {
description: `Set this to an array of strings representing the AST context (or an object with
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
Use the \`minimum\` property (defaults to 1) to indicate how many are required
for the rule to be reported.
Use the \`message\` property to indicate the specific error to be shown when an
error is reported for that context being found missing. You may use
\`{{context}}\` and \`{{comment}}\` with such messages. Defaults to
\`"Syntax is required: {{context}}"\`, or with a comment, to
\`"Syntax is required: {{context}} with {{comment}}"\`.
Set to \`"any"\` if you want the rule to apply to any JSDoc block throughout
your files (as is necessary for finding function blocks not attached to a
function declaration or expression, i.e., \`@callback\` or \`@function\` (or its
aliases \`@func\` or \`@method\`) (including those associated with an \`@interface\`).
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
comment: {
type: 'string'
},
context: {
type: 'string'
},
message: {
type: 'string'
},
minimum: {
type: 'integer'
}
},
type: 'object'
}]
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=noMissingSyntax.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,10 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
export type ContextObject = {
comment: string;
context: string;
message: string;
minimum: import("../iterateJsdoc.js").Integer;
};
export type Context = string | ContextObject;
//# sourceMappingURL=noMissingSyntax.d.ts.map

View file

@ -0,0 +1,126 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const middleAsterisksBlockWS = /^([\t ]|\*(?!\*))+/v;
const middleAsterisksNoBlockWS = /^\*+/v;
const endAsterisksSingleLineBlockWS = /\*((?:\*|(?: |\t))*)\*$/v;
const endAsterisksMultipleLineBlockWS = /((?:\*|(?: |\t))*)\*$/v;
const endAsterisksSingleLineNoBlockWS = /\*(\**)\*$/v;
const endAsterisksMultipleLineNoBlockWS = /(\**)\*$/v;
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
utils
}) => {
const {
allowWhitespace = false,
preventAtEnd = true,
preventAtMiddleLines = true
} = context.options[0] || {};
const middleAsterisks = allowWhitespace ? middleAsterisksNoBlockWS : middleAsterisksBlockWS;
jsdoc.source.some(({
number,
tokens
}) => {
const {
delimiter,
description,
end,
name,
postDelimiter,
tag,
type
} = tokens;
if (preventAtMiddleLines && !end && !tag && !type && !name && (!allowWhitespace && middleAsterisks.test(description) || allowWhitespace && middleAsterisks.test(postDelimiter + description))) {
// console.log('description', JSON.stringify(description));
const fix = () => {
tokens.description = description.replace(middleAsterisks, '');
};
utils.reportJSDoc('Should be no multiple asterisks on middle lines.', {
line: number
}, fix, true);
return true;
}
if (!preventAtEnd || !end) {
return false;
}
const isSingleLineBlock = delimiter === '/**';
const delim = isSingleLineBlock ? '*' : delimiter;
const endAsterisks = allowWhitespace ? isSingleLineBlock ? endAsterisksSingleLineNoBlockWS : endAsterisksMultipleLineNoBlockWS : isSingleLineBlock ? endAsterisksSingleLineBlockWS : endAsterisksMultipleLineBlockWS;
const endingAsterisksAndSpaces = (allowWhitespace ? postDelimiter + description + delim : description + delim).match(endAsterisks);
if (!endingAsterisksAndSpaces || !isSingleLineBlock && endingAsterisksAndSpaces[1] && !endingAsterisksAndSpaces[1].trim()) {
return false;
}
const endFix = () => {
if (!isSingleLineBlock) {
tokens.delimiter = '';
}
tokens.description = (description + delim).replace(endAsterisks, '');
};
utils.reportJSDoc('Should be no multiple asterisks on end lines.', {
line: number
}, endFix, true);
return true;
});
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Prevents use of multiple asterisks at the beginning of lines.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-multi-asterisks.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
allowWhitespace: {
description: `Set to \`true\` if you wish to allow asterisks after a space (as with Markdown):
\`\`\`js
/**
* *bold* text
*/
\`\`\`
Defaults to \`false\`.`,
type: 'boolean'
},
preventAtEnd: {
description: `Prevent the likes of this:
\`\`\`js
/**
*
*
**/
\`\`\`
Defaults to \`true\`.`,
type: 'boolean'
},
preventAtMiddleLines: {
description: `Prevent the likes of this:
\`\`\`js
/**
*
**
*/
\`\`\`
Defaults to \`true\`.`,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=noMultiAsterisks.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noMultiAsterisks.d.ts.map

View file

@ -0,0 +1,68 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _buildForbidRuleDefinition = require("../buildForbidRuleDefinition.cjs");
var _default = exports.default = (0, _buildForbidRuleDefinition.buildForbidRuleDefinition)({
getContexts(context, report) {
if (!context.options.length) {
report('Rule `no-restricted-syntax` is missing a `contexts` option.');
return false;
}
const {
contexts
} = context.options[0];
return contexts;
},
schema: [{
additionalProperties: false,
properties: {
contexts: {
description: `Set this to an array of strings representing the AST context (or an object with
\`context\` and \`comment\` properties) where you wish the rule to be applied.
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
Use the \`message\` property to indicate the specific error to be shown when an
error is reported for that context being found. Defaults to
\`"Syntax is restricted: {{context}}"\`, or with a comment, to
\`"Syntax is restricted: {{context}} with {{comment}}"\`.
Set to \`"any"\` if you want the rule to apply to any JSDoc block throughout
your files (as is necessary for finding function blocks not attached to a
function declaration or expression, i.e., \`@callback\` or \`@function\` (or its
aliases \`@func\` or \`@method\`) (including those associated with an \`@interface\`).
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
comment: {
type: 'string'
},
context: {
type: 'string'
},
message: {
type: 'string'
}
},
type: 'object'
}]
},
type: 'array'
}
},
required: ['contexts'],
type: 'object'
}],
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-restricted-syntax.md#repos-sticky-header'
});
module.exports = exports.default;
//# sourceMappingURL=noRestrictedSyntax.cjs.map

View file

@ -0,0 +1 @@
{"version":3,"file":"noRestrictedSyntax.cjs","names":["_buildForbidRuleDefinition","require","_default","exports","default","buildForbidRuleDefinition","getContexts","context","report","options","length","contexts","schema","additionalProperties","properties","description","items","anyOf","type","comment","message","required","url","module"],"sources":["../../src/rules/noRestrictedSyntax.js"],"sourcesContent":["import {\n buildForbidRuleDefinition,\n} from '../buildForbidRuleDefinition.js';\n\nexport default buildForbidRuleDefinition({\n getContexts (context, report) {\n if (!context.options.length) {\n report('Rule `no-restricted-syntax` is missing a `contexts` option.');\n return false;\n }\n\n const {\n contexts,\n } = context.options[0];\n\n return contexts;\n },\n schema: [\n {\n additionalProperties: false,\n properties: {\n contexts: {\n description: `Set this to an array of strings representing the AST context (or an object with\n\\`context\\` and \\`comment\\` properties) where you wish the rule to be applied.\n\n\\`context\\` defaults to \\`any\\` and \\`comment\\` defaults to no specific comment context.\n\nUse the \\`message\\` property to indicate the specific error to be shown when an\nerror is reported for that context being found. Defaults to\n\\`\"Syntax is restricted: {{context}}\"\\`, or with a comment, to\n\\`\"Syntax is restricted: {{context}} with {{comment}}\"\\`.\n\nSet to \\`\"any\"\\` if you want the rule to apply to any JSDoc block throughout\nyour files (as is necessary for finding function blocks not attached to a\nfunction declaration or expression, i.e., \\`@callback\\` or \\`@function\\` (or its\naliases \\`@func\\` or \\`@method\\`) (including those associated with an \\`@interface\\`).\n\nSee the [\"AST and Selectors\"](../#advanced-ast-and-selectors)\nsection of our Advanced docs for more on the expected format.`,\n items: {\n anyOf: [\n {\n type: 'string',\n },\n {\n additionalProperties: false,\n properties: {\n comment: {\n type: 'string',\n },\n context: {\n type: 'string',\n },\n message: {\n type: 'string',\n },\n },\n type: 'object',\n },\n ],\n },\n type: 'array',\n },\n },\n required: [\n 'contexts',\n ],\n type: 'object',\n },\n ],\n url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-restricted-syntax.md#repos-sticky-header',\n});\n"],"mappings":";;;;;;AAAA,IAAAA,0BAAA,GAAAC,OAAA;AAEyC,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAE1B,IAAAC,oDAAyB,EAAC;EACvCC,WAAWA,CAAEC,OAAO,EAAEC,MAAM,EAAE;IAC5B,IAAI,CAACD,OAAO,CAACE,OAAO,CAACC,MAAM,EAAE;MAC3BF,MAAM,CAAC,6DAA6D,CAAC;MACrE,OAAO,KAAK;IACd;IAEA,MAAM;MACJG;IACF,CAAC,GAAGJ,OAAO,CAACE,OAAO,CAAC,CAAC,CAAC;IAEtB,OAAOE,QAAQ;EACjB,CAAC;EACDC,MAAM,EAAE,CACN;IACEC,oBAAoB,EAAE,KAAK;IAC3BC,UAAU,EAAE;MACVH,QAAQ,EAAE;QACRI,WAAW,EAAE;AACvB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,8DAA8D;QACpDC,KAAK,EAAE;UACLC,KAAK,EAAE,CACL;YACEC,IAAI,EAAE;UACR,CAAC,EACD;YACEL,oBAAoB,EAAE,KAAK;YAC3BC,UAAU,EAAE;cACVK,OAAO,EAAE;gBACPD,IAAI,EAAE;cACR,CAAC;cACDX,OAAO,EAAE;gBACPW,IAAI,EAAE;cACR,CAAC;cACDE,OAAO,EAAE;gBACPF,IAAI,EAAE;cACR;YACF,CAAC;YACDA,IAAI,EAAE;UACR,CAAC;QAEL,CAAC;QACDA,IAAI,EAAE;MACR;IACF,CAAC;IACDG,QAAQ,EAAE,CACR,UAAU,CACX;IACDH,IAAI,EAAE;EACR,CAAC,CACF;EACDI,GAAG,EAAE;AACP,CAAC,CAAC;AAAAC,MAAA,CAAApB,OAAA,GAAAA,OAAA,CAAAC,OAAA","ignoreList":[]}

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noRestrictedSyntax.d.ts.map

View file

@ -0,0 +1,101 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @param {import('comment-parser').Line} line
*/
const removeType = ({
tokens
}) => {
tokens.postTag = '';
tokens.type = '';
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
node,
utils
}) => {
if (!utils.isIteratingFunctionOrVariable() && !utils.isVirtualFunction()) {
return;
}
const tags = utils.getPresentTags(['param', 'arg', 'argument', 'returns', 'return']);
for (const tag of tags) {
if (tag.type) {
utils.reportJSDoc(`Types are not permitted on @${tag.tag}.`, tag, () => {
for (const source of tag.source) {
removeType(source);
}
});
}
}
if (node?.type === 'ClassDeclaration') {
const propertyTags = utils.getPresentTags(['prop', 'property']);
for (const tag of propertyTags) {
if (tag.type) {
utils.reportJSDoc(`Types are not permitted on @${tag.tag} in the supplied context.`, tag, () => {
for (const source of tag.source) {
removeType(source);
}
});
}
}
}
}, {
contextDefaults: ['ArrowFunctionExpression', 'FunctionDeclaration', 'FunctionExpression', 'TSDeclareFunction',
// Add this to above defaults
'TSMethodSignature', 'ClassDeclaration'],
meta: {
docs: {
description: 'This rule reports types being used on `@param` or `@returns` (redundant with TypeScript).',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-types.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
contexts: {
description: `Set this to an array of strings representing the AST context (or an object with
optional \`context\` and \`comment\` properties) where you wish the rule to be applied.
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
Overrides the default contexts (\`ArrowFunctionExpression\`, \`FunctionDeclaration\`,
\`FunctionExpression\`, \`TSDeclareFunction\`, \`TSMethodSignature\`,
\`ClassDeclaration\`). Set to \`"any"\` if you want
the rule to apply to any JSDoc block throughout your files (as is necessary
for finding function blocks not attached to a function declaration or
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
\`@method\`) (including those associated with an \`@interface\`).
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
comment: {
type: 'string'
},
context: {
type: 'string'
}
},
type: 'object'
}]
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=noTypes.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noTypes.d.ts.map

View file

@ -0,0 +1,500 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireWildcard(require("../iterateJsdoc.cjs"));
var _jsdoccomment = require("@es-joy/jsdoccomment");
var _parseImportsExports = require("parse-imports-exports");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const extraTypes = ['null', 'undefined', 'void', 'string', 'boolean', 'object', 'function', 'symbol', 'number', 'bigint', 'NaN', 'Infinity', 'any', '*', 'never', 'unknown', 'const', 'this', 'true', 'false', 'Array', 'Object', 'RegExp', 'Date', 'Function', 'Intl'];
const globalTypes = ['globalThis', 'global', 'window', 'self'];
const typescriptGlobals = [
// https://www.typescriptlang.org/docs/handbook/utility-types.html
'Awaited', 'Partial', 'Required', 'Readonly', 'Record', 'Pick', 'Omit', 'Exclude', 'Extract', 'NonNullable', 'Parameters', 'ConstructorParameters', 'ReturnType', 'InstanceType', 'ThisParameterType', 'OmitThisParameter', 'ThisType', 'Uppercase', 'Lowercase', 'Capitalize', 'Uncapitalize'];
/**
* @param {string|false|undefined} [str]
* @returns {undefined|string|false}
*/
const stripPseudoTypes = str => {
return str && str.replace(/(?:\.|<>|\.<>|\[\])$/v, '');
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
node,
report,
settings,
sourceCode,
state,
utils
}) => {
/** @type {string[]} */
const foundTypedefValues = [];
const {
scopeManager
} = sourceCode;
// When is this ever `null`?
const globalScope = /** @type {import('eslint').Scope.Scope} */
scopeManager.globalScope;
const
/**
* @type {{
* checkUsedTypedefs: boolean
* definedTypes: string[],
* disableReporting: boolean,
* markVariablesAsUsed: boolean,
* }}
*/
{
checkUsedTypedefs = false,
definedTypes = [],
disableReporting = false,
markVariablesAsUsed = true
} = context.options[0] || {};
/** @type {(string|undefined)[]} */
let definedPreferredTypes = [];
const {
mode,
preferredTypes,
structuredTags
} = settings;
if (Object.keys(preferredTypes).length) {
definedPreferredTypes = /** @type {string[]} */Object.values(preferredTypes).map(preferredType => {
if (typeof preferredType === 'string') {
// May become an empty string but will be filtered out below
return stripPseudoTypes(preferredType);
}
if (!preferredType) {
return undefined;
}
if (typeof preferredType !== 'object') {
utils.reportSettings('Invalid `settings.jsdoc.preferredTypes`. Values must be falsy, a string, or an object.');
}
return stripPseudoTypes(preferredType.replacement);
}).filter(Boolean);
}
const allComments = sourceCode.getAllComments();
const comments = allComments.filter(comment => {
return /^\*(?!\*)/v.test(comment.value);
}).map(commentNode => {
return (0, _iterateJsdoc.parseComment)(commentNode, '');
});
const globals = allComments.filter(comment => {
return /^\s*globals/v.test(comment.value);
}).flatMap(commentNode => {
return commentNode.value.replace(/^\s*globals/v, '').trim().split(/,\s*/v);
}).concat(Object.keys(context.languageOptions.globals ?? []));
const typedefs = comments.flatMap(doc => {
return doc.tags.filter(({
tag
}) => {
return utils.isNameOrNamepathDefiningTag(tag) && !['arg', 'argument', 'param', 'prop', 'property'].includes(tag);
});
});
const typedefDeclarations = typedefs.map(tag => {
return tag.name;
});
const importTags = settings.mode === 'typescript' ? (/** @type {string[]} */comments.flatMap(doc => {
return doc.tags.filter(({
tag
}) => {
return tag === 'import';
});
}).flatMap(tag => {
const {
description,
name,
type
} = tag;
const typePart = type ? `{${type}} ` : '';
const imprt = 'import ' + (description ? `${typePart}${name} ${description}` : `${typePart}${name}`);
const importsExports = (0, _parseImportsExports.parseImportsExports)(imprt.trim());
const types = [];
const namedImports = Object.values(importsExports.namedImports || {})[0]?.[0];
if (namedImports) {
if (namedImports.default) {
types.push(namedImports.default);
}
if (namedImports.names) {
types.push(...Object.keys(namedImports.names));
}
}
const namespaceImports = Object.values(importsExports.namespaceImports || {})[0]?.[0];
if (namespaceImports) {
if (namespaceImports.namespace) {
types.push(namespaceImports.namespace);
}
if (namespaceImports.default) {
types.push(namespaceImports.default);
}
}
return types;
}).filter(Boolean)) : [];
const ancestorNodes = [];
let currentNode = node;
// No need for Program node?
while (currentNode?.parent) {
ancestorNodes.push(currentNode);
currentNode = currentNode.parent;
}
/**
* @param {import('eslint').Rule.Node} ancestorNode
* @returns {import('comment-parser').Spec[]}
*/
const getTemplateTags = function (ancestorNode) {
const commentNode = (0, _jsdoccomment.getJSDocComment)(sourceCode, ancestorNode, settings);
if (!commentNode) {
return [];
}
const jsdc = (0, _iterateJsdoc.parseComment)(commentNode, '');
return jsdc.tags.filter(tag => {
return tag.tag === 'template';
});
};
// `currentScope` may be `null` or `Program`, so in such a case,
// we look to present tags instead
const templateTags = ancestorNodes.length ? ancestorNodes.flatMap(ancestorNode => {
return getTemplateTags(ancestorNode);
}) : utils.getPresentTags(['template']);
const closureGenericTypes = templateTags.flatMap(tag => {
return utils.parseClosureTemplateTag(tag);
});
// In modules, including Node, there is a global scope at top with the
// Program scope inside
const cjsOrESMScope = globalScope.childScopes[0]?.block?.type === 'Program';
/**
* @param {import("eslint").Scope.Scope | null} scope
* @returns {Set<string>}
*/
const getValidRuntimeIdentifiers = scope => {
const result = new Set();
let scp = scope;
while (scp) {
for (const {
name
} of scp.variables) {
result.add(name);
}
scp = scp.upper;
}
return result;
};
/**
* We treat imports differently as we can't introspect their children.
* @type {string[]}
*/
const imports = [];
const allDefinedTypes = new Set(globalScope.variables.map(({
name
}) => {
return name;
})
// If the file is a module, concat the variables from the module scope.
.concat(cjsOrESMScope ? globalScope.childScopes.flatMap(({
variables
}) => {
return variables;
}).flatMap(({
identifiers,
name
}) => {
const globalItem = /** @type {import('estree').Identifier & {parent: import('@typescript-eslint/types').TSESTree.Node}} */identifiers?.[0]?.parent;
switch (globalItem?.type) {
case 'ClassDeclaration':
return [name, ...globalItem.body.body.map(item => {
const property = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */(/** @type {import('@typescript-eslint/types').TSESTree.PropertyDefinition} */item?.key)?.name;
/* c8 ignore next 3 -- Guard */
if (!property) {
return '';
}
return `${name}.${property}`;
}).filter(Boolean)];
case 'ImportDefaultSpecifier':
case 'ImportNamespaceSpecifier':
case 'ImportSpecifier':
imports.push(name);
break;
case 'TSInterfaceDeclaration':
return [name, ...globalItem.body.body.map(item => {
const property = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */(/** @type {import('@typescript-eslint/types').TSESTree.TSPropertySignature} */item?.key)?.name;
/* c8 ignore next 3 -- Guard */
if (!property) {
return '';
}
return `${name}.${property}`;
}).filter(Boolean)];
case 'VariableDeclarator':
if (/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */(/** @type {import('@typescript-eslint/types').TSESTree.CallExpression} */globalItem?.init?.callee)?.name === 'require') {
imports.push(/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */globalItem.id.name);
break;
}
// Module scope names are also defined
return [name];
}
return [name];
/* c8 ignore next */
}) : []).concat(extraTypes).concat(typedefDeclarations).concat(importTags).concat(definedTypes).concat(/** @type {string[]} */definedPreferredTypes).concat((() => {
// Other methods are not in scope, but we need them, and we grab them here
if (node?.type === 'MethodDefinition') {
return /** @type {import('estree').ClassBody} */node.parent.body.flatMap(methodOrProp => {
if (methodOrProp.type === 'MethodDefinition') {
// eslint-disable-next-line unicorn/no-lonely-if -- Pattern
if (methodOrProp.key.type === 'Identifier') {
return [methodOrProp.key.name, `${/** @type {import('estree').ClassDeclaration} */node.parent?.parent?.id?.name}.${methodOrProp.key.name}`];
}
}
if (methodOrProp.type === 'PropertyDefinition') {
// eslint-disable-next-line unicorn/no-lonely-if -- Pattern
if (methodOrProp.key.type === 'Identifier') {
return [methodOrProp.key.name, `${/** @type {import('estree').ClassDeclaration} */node.parent?.parent?.id?.name}.${methodOrProp.key.name}`];
}
}
/* c8 ignore next 2 -- Not yet built */
return '';
}).filter(Boolean);
}
return [];
})()).concat(...getValidRuntimeIdentifiers(node && (sourceCode.getScope && /* c8 ignore next 3 */
sourceCode.getScope(node) ||
// @ts-expect-error ESLint 8
context.getScope()))).concat(settings.mode === 'jsdoc' ? [] : [...(settings.mode === 'typescript' ? typescriptGlobals : []), ...closureGenericTypes]));
/**
* @typedef {{
* parsedType: import('jsdoc-type-pratt-parser').RootResult;
* tag: import('comment-parser').Spec|import('@es-joy/jsdoccomment').JsdocInlineTagNoType & {
* line?: import('../iterateJsdoc.js').Integer
* }
* }} TypeAndTagInfo
*/
/**
* @param {string} propertyName
* @returns {(tag: (import('@es-joy/jsdoccomment').JsdocInlineTagNoType & {
* name?: string,
* type?: string,
* line?: import('../iterateJsdoc.js').Integer
* })|import('comment-parser').Spec & {
* namepathOrURL?: string
* }
* ) => undefined|TypeAndTagInfo}
*/
const tagToParsedType = propertyName => {
return tag => {
try {
const potentialType = tag[(/** @type {"type"|"name"|"namepathOrURL"} */propertyName)];
return {
parsedType: mode === 'permissive' ? (0, _jsdoccomment.tryParse)(/** @type {string} */potentialType) : (0, _jsdoccomment.parse)(/** @type {string} */potentialType, mode),
tag
};
} catch {
return undefined;
}
};
};
const typeTags = utils.filterTags(({
tag
}) => {
return tag !== 'import' && utils.tagMightHaveTypePosition(tag) && (tag !== 'suppress' || settings.mode !== 'closure');
}).map(tagToParsedType('type'));
const namepathReferencingTags = utils.filterTags(({
tag
}) => {
return utils.isNamepathReferencingTag(tag);
}).map(tagToParsedType('name'));
const namepathOrUrlReferencingTags = utils.filterAllTags(({
tag
}) => {
return utils.isNamepathOrUrlReferencingTag(tag);
}).map(tagToParsedType('namepathOrURL'));
const definedNamesAndNamepaths = new Set(utils.filterTags(({
tag
}) => {
return utils.isNameOrNamepathDefiningTag(tag);
}).map(({
name
}) => {
return name;
}));
const tagsWithTypes = /** @type {TypeAndTagInfo[]} */[...typeTags, ...namepathReferencingTags, ...namepathOrUrlReferencingTags
// Remove types which failed to parse
].filter(Boolean);
for (const {
parsedType,
tag
} of tagsWithTypes) {
// eslint-disable-next-line complexity -- Refactor
(0, _jsdoccomment.traverse)(parsedType, (nde, parentNode) => {
/**
* @type {import('jsdoc-type-pratt-parser').NameResult & {
* _parent?: import('jsdoc-type-pratt-parser').NonRootResult
* }}
*/
// eslint-disable-next-line canonical/id-match -- Avoid clashes
nde._parent = parentNode;
const {
type,
value
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */nde;
let val = value;
/** @type {import('jsdoc-type-pratt-parser').NonRootResult|undefined} */
let currNode = nde;
do {
currNode =
/**
* @type {import('jsdoc-type-pratt-parser').NameResult & {
* _parent?: import('jsdoc-type-pratt-parser').NonRootResult
* }}
*/
currNode._parent;
if (
// Avoid appending for imports and globals since we don't want to
// check their properties which may or may not exist
!imports.includes(val) && !globals.includes(val) && !importTags.includes(val) && !extraTypes.includes(val) && !typedefDeclarations.includes(val) && !globalTypes.includes(val) && currNode && 'right' in currNode && currNode.right?.type === 'JsdocTypeProperty') {
val = val + '.' + currNode.right.value;
}
} while (currNode?.type === 'JsdocTypeNamePath');
if (type === 'JsdocTypeName') {
const structuredTypes = structuredTags[tag.tag]?.type;
if (!allDefinedTypes.has(val) && !definedNamesAndNamepaths.has(val) && (!Array.isArray(structuredTypes) || !structuredTypes.includes(val))) {
const parent =
/**
* @type {import('jsdoc-type-pratt-parser').RootResult & {
* _parent?: import('jsdoc-type-pratt-parser').NonRootResult
* }}
*/
nde._parent;
if (parent?.type === 'JsdocTypeTypeParameter') {
return;
}
if (parent?.type === 'JsdocTypeFunction' && /** @type {import('jsdoc-type-pratt-parser').FunctionResult} */
parent?.typeParameters?.some(typeParam => {
return value === typeParam.name.value;
})) {
return;
}
if (!disableReporting) {
report(`The type '${val}' is undefined.`, null, tag);
}
} else if (markVariablesAsUsed && !extraTypes.includes(val)) {
if (sourceCode.markVariableAsUsed) {
sourceCode.markVariableAsUsed(val);
/* c8 ignore next 4 */
} else {
// @ts-expect-error ESLint 8
context.markVariableAsUsed(val);
}
}
if (checkUsedTypedefs && typedefDeclarations.includes(val)) {
foundTypedefValues.push(val);
}
}
});
}
state.foundTypedefValues = foundTypedefValues;
}, {
// We use this method rather than checking at end of handler above because
// in that case, it is invoked too many times and would thus report errors
// too many times.
exit({
context,
state,
utils
}) {
const {
checkUsedTypedefs = false
} = context.options[0] || {};
if (!checkUsedTypedefs) {
return;
}
const allComments = context.sourceCode.getAllComments();
const comments = allComments.filter(comment => {
return /^\*(?!\*)/v.test(comment.value);
}).map(commentNode => {
return {
doc: (0, _iterateJsdoc.parseComment)(commentNode, ''),
loc: commentNode.loc
};
});
const typedefs = comments.flatMap(({
doc,
loc
}) => {
const tags = doc.tags.filter(({
tag
}) => {
return utils.isNameOrNamepathDefiningTag(tag);
});
if (!tags.length) {
return [];
}
return {
loc,
tags
};
});
for (const typedef of typedefs) {
if (!state.foundTypedefValues.includes(typedef.tags[0].name)) {
context.report({
loc: (/** @type {import('@eslint/core').SourceLocation} */typedef.loc),
message: 'This typedef was not used within the file'
});
}
}
},
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Besides some expected built-in types, prohibits any types not specified as globals or within `@typedef`.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/no-undefined-types.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
checkUsedTypedefs: {
description: 'Whether to check typedefs for use within the file',
type: 'boolean'
},
definedTypes: {
description: `This array can be populated to indicate other types which
are automatically considered as defined (in addition to globals, etc.).
Defaults to an empty array.`,
items: {
type: 'string'
},
type: 'array'
},
disableReporting: {
description: `Whether to disable reporting of errors. Defaults to
\`false\`. This may be set to \`true\` in order to take advantage of only
marking defined variables as used or checking used typedefs.`,
type: 'boolean'
},
markVariablesAsUsed: {
description: `Whether to mark variables as used for the purposes
of the \`no-unused-vars\` rule when they are not found to be undefined.
Defaults to \`true\`. May be set to \`false\` to enforce a practice of not
importing types unless used in code.`,
type: 'boolean'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=noUndefinedTypes.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=noUndefinedTypes.d.ts.map

View file

@ -0,0 +1,335 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireWildcard(require("../iterateJsdoc.cjs"));
var _jsdoccomment = require("@es-joy/jsdoccomment");
var _parseImportsExports = require("parse-imports-exports");
var _toValidIdentifier = _interopRequireDefault(require("../to-valid-identifier.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
indent,
jsdoc,
settings,
sourceCode,
utils
}) => {
const {
mode
} = settings;
const {
enableFixer = true,
exemptTypedefs = true,
outputType = 'namespaced-import'
} = context.options[0] || {};
const allComments = sourceCode.getAllComments();
const comments = allComments.filter(comment => {
return /^\*(?!\*)/v.test(comment.value);
}).map(commentNode => {
return (0, _jsdoccomment.commentParserToESTree)((0, _iterateJsdoc.parseComment)(commentNode, ''), mode === 'permissive' ? 'typescript' : mode);
});
const typedefs = comments.flatMap(doc => {
return doc.tags.filter(({
tag
}) => {
return utils.isNameOrNamepathDefiningTag(tag);
});
});
const imports = comments.flatMap(doc => {
return doc.tags.filter(({
tag
}) => {
return tag === 'import';
});
}).map(tag => {
// Causes problems with stringification otherwise
tag.delimiter = '';
return tag;
});
/**
* @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
*/
const iterateInlineImports = tag => {
const potentialType = tag.type;
let parsedType;
try {
parsedType = mode === 'permissive' ? (0, _jsdoccomment.tryParse)(/** @type {string} */potentialType) : (0, _jsdoccomment.parse)(/** @type {string} */potentialType, mode);
} catch {
return;
}
(0, _jsdoccomment.traverse)(parsedType, (nde, parentNode) => {
// @ts-expect-error Adding our own property for use below
nde.parentNode = parentNode;
});
(0, _jsdoccomment.traverse)(parsedType, nde => {
const {
element,
type
} = /** @type {import('jsdoc-type-pratt-parser').ImportResult} */nde;
if (type !== 'JsdocTypeImport') {
return;
}
let currentNode = nde;
/** @type {string[]} */
const pathSegments = [];
/** @type {import('jsdoc-type-pratt-parser').NamePathResult[]} */
const nodes = [];
/** @type {string[]} */
const extraPathSegments = [];
/** @type {(import('jsdoc-type-pratt-parser').QuoteStyle|undefined)[]} */
const quotes = [];
const propertyOrBrackets = /** @type {import('jsdoc-type-pratt-parser').NamePathResult['pathType'][]} */[];
// @ts-expect-error Referencing our own property added above
while (currentNode && currentNode.parentNode) {
// @ts-expect-error Referencing our own property added above
currentNode = currentNode.parentNode;
/* c8 ignore next 3 -- Guard */
if (currentNode.type !== 'JsdocTypeNamePath') {
break;
}
pathSegments.unshift(currentNode.right.type === 'JsdocTypeIndexedAccessIndex' ? (0, _jsdoccomment.stringify)(currentNode.right.right) : currentNode.right.value);
nodes.unshift(currentNode);
propertyOrBrackets.unshift(currentNode.pathType);
quotes.unshift(currentNode.right.type === 'JsdocTypeIndexedAccessIndex' ? undefined : currentNode.right.meta.quote);
}
/**
* @param {string} name
* @param {string[]} extrPathSegments
*/
const getFixer = (name, extrPathSegments) => {
const matchingName = (0, _toValidIdentifier.default)(name);
return () => {
/** @type {import('jsdoc-type-pratt-parser').NamePathResult|undefined} */
let node = nodes.at(0);
if (!node) {
// Not really a NamePathResult, but will be converted later anyways
node = /** @type {import('jsdoc-type-pratt-parser').NamePathResult} */
/** @type {unknown} */
nde;
}
const keys = /** @type {(keyof import('jsdoc-type-pratt-parser').NamePathResult)[]} */
Object.keys(node);
for (const key of keys) {
delete node[key];
}
if (extrPathSegments.length) {
let newNode = /** @type {import('jsdoc-type-pratt-parser').NamePathResult} */
/** @type {unknown} */
node;
while (extrPathSegments.length && newNode) {
newNode.type = 'JsdocTypeNamePath';
newNode.right = {
meta: {
quote: quotes.shift()
},
type: 'JsdocTypeProperty',
value: (/** @type {string} */extrPathSegments.shift())
};
newNode.pathType = /** @type {import('jsdoc-type-pratt-parser').NamePathResult['pathType']} */
propertyOrBrackets.shift();
// @ts-expect-error Temporary
newNode.left = {};
newNode = /** @type {import('jsdoc-type-pratt-parser').NamePathResult} */
newNode.left;
}
const nameNode = /** @type {import('jsdoc-type-pratt-parser').NameResult} */
/** @type {unknown} */
newNode;
nameNode.type = 'JsdocTypeName';
nameNode.value = matchingName;
} else {
const newNode = /** @type {import('jsdoc-type-pratt-parser').NameResult} */
/** @type {unknown} */
node;
newNode.type = 'JsdocTypeName';
newNode.value = matchingName;
}
for (const src of tag.source) {
if (src.tokens.type) {
src.tokens.type = `{${(0, _jsdoccomment.stringify)(parsedType)}}`;
break;
}
}
};
};
/** @type {string[]} */
let unusedPathSegments = [];
const findMatchingTypedef = () => {
// Don't want typedefs to find themselves
if (!exemptTypedefs) {
return undefined;
}
const pthSegments = [...pathSegments];
return typedefs.find(typedef => {
let typedefNode = typedef.parsedType;
let namepathMatch;
while (typedefNode && typedefNode.type === 'JsdocTypeNamePath') {
const pathSegment = pthSegments.shift();
if (!pathSegment) {
namepathMatch = false;
break;
}
if (typedefNode.right.type === 'JsdocTypeIndexedAccessIndex' && (0, _jsdoccomment.stringify)(typedefNode.right.right) !== pathSegment || typedefNode.right.type !== 'JsdocTypeIndexedAccessIndex' && typedefNode.right.value !== pathSegment) {
if (namepathMatch === true) {
// It stopped matching, so stop
break;
}
extraPathSegments.push(pathSegment);
namepathMatch = false;
continue;
}
namepathMatch = true;
unusedPathSegments = pthSegments;
typedefNode = typedefNode.left;
}
return namepathMatch &&
// `import('eslint')` matches
typedefNode && typedefNode.type === 'JsdocTypeImport' && typedefNode.element.value === element.value;
});
};
// Check @typedef's first as should be longest match, allowing
// for shorter abbreviations
const matchingTypedef = findMatchingTypedef();
if (matchingTypedef) {
utils.reportJSDoc('Inline `import()` found; using `@typedef`', tag, enableFixer ? getFixer(matchingTypedef.name, [...extraPathSegments, ...unusedPathSegments.slice(-1), ...unusedPathSegments.slice(0, -1)]) : null);
return;
}
const findMatchingImport = () => {
for (const imprt of imports) {
const parsedImport = (0, _parseImportsExports.parseImportsExports)((0, _jsdoccomment.estreeToString)(imprt).replace(/^\s*@/v, '').trim());
const namedImportsModuleSpecifier = Object.keys(parsedImport.namedImports || {})[0];
const namedImports = Object.values(parsedImport.namedImports || {})[0]?.[0];
const namedImportNames = (namedImports && namedImports.names && Object.keys(namedImports.names)) ?? [];
const namespaceImports = Object.values(parsedImport.namespaceImports || {})[0]?.[0];
const namespaceImportsDefault = namespaceImports && namespaceImports.default;
const namespaceImportsNamespace = namespaceImports && namespaceImports.namespace;
const namespaceImportsModuleSpecifier = Object.keys(parsedImport.namespaceImports || {})[0];
const lastPathSegment = pathSegments.at(-1);
if (namespaceImportsDefault && namespaceImportsModuleSpecifier === element.value || element.value === namedImportsModuleSpecifier && (lastPathSegment && namedImportNames.includes(lastPathSegment) || lastPathSegment === 'default') || namespaceImportsNamespace && namespaceImportsModuleSpecifier === element.value) {
return {
namedImportNames,
namedImports,
namedImportsModuleSpecifier,
namespaceImports,
namespaceImportsDefault,
namespaceImportsModuleSpecifier,
namespaceImportsNamespace
};
}
}
return undefined;
};
const matchingImport = findMatchingImport();
if (matchingImport) {
const {
namedImportNames,
namedImports,
namedImportsModuleSpecifier,
namespaceImportsNamespace
} = matchingImport;
if (!namedImportNames.length && namedImportsModuleSpecifier && namedImports.default) {
utils.reportJSDoc('Inline `import()` found; prefer `@import`', tag, enableFixer ? getFixer(namedImports.default, []) : null);
return;
}
const lastPthSegment = pathSegments.at(-1);
if (lastPthSegment && namedImportNames.includes(lastPthSegment)) {
utils.reportJSDoc('Inline `import()` found; prefer `@import`', tag, enableFixer ? getFixer(lastPthSegment, pathSegments.slice(0, -1)) : null);
return;
}
if (namespaceImportsNamespace) {
utils.reportJSDoc('Inline `import()` found; prefer `@import`', tag, enableFixer ? getFixer(namespaceImportsNamespace, [...pathSegments]) : null);
return;
}
}
if (!pathSegments.length) {
utils.reportJSDoc('Inline `import()` found; prefer `@import`', tag, enableFixer ? fixer => {
getFixer(element.value, [])();
const programNode = sourceCode.ast;
const commentNodes = sourceCode.getCommentsBefore(programNode);
return fixer.insertTextBefore(
// @ts-expect-error Ok
commentNodes[0] ?? programNode, `/** @import * as ${(0, _toValidIdentifier.default)(element.value)} from '${element.value}'; */${commentNodes[0] ? '\n' + indent : ''}`);
} : null);
return;
}
const lstPathSegment = pathSegments.at(-1);
if (lstPathSegment && lstPathSegment === 'default') {
utils.reportJSDoc('Inline `import()` found; prefer `@import`', tag, enableFixer ? fixer => {
getFixer(element.value, [])();
const programNode = sourceCode.ast;
const commentNodes = sourceCode.getCommentsBefore(programNode);
return fixer.insertTextBefore(
// @ts-expect-error Ok
commentNodes[0] ?? programNode, `/** @import ${element.value} from '${element.value}'; */${commentNodes[0] ? '\n' + indent : ''}`);
} : null);
return;
}
utils.reportJSDoc('Inline `import()` found; prefer `@import`', tag, enableFixer ? fixer => {
if (outputType === 'namespaced-import') {
getFixer(element.value, [...pathSegments])();
} else {
getFixer(/** @type {string} */pathSegments.at(-1), pathSegments.slice(0, -1))();
}
const programNode = sourceCode.ast;
const commentNodes = sourceCode.getCommentsBefore(programNode);
return fixer.insertTextBefore(
// @ts-expect-error Ok
commentNodes[0] ?? programNode, outputType === 'namespaced-import' ? `/** @import * as ${(0, _toValidIdentifier.default)(element.value)} from '${element.value}'; */${commentNodes[0] ? '\n' + indent : ''}` : `/** @import { ${(0, _toValidIdentifier.default)(/* c8 ignore next -- TS */
pathSegments.at(-1) ?? '')} } from '${element.value}'; */${commentNodes[0] ? '\n' + indent : ''}`);
} : null);
});
};
for (const tag of jsdoc.tags) {
const mightHaveTypePosition = utils.tagMightHaveTypePosition(tag.tag);
const hasTypePosition = mightHaveTypePosition === true && Boolean(tag.type);
if (hasTypePosition && (!exemptTypedefs || !utils.isNameOrNamepathDefiningTag(tag.tag))) {
iterateInlineImports(tag);
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Prefer `@import` tags to inline `import()` statements.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/prefer-import-tag.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
additionalProperties: false,
properties: {
enableFixer: {
description: 'Whether or not to enable the fixer to add `@import` tags.',
type: 'boolean'
},
exemptTypedefs: {
description: 'Whether to allow `import()` statements within `@typedef`',
type: 'boolean'
},
// We might add `typedef` and `typedef-local-only`, but also raises
// question of how deep the generated typedef should be
outputType: {
description: 'What kind of `@import` to generate when no matching `@typedef` or `@import` is found',
enum: ['named-import', 'namespaced-import'],
type: 'string'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=preferImportTag.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=preferImportTag.d.ts.map

View file

@ -0,0 +1,190 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
indent,
jsdoc,
utils
}) => {
const [defaultRequireValue = 'always', {
tags: tagMap = {}
} = {}] = context.options;
const {
source
} = jsdoc;
const always = defaultRequireValue === 'always';
const never = defaultRequireValue === 'never';
/** @type {string} */
let currentTag;
source.some(({
number,
tokens
}) => {
const {
delimiter,
description,
end,
tag
} = tokens;
/**
* @returns {void}
*/
const neverFix = () => {
tokens.delimiter = '';
tokens.postDelimiter = '';
};
/**
* @param {string} checkValue
* @returns {boolean}
*/
const checkNever = checkValue => {
if (delimiter && delimiter !== '/**' && (never && !tagMap.always?.includes(checkValue) || tagMap.never?.includes(checkValue))) {
utils.reportJSDoc('Expected JSDoc line to have no prefix.', {
column: 0,
line: number
}, neverFix);
return true;
}
return false;
};
/**
* @returns {void}
*/
const alwaysFix = () => {
if (!tokens.start) {
tokens.start = indent + ' ';
}
tokens.delimiter = '*';
tokens.postDelimiter = tag || description ? ' ' : '';
};
/**
* @param {string} checkValue
* @returns {boolean}
*/
const checkAlways = checkValue => {
if (!delimiter && (always && !tagMap.never?.includes(checkValue) || tagMap.always?.includes(checkValue))) {
utils.reportJSDoc('Expected JSDoc line to have the prefix.', {
column: 0,
line: number
}, alwaysFix);
return true;
}
return false;
};
if (tag) {
// Remove at sign
currentTag = tag.slice(1);
}
if (
// If this is the end but has a tag, the delimiter will also be
// populated and will be safely ignored later
end && !tag) {
return false;
}
if (!currentTag) {
if (tagMap.any?.includes('*description')) {
return false;
}
if (checkNever('*description')) {
return true;
}
if (checkAlways('*description')) {
return true;
}
return false;
}
if (tagMap.any?.includes(currentTag)) {
return false;
}
if (checkNever(currentTag)) {
return true;
}
if (checkAlways(currentTag)) {
return true;
}
return false;
});
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Requires that each JSDoc line starts with an `*`.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-asterisk-prefix.md#repos-sticky-header'
},
fixable: 'code',
schema: [{
description: `If it is \`"always"\` then a problem is raised when there is no asterisk
prefix on a given JSDoc line. If it is \`"never"\` then a problem is raised
when there is an asterisk present.
The default value is \`"always"\`. You may also set the default to \`"any"\`
and use the \`tags\` option to apply to specific tags only.`,
enum: ['always', 'never', 'any'],
type: 'string'
}, {
additionalProperties: false,
properties: {
tags: {
additionalProperties: false,
description: `If you want different values to apply to specific tags, you may use
the \`tags\` option object. The keys are \`always\`, \`never\`, or \`any\` and
the values are arrays of tag names or the special value \`*description\`
which applies to the main JSDoc block description.
\`\`\`js
{
'jsdoc/require-asterisk-prefix': ['error', 'always', {
tags: {
always: ['*description'],
any: ['example', 'license'],
never: ['copyright']
}
}]
}
\`\`\``,
properties: {
always: {
description: `If it is \`"always"\` then a problem is raised when there is no asterisk
prefix on a given JSDoc line.`,
items: {
type: 'string'
},
type: 'array'
},
any: {
description: 'No problem is raised regardless of asterisk presence or non-presence.',
items: {
type: 'string'
},
type: 'array'
},
never: {
description: `If it is \`"never"\` then a problem is raised
when there is an asterisk present.`,
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}
},
type: 'object'
}],
type: 'layout'
}
});
module.exports = exports.default;
//# sourceMappingURL=requireAsteriskPrefix.cjs.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,3 @@
declare const _default: import("eslint").Rule.RuleModule;
export default _default;
//# sourceMappingURL=requireAsteriskPrefix.d.ts.map

View file

@ -0,0 +1,164 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* @param {string} description
* @returns {import('../iterateJsdoc.js').Integer}
*/
const checkDescription = description => {
return description.trim().split('\n').filter(Boolean).length;
};
var _default = exports.default = (0, _iterateJsdoc.default)(({
context,
jsdoc,
report,
utils
}) => {
if (utils.avoidDocs()) {
return;
}
const {
descriptionStyle = 'body'
} = context.options[0] || {};
let targetTagName = utils.getPreferredTagName({
// We skip reporting except when `@description` is essential to the rule,
// so user can block the tag and still meaningfully use this rule
// even if the tag is present (and `check-tag-names` is the one to
// normally report the fact that it is blocked but present)
skipReportingBlockedTag: descriptionStyle !== 'tag',
tagName: 'description'
});
if (!targetTagName) {
return;
}
const isBlocked = typeof targetTagName === 'object' && 'blocked' in targetTagName && targetTagName.blocked;
if (isBlocked) {
targetTagName = /** @type {{blocked: true; tagName: string;}} */targetTagName.tagName;
}
if (descriptionStyle !== 'tag') {
const {
description
} = utils.getDescription();
if (checkDescription(description || '')) {
return;
}
if (descriptionStyle === 'body') {
const descTags = utils.getPresentTags(['desc', 'description']);
if (descTags.length) {
const [{
tag: tagName
}] = descTags;
report(`Remove the @${tagName} tag to leave a plain block description or add additional description text above the @${tagName} line.`);
} else {
report('Missing JSDoc block description.');
}
return;
}
}
const functionExamples = isBlocked ? [] : jsdoc.tags.filter(({
tag
}) => {
return tag === targetTagName;
});
if (!functionExamples.length) {
report(descriptionStyle === 'any' ? `Missing JSDoc block description or @${targetTagName} declaration.` : `Missing JSDoc @${targetTagName} declaration.`);
return;
}
for (const example of functionExamples) {
if (!checkDescription(`${example.name} ${utils.getTagDescription(example)}`)) {
report(`Missing JSDoc @${targetTagName} description.`, null, example);
}
}
}, {
contextDefaults: true,
meta: {
docs: {
description: 'Requires that all functions (and potentially other contexts) have a description.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-description.md#repos-sticky-header'
},
schema: [{
additionalProperties: false,
properties: {
checkConstructors: {
default: true,
description: `A value indicating whether \`constructor\`s should be
checked. Defaults to \`true\`.`,
type: 'boolean'
},
checkGetters: {
default: true,
description: `A value indicating whether getters should be checked.
Defaults to \`true\`.`,
type: 'boolean'
},
checkSetters: {
default: true,
description: `A value indicating whether setters should be checked.
Defaults to \`true\`.`,
type: 'boolean'
},
contexts: {
description: `Set to an array of strings representing the AST context
where you wish the rule to be applied (e.g., \`ClassDeclaration\` for ES6
classes).
\`context\` defaults to \`any\` and \`comment\` defaults to no specific comment context.
Overrides the default contexts (\`ArrowFunctionExpression\`,
\`FunctionDeclaration\`, \`FunctionExpression\`). Set to \`"any"\` if you want
the rule to apply to any JSDoc block throughout your files (as is necessary
for finding function blocks not attached to a function declaration or
expression, i.e., \`@callback\` or \`@function\` (or its aliases \`@func\` or
\`@method\`) (including those associated with an \`@interface\`).
See the ["AST and Selectors"](../#advanced-ast-and-selectors)
section of our Advanced docs for more on the expected format.`,
items: {
anyOf: [{
type: 'string'
}, {
additionalProperties: false,
properties: {
comment: {
type: 'string'
},
context: {
type: 'string'
}
},
type: 'object'
}]
},
type: 'array'
},
descriptionStyle: {
description: `Whether to accept implicit descriptions (\`"body"\`) or
\`@description\` tags (\`"tag"\`) as satisfying the rule. Set to \`"any"\` to
accept either style. Defaults to \`"body"\`.`,
enum: ['body', 'tag', 'any'],
type: 'string'
},
exemptedBy: {
description: `Array of tags (e.g., \`['type']\`) whose presence on the
document block avoids the need for a \`@description\`. Defaults to an
array with \`inheritdoc\`. If you set this array, it will overwrite the
default, so be sure to add back \`inheritdoc\` if you wish its presence
to cause exemption of the rule.`,
items: {
type: 'string'
},
type: 'array'
}
},
type: 'object'
}],
type: 'suggestion'
}
});
module.exports = exports.default;
//# sourceMappingURL=requireDescription.cjs.map

Some files were not shown because too many files have changed in this diff Show more