Address new TypeScript warnings in core files, improve type definitions.
Some checks are pending
Checkers / linkcheck (push) Waiting to run
Checkers / spellcheck (push) Waiting to run
CI / build (20, macos-latest) (push) Waiting to run
CI / build (20, ubuntu-latest) (push) Waiting to run
CI / build (20, windows-latest) (push) Waiting to run
CI / build (22, macos-latest) (push) Waiting to run
CI / build (22, ubuntu-latest) (push) Waiting to run
CI / build (22, windows-latest) (push) Waiting to run
CI / build (24, macos-latest) (push) Waiting to run
CI / build (24, ubuntu-latest) (push) Waiting to run
CI / build (24, windows-latest) (push) Waiting to run
CI / pnpm (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
TestRepos / build (latest, ubuntu-latest) (push) Waiting to run
UpdateTestRepos / update (push) Waiting to run

This commit is contained in:
David Anson 2025-10-11 16:36:47 -07:00
parent bd02390014
commit 7beb9fc9d0
32 changed files with 354 additions and 170 deletions

View file

@ -24,15 +24,17 @@ function validateRuleList(ruleList, synchronous) {
// No need to validate if only using built-in rules
return result;
}
/** @type {Object.<string, boolean>} */
const allIds = {};
for (const [ index, rule ] of ruleList.entries()) {
const customIndex = index - rules.length;
// eslint-disable-next-line jsdoc/require-jsdoc
function newError(property, value) {
// eslint-disable-next-line jsdoc/reject-any-type, jsdoc/require-jsdoc
function newError(/** @type {string} */ property, /** @type {any} */ value) {
return new Error(
`Property '${property}' of custom rule at index ${customIndex} is incorrect: '${value}'.`);
}
for (const property of [ "names", "tags" ]) {
// @ts-ignore
const value = rule[property];
if (!result &&
(!value || !Array.isArray(value) || (value.length === 0) ||
@ -45,6 +47,7 @@ function validateRuleList(ruleList, synchronous) {
[ "function", "function" ]
]) {
const property = propertyInfo[0];
// @ts-ignore
const value = rule[property];
if (!result && (!value || (typeof value !== propertyInfo[1]))) {
result = newError(property, value);
@ -113,10 +116,12 @@ function newResults(ruleList) {
*
* @param {boolean} useAlias True if rule alias should be used instead of name.
* @returns {string} String representation of the instance.
* @this {LintResults}
*/
function toString(useAlias) {
// eslint-disable-next-line consistent-this, no-invalid-this, unicorn/no-this-assignment
// eslint-disable-next-line consistent-this, unicorn/no-this-assignment
const lintResults = this;
/** @type {Object.<string, Rule> | null} */
let ruleNameToRule = null;
const results = [];
const keys = Object.keys(lintResults);
@ -127,6 +132,7 @@ function newResults(ruleList) {
for (const result of fileResults) {
const ruleMoniker = result.ruleNames ?
result.ruleNames.join("/") :
// @ts-ignore
(result.ruleName + "/" + result.ruleAlias);
results.push(
file + ": " +
@ -173,14 +179,23 @@ function newResults(ruleList) {
return lintResults;
}
/**
* Result object for removeFrontMatter.
*
* @typedef {Object} RemoveFrontMatterResult
* @property {string} content Markdown content.
* @property {string[]} frontMatterLines Front matter lines.
*/
/**
* Remove front matter (if present at beginning of content).
*
* @param {string} content Markdown content.
* @param {RegExp | null} frontMatter Regular expression to match front matter.
* @returns {Object} Trimmed content and front matter lines.
* @returns {RemoveFrontMatterResult} Trimmed content and front matter lines.
*/
function removeFrontMatter(content, frontMatter) {
/** @type {string[]} */
let frontMatterLines = [];
if (frontMatter) {
const frontMatterMatch = content.match(frontMatter);
@ -207,6 +222,7 @@ function removeFrontMatter(content, frontMatter) {
* @returns {Object.<string, string[]>} Map of alias to rule name.
*/
function mapAliasToRuleNames(ruleList) {
/** @type {Object.<string, string[]>} */
const aliasToRuleNames = {};
// const tagToRuleNames = {};
for (const rule of ruleList) {
@ -357,7 +373,7 @@ function getEnabledRulesPerLineNumber(
const enabledRulesPerLineNumber = new Array(1 + frontMatterLines.length);
// Helper functions
// eslint-disable-next-line jsdoc/require-jsdoc
function handleInlineConfig(input, forEachMatch, forEachLine) {
function handleInlineConfig(/** @type {string[]} */ input, /** @type {(act: string, par: string, ind: number) => void} */ forEachMatch, /** @type {(() => void)|undefined} */ forEachLine = undefined) {
for (const [ lineIndex, line ] of input.entries()) {
if (!noInlineConfig) {
let match = null;
@ -378,7 +394,7 @@ function getEnabledRulesPerLineNumber(
}
}
// eslint-disable-next-line jsdoc/require-jsdoc
function configureFile(action, parameter) {
function configureFile(/** @type {string} */ action, /** @type {string} */ parameter) {
if (action === "CONFIGURE-FILE") {
const { "config": parsed } = parseConfiguration(
"CONFIGURE-FILE", parameter, configParsers
@ -392,7 +408,7 @@ function getEnabledRulesPerLineNumber(
}
}
// eslint-disable-next-line jsdoc/require-jsdoc
function applyEnableDisable(action, parameter, state) {
function applyEnableDisable(/** @type {string} */ action, /** @type {string} */ parameter, /** @type {Map<string, boolean>} */ state) {
state = new Map(state);
const enabled = (action.startsWith("ENABLE"));
const trimmed = parameter && parameter.trim();
@ -406,13 +422,13 @@ function getEnabledRulesPerLineNumber(
return state;
}
// eslint-disable-next-line jsdoc/require-jsdoc
function enableDisableFile(action, parameter) {
function enableDisableFile(/** @type {string} */ action, /** @type {string} */ parameter) {
if ((action === "ENABLE-FILE") || (action === "DISABLE-FILE")) {
enabledRules = applyEnableDisable(action, parameter, enabledRules);
}
}
// eslint-disable-next-line jsdoc/require-jsdoc
function captureRestoreEnableDisable(action, parameter) {
function captureRestoreEnableDisable(/** @type {string} */ action, /** @type {string} */ parameter) {
if (action === "CAPTURE") {
capturedRules = enabledRules;
} else if (action === "RESTORE") {
@ -426,7 +442,7 @@ function getEnabledRulesPerLineNumber(
enabledRulesPerLineNumber.push(enabledRules);
}
// eslint-disable-next-line jsdoc/require-jsdoc
function disableLineNextLine(action, parameter, lineNumber) {
function disableLineNextLine(/** @type {string} */ action, /** @type {string} */ parameter, /** @type {number} */ lineNumber) {
const disableLine = (action === "DISABLE-LINE");
const disableNextLine = (action === "DISABLE-NEXT-LINE");
if (disableLine || disableNextLine) {
@ -496,7 +512,8 @@ function lintContent(
synchronous,
callback) {
// Provide a consistent error-reporting callback
const callbackError = (error) => callback(error instanceof Error ? error : new Error(error));
// eslint-disable-next-line jsdoc/reject-any-type
const callbackError = (/** @type {any} */ error) => callback(error instanceof Error ? error : new Error(error));
// Remove UTF-8 byte order marker (if present)
content = content.replace(/^\uFEFF/, "");
// Remove front matter
@ -531,7 +548,7 @@ function lintContent(
// Parse content into lines and get markdown-it tokens
const lines = content.split(helpers.newLineRe);
// Function to run after fetching markdown-it tokens (when needed)
const lintContentInternal = (markdownitTokens) => {
const lintContentInternal = (/** @type {MarkdownItToken[]} */ markdownitTokens) => {
// Create (frozen) parameters for rules
/** @type {MarkdownParsers} */
// @ts-ignore
@ -585,15 +602,17 @@ function lintContent(
...paramsBase,
...tokens,
parsers,
/** @type {RuleConfiguration} */
// @ts-ignore
"config": effectiveConfig[ruleName]
});
// eslint-disable-next-line jsdoc/require-jsdoc
function throwError(property) {
function throwError(/** @type {string} */ property) {
throw new Error(
`Value of '${property}' passed to onError by '${ruleName}' is incorrect for '${name}'.`);
}
// eslint-disable-next-line jsdoc/require-jsdoc
function onError(errorInfo) {
function onError(/** @type {RuleOnErrorInfo} */ errorInfo) {
if (!errorInfo ||
!helpers.isNumber(errorInfo.lineNumber) ||
(errorInfo.lineNumber < 1) ||
@ -683,6 +702,7 @@ function lintContent(
});
}
// Call (possibly external) rule function to report errors
// @ts-ignore
const catchCallsOnError = (error) => onError({
"lineNumber": 1,
"detail": `This rule threw an exception: ${error.message || error}`
@ -772,7 +792,7 @@ function lintContent(
* @param {boolean} handleRuleFailures Whether to handle exceptions in rules.
* @param {boolean} noInlineConfig Whether to allow inline configuration.
* @param {number} resultVersion Version of the LintResults object to return.
* @param {Object} fs File system implementation.
* @param {FsLike} fs File system implementation.
* @param {boolean} synchronous Whether to execute synchronously.
* @param {LintContentCallback} callback Callback (err, result) function.
* @returns {void}
@ -792,7 +812,7 @@ function lintFile(
synchronous,
callback) {
// eslint-disable-next-line jsdoc/require-jsdoc
function lintContentWrapper(err, content) {
function lintContentWrapper(/** @type {Error | null} */ err, /** @type {string} */ content) {
if (err) {
return callback(err);
}
@ -832,6 +852,8 @@ function lintInput(options, synchronous, callback) {
// Normalize inputs
options = options || {};
callback = callback || function noop() {};
/** @type {Rule[]} */
// @ts-ignore
const customRuleList =
[ options.customRules || [] ]
.flat()
@ -851,6 +873,7 @@ function lintInput(options, synchronous, callback) {
callback(ruleErr);
return;
}
/** @type {string[]} */
let files = [];
if (Array.isArray(options.files)) {
files = [ ...options.files ];
@ -866,11 +889,14 @@ function lintInput(options, synchronous, callback) {
options.frontMatter;
const handleRuleFailures = !!options.handleRuleFailures;
const noInlineConfig = !!options.noInlineConfig;
// @ts-ignore
// eslint-disable-next-line dot-notation
const resultVersion = (options["resultVersion"] === undefined) ? 3 : options["resultVersion"];
const markdownItFactory =
options.markdownItFactory ||
(() => { throw new Error("The option 'markdownItFactory' was required (due to the option 'customRules' including a rule requiring the 'markdown-it' parser), but 'markdownItFactory' was not set."); });
/** @type {FsLike} */
// @ts-ignore
const fs = options.fs || nodeFs;
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
const results = newResults(ruleList);
@ -878,14 +904,16 @@ function lintInput(options, synchronous, callback) {
let concurrency = 0;
// eslint-disable-next-line jsdoc/require-jsdoc
function lintWorker() {
let currentItem = null;
/** @type {string | undefined} */
let currentItem = undefined;
// eslint-disable-next-line jsdoc/require-jsdoc
function lintWorkerCallback(err, result) {
function lintWorkerCallback(/** @type {Error | null} */ err, /** @type {LintError[] | undefined} */ result) {
concurrency--;
if (err) {
done = true;
return callback(err);
}
// @ts-ignore
results[currentItem] = result;
if (!synchronous) {
lintWorker();
@ -894,10 +922,9 @@ function lintInput(options, synchronous, callback) {
}
if (done) {
// Abort for error or nothing left to do
} else if (files.length > 0) {
} else if ((currentItem = files.shift())) {
// Lint next file
concurrency++;
currentItem = files.shift();
lintFile(
ruleList,
aliasToRuleNames,
@ -1013,15 +1040,24 @@ export function lintSync(options) {
return results;
}
/**
* Node fs instance (or compatible object).
*
* @typedef FsLike
* @property {(path: string, callback: (err: Error) => void) => void} access access method.
* @property {(path: string) => void} accessSync accessSync method.
* @property {(path: string, encoding: string, callback: (err: Error, data: string) => void) => void} readFile readFile method.
* @property {(path: string, encoding: string) => string} readFileSync readFileSync method.
*/
/**
* Resolve referenced "extends" path in a configuration file
* using path.resolve() with require.resolve() as a fallback.
*
* @param {string} configFile Configuration file name.
* @param {string} referenceId Referenced identifier to resolve.
* @param {Object} fs File system implementation.
* @param {ResolveConfigExtendsCallback} callback Callback (err, result)
* function.
* @param {FsLike} fs File system implementation.
* @param {ResolveConfigExtendsCallback} callback Callback (err, result) function.
* @returns {void}
*/
function resolveConfigExtends(configFile, referenceId, fs, callback) {
@ -1049,7 +1085,7 @@ function resolveConfigExtends(configFile, referenceId, fs, callback) {
*
* @param {string} configFile Configuration file name.
* @param {string} referenceId Referenced identifier to resolve.
* @param {Object} fs File system implementation.
* @param {FsLike} fs File system implementation.
* @returns {string} Resolved path to file.
*/
function resolveConfigExtendsSync(configFile, referenceId, fs) {
@ -1074,9 +1110,8 @@ function resolveConfigExtendsSync(configFile, referenceId, fs) {
*
* @param {Configuration} config Configuration object.
* @param {string} file Configuration file name.
* @param {ConfigurationParser[] | undefined} parsers Parsing
* function(s).
* @param {Object} fs File system implementation.
* @param {ConfigurationParser[] | undefined} parsers Parsing function(s).
* @param {FsLike} fs File system implementation.
* @param {ReadConfigCallback} callback Callback (err, result) function.
* @returns {void}
*/
@ -1116,7 +1151,7 @@ function extendConfig(config, file, parsers, fs, callback) {
* @param {Configuration} config Configuration object.
* @param {string} file Configuration file name.
* @param {ConfigurationParser[] | undefined} parsers Parsing function(s).
* @param {Object} fs File system implementation.
* @param {FsLike} fs File system implementation.
* @returns {Promise<Configuration>} Configuration object.
*/
export function extendConfigPromise(config, file, parsers, fs) {
@ -1135,16 +1170,17 @@ export function extendConfigPromise(config, file, parsers, fs) {
* Read specified configuration file.
*
* @param {string} file Configuration file name.
* @param {ConfigurationParser[] | ReadConfigCallback} [parsers] Parsing
* function(s).
* @param {Object} [fs] File system implementation.
* @param {ConfigurationParser[] | ReadConfigCallback} [parsers] Parsing function(s).
* @param {FsLike | ReadConfigCallback} [fs] File system implementation.
* @param {ReadConfigCallback} [callback] Callback (err, result) function.
* @returns {void}
*/
export function readConfigAsync(file, parsers, fs, callback) {
if (!callback) {
if (fs) {
// @ts-ignore
callback = fs;
// @ts-ignore
fs = null;
} else {
// @ts-ignore
@ -1153,12 +1189,12 @@ export function readConfigAsync(file, parsers, fs, callback) {
parsers = null;
}
}
if (!fs) {
fs = nodeFs;
}
/** @type {FsLike} */
// @ts-ignore
const fsLike = fs || nodeFs;
// Read file
file = helpers.expandTildePath(file, os);
fs.readFile(file, "utf8", (err, content) => {
fsLike.readFile(file, "utf8", (err, content) => {
if (err) {
// @ts-ignore
return callback(err);
@ -1172,7 +1208,7 @@ export function readConfigAsync(file, parsers, fs, callback) {
}
// Extend configuration
// @ts-ignore
return extendConfig(config, file, parsers, fs, callback);
return extendConfig(config, file, parsers, fsLike, callback);
});
}
@ -1181,7 +1217,7 @@ export function readConfigAsync(file, parsers, fs, callback) {
*
* @param {string} file Configuration file name.
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
* @param {Object} [fs] File system implementation.
* @param {FsLike} [fs] File system implementation.
* @returns {Promise<Configuration>} Configuration object.
*/
export function readConfigPromise(file, parsers, fs) {
@ -1201,16 +1237,16 @@ export function readConfigPromise(file, parsers, fs) {
*
* @param {string} file Configuration file name.
* @param {ConfigurationParser[]} [parsers] Parsing function(s).
* @param {Object} [fs] File system implementation.
* @param {FsLike} [fs] File system implementation.
* @returns {Configuration} Configuration object.
*/
export function readConfigSync(file, parsers, fs) {
if (!fs) {
fs = nodeFs;
}
/** @type {FsLike} */
// @ts-ignore
const fsLike = fs || nodeFs;
// Read file
file = helpers.expandTildePath(file, os);
const content = fs.readFileSync(file, "utf8");
const content = fsLike.readFileSync(file, "utf8");
// Try to parse file
const { config, message } = parseConfiguration(file, content, parsers);
if (!config) {
@ -1224,7 +1260,7 @@ export function readConfigSync(file, parsers, fs) {
const resolvedExtends = resolveConfigExtendsSync(
file,
helpers.expandTildePath(configExtends, os),
fs
fsLike
);
return {
...readConfigSync(resolvedExtends, parsers, fs),
@ -1512,7 +1548,7 @@ export function getVersion() {
* @property {Rule[] | Rule} [customRules] Custom rules.
* @property {string[] | string} [files] Files to lint.
* @property {RegExp | null} [frontMatter] Front matter pattern.
* @property {Object} [fs] File system implementation.
* @property {FsLike} [fs] File system implementation.
* @property {boolean} [handleRuleFailures] True to catch exceptions.
* @property {MarkdownItFactory} [markdownItFactory] Function to create a markdown-it parser.
* @property {boolean} [noInlineConfig] True to ignore HTML directives.
@ -1522,7 +1558,7 @@ export function getVersion() {
/**
* A markdown-it plugin.
*
* @typedef {Array} Plugin
* @typedef {Object[]} Plugin
*/
/**
@ -1538,11 +1574,11 @@ export function getVersion() {
* @property {number} lineNumber Line number (1-based).
* @property {string[]} ruleNames Rule name(s).
* @property {string} ruleDescription Rule description.
* @property {string} ruleInformation Link to more information.
* @property {string} errorDetail Detail about the error.
* @property {string} errorContext Context for the error.
* @property {number[]|null} errorRange Column number (1-based) and length.
* @property {FixInfo|null} fixInfo Fix information.
* @property {string | null} ruleInformation Link to more information.
* @property {string | null} errorDetail Detail about the error.
* @property {string | null} errorContext Context for the error.
* @property {number[] | null} errorRange Column number (1-based) and length.
* @property {FixInfo | null} fixInfo Fix information.
* @property {"error" | "warning"} severity Severity of the error.
*/