diff --git a/lib/exports.d.mts b/lib/exports.d.mts index 7d9639af..afac5561 100644 --- a/lib/exports.d.mts +++ b/lib/exports.d.mts @@ -26,5 +26,4 @@ export type RuleOnErrorFixInfo = import("./markdownlint.mjs").RuleOnErrorFixInfo export type RuleOnErrorFixInfoNormalized = import("./markdownlint.mjs").RuleOnErrorFixInfoNormalized; export type RuleOnErrorInfo = import("./markdownlint.mjs").RuleOnErrorInfo; export type RuleParams = import("./markdownlint.mjs").RuleParams; -export type ToStringCallback = import("./markdownlint.mjs").ToStringCallback; -export { applyFix, applyFixes, getVersion } from "./markdownlint.mjs"; +export { applyFix, applyFixes, formatLintError, getVersion, parseLintErrorString } from "./markdownlint.mjs"; diff --git a/lib/exports.mjs b/lib/exports.mjs index dc0d137d..d96ab78b 100644 --- a/lib/exports.mjs +++ b/lib/exports.mjs @@ -1,6 +1,6 @@ // @ts-check -export { applyFix, applyFixes, getVersion } from "./markdownlint.mjs"; +export { applyFix, applyFixes, formatLintError, getVersion, parseLintErrorString } from "./markdownlint.mjs"; export { resolveModule } from "./resolve-module.cjs"; /** @typedef {import("./markdownlint.mjs").Configuration} Configuration */ @@ -30,4 +30,3 @@ export { resolveModule } from "./resolve-module.cjs"; /** @typedef {import("./markdownlint.mjs").RuleOnErrorFixInfoNormalized} RuleOnErrorFixInfoNormalized */ /** @typedef {import("./markdownlint.mjs").RuleOnErrorInfo} RuleOnErrorInfo */ /** @typedef {import("./markdownlint.mjs").RuleParams} RuleParams */ -/** @typedef {import("./markdownlint.mjs").ToStringCallback} ToStringCallback */ diff --git a/lib/markdownlint.d.mts b/lib/markdownlint.d.mts index 1117fd0d..8a6df4d6 100644 --- a/lib/markdownlint.d.mts +++ b/lib/markdownlint.d.mts @@ -76,6 +76,21 @@ export function applyFix(line: string, fixInfo: RuleOnErrorFixInfo, lineEnding?: * @returns {string} Fixed content. */ export function applyFixes(input: string, errors: RuleOnErrorInfo[]): string; +/** + * TBD + * + * @param {string} source Source file name or identifier. + * @param {LintError} lintError Lint error. + * @returns {string} Lint error string. + */ +export function formatLintError(source: string, lintError: LintError): string; +/** + * TBD + * + * @param {string} lintErrorString Lint error string. + * @returns {Object|null} Lint error (if valid). + */ +export function parseLintErrorString(lintErrorString: string): any | null; /** * Gets the (semantic) version of the library. * @@ -442,10 +457,6 @@ export type Options = { * A markdown-it plugin. */ export type Plugin = any[]; -/** - * Function to pretty-print lint results. - */ -export type ToStringCallback = () => string; /** * Lint results. */ @@ -483,11 +494,11 @@ export type LintError = { /** * Column number (1-based) and length. */ - errorRange: number[]; + errorRange: number[] | null; /** * Fix information. */ - fixInfo?: FixInfo; + fixInfo: FixInfo | null; }; /** * Fix information. diff --git a/lib/markdownlint.mjs b/lib/markdownlint.mjs index 3bbad333..d3cee171 100644 --- a/lib/markdownlint.mjs +++ b/lib/markdownlint.mjs @@ -522,6 +522,7 @@ function lintContent( "config": null }); // Function to run for each rule + /** @type {LintError[]} */ const results = []; /** * @param {Rule} rule Rule. @@ -1303,6 +1304,44 @@ export function applyFixes(input, errors) { return lines.filter((line) => line !== null).join(lineEnding); } +/** + * TBD + * + * @param {string} source Source file name or identifier. + * @param {LintError} lintError Lint error. + * @returns {string} Lint error string. + */ +export function formatLintError(source, lintError) { + const { lineNumber, ruleNames, ruleDescription, errorDetail, errorContext, errorRange } = lintError; + const ruleName = ruleNames.join("/"); + const description = ruleDescription + + (errorDetail ? ` [${errorDetail}]` : "") + + (errorContext ? ` [Context: "${errorContext}"]` : ""); + const column = (errorRange && errorRange[0]) || 0; + const columnText = column ? `:${column}` : ""; + return `${source}:${lineNumber}${columnText} ${ruleName} ${description}`; +} + +/** + * TBD + * + * @param {string} lintErrorString Lint error string. + * @returns {Object|null} Lint error (if valid). + */ +export function parseLintErrorString(lintErrorString) { + const matchRe = /^([^:]+):\s*(\d+)(?::(\d+))?:?\s(\S+)\s(.+)$/; + const match = matchRe.exec(lintErrorString); + return match ? + { + "source": match[1], + "line": match[2], + "column": match[3], + "rule": match[4], + "message": match[5] + } : + null; +} + /** * Gets the (semantic) version of the library. * @@ -1492,18 +1531,10 @@ export function getVersion() { * @typedef {Array} Plugin */ -/** - * Function to pretty-print lint results. - * - * @callback ToStringCallback - * @returns {string} Pretty-printed results. - */ - /** * Lint results. * * @typedef {Object.} LintResults - * @property {ToStringCallback} toString String representation. */ /** @@ -1516,8 +1547,8 @@ export function getVersion() { * @property {string} ruleInformation Link to more information. * @property {string} errorDetail Detail about the error. * @property {string} errorContext Context for the error. - * @property {number[]} errorRange Column number (1-based) and length. - * @property {FixInfo} [fixInfo] Fix information. + * @property {number[]|null} errorRange Column number (1-based) and length. + * @property {FixInfo|null} fixInfo Fix information. */ /** diff --git a/test/markdownlint-test.mjs b/test/markdownlint-test.mjs index 1d7879d4..ae62da8e 100644 --- a/test/markdownlint-test.mjs +++ b/test/markdownlint-test.mjs @@ -13,7 +13,7 @@ import pluginInline from "markdown-it-for-inline"; import pluginSub from "markdown-it-sub"; import pluginSup from "markdown-it-sup"; import test from "ava"; -import { getVersion } from "markdownlint"; +import { formatLintError, getVersion, parseLintErrorString } from "markdownlint"; import { lint as lintAsync } from "markdownlint/async"; import { lint as lintPromise } from "markdownlint/promise"; import { lint as lintSync } from "markdownlint/sync"; @@ -1392,27 +1392,30 @@ test("getVersion", (t) => { t.is(actual, expected, "Version string not correct."); }); -const matcherRe = /^(?[^:]+):\s*(?\d+)(?::(?\d+))?:?\s(?\S+)\s(?.+)$/; - -test("problemMatcher", async(t) => { - t.plan(2); - const content = "# Heading\nText `code ` text "; +test("formatLintError-parseLintErrorString", async(t) => { + t.plan(29); + t.is(null, parseLintErrorString("")); + const content = "# Heading\nText `code ` text \n\n"; const options = { "strings": { "relative/path/file name.md": content } }; const results = await lintPromise(options); - const getMatches = (input) => input.split("\n").map((line) => matcherRe.exec(line)?.groups); - t.snapshot(getMatches(results.toString())); - // eslint-disable-next-line camelcase - const cli_0_45_0__cli2_0_18_1 = - `relative/path/file name.md:1:3 MD019/no-multiple-space-atx Multiple spaces after hash on atx style heading [Context: "# Heading"] -relative/path/file name.md:1 MD022/blanks-around-headings Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "# Heading"] -relative/path/file name.md:2:18 MD009/no-trailing-spaces Trailing spaces [Expected: 0 or 2; Actual: 1] -relative/path/file name.md:2:11 MD038/no-space-in-code Spaces inside code span elements [Context: "\`code \`"] -relative/path/file name.md:2:18 MD047/single-trailing-newline Files should end with a single newline character`; - t.snapshot(getMatches(cli_0_45_0__cli2_0_18_1)); + for (const source of Object.keys(results)) { + for (const lintError of results[source]) { + const formatted = formatLintError(source, lintError); + const parsed = parseLintErrorString(formatted); + t.is(parsed.source, source); + t.is(parsed.line, lintError.lineNumber.toString()); + t.is(Boolean(parsed.column), Boolean(lintError.errorRange)); + if (lintError.errorRange) { + t.is(parsed.column, lintError.errorRange[0].toString()); + } + t.is(parsed.rule, lintError.ruleNames.join("/")); + t.is(parsed.message.replaceAll(/ \[[^\]]+\]/g, ""), lintError.ruleDescription); + } + } }); test("constants", (t) => { diff --git a/test/snapshots/markdownlint-test-exports.mjs.md b/test/snapshots/markdownlint-test-exports.mjs.md index f03e1f08..5745084e 100644 --- a/test/snapshots/markdownlint-test-exports.mjs.md +++ b/test/snapshots/markdownlint-test-exports.mjs.md @@ -12,7 +12,9 @@ Generated by [AVA](https://avajs.dev). markdownlint: [ 'applyFix', 'applyFixes', + 'formatLintError', 'getVersion', + 'parseLintErrorString', 'resolveModule', ], 'markdownlint/async': [ diff --git a/test/snapshots/markdownlint-test-exports.mjs.snap b/test/snapshots/markdownlint-test-exports.mjs.snap index bf686f39..26faffb6 100644 Binary files a/test/snapshots/markdownlint-test-exports.mjs.snap and b/test/snapshots/markdownlint-test-exports.mjs.snap differ