Deprecate LintResults.toString() (at edit time; continue to support at run time), provide helper function formatLintResults, remove outdated grunt/gulp samples, improve typing.
Some checks failed
Checkers / linkcheck (push) Has been cancelled
Checkers / spellcheck (push) Has been cancelled
CI / build (20, macos-latest) (push) Has been cancelled
CI / build (20, ubuntu-latest) (push) Has been cancelled
CI / build (20, windows-latest) (push) Has been cancelled
CI / build (22, macos-latest) (push) Has been cancelled
CI / build (22, ubuntu-latest) (push) Has been cancelled
CI / build (22, windows-latest) (push) Has been cancelled
CI / build (24, macos-latest) (push) Has been cancelled
CI / build (24, ubuntu-latest) (push) Has been cancelled
CI / build (24, windows-latest) (push) Has been cancelled
CI / pnpm (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
TestRepos / build (latest, ubuntu-latest) (push) Has been cancelled
UpdateTestRepos / update (push) Has been cancelled

This commit is contained in:
David Anson 2025-09-08 21:09:35 -07:00
parent 2a06f0a871
commit 616c3f2c22
18 changed files with 161 additions and 207 deletions

View file

@ -659,9 +659,9 @@ Standard completion callback.
Type: `Object` Type: `Object`
Call `result.toString()` for convenience or see below for an example of the Map of input file names and string identifiers to issues within.
structure of the `result` object. Passing the value `true` to `toString()`
uses rule aliases (ex: `no-hard-tabs`) instead of names (ex: `MD010`). See the [Usage section](#usage) for an example of the structure of this object.
### Config ### Config
@ -837,7 +837,7 @@ console.log(getVersion());
## Usage ## Usage
Invoke `lint` and use the `result` object's `toString` method: Invoke `lint` as an asynchronous call:
```javascript ```javascript
import { lint as lintAsync } from "markdownlint/async"; import { lint as lintAsync } from "markdownlint/async";
@ -852,34 +852,21 @@ const options = {
lintAsync(options, function callback(error, results) { lintAsync(options, function callback(error, results) {
if (!error && results) { if (!error && results) {
console.log(results.toString()); console.dir(results, { "colors": true, "depth": null });
} }
}); });
``` ```
Output:
```text
bad.string: 3: MD010/no-hard-tabs Hard tabs [Column: 19]
bad.string: 1: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#bad.string"]
bad.string: 3: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#This string fails some rules."]
bad.string: 1: MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#bad.string"]
bad.md: 3: MD010/no-hard-tabs Hard tabs [Column: 17]
bad.md: 1: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#bad.md"]
bad.md: 3: MD018/no-missing-space-atx No space after hash on atx style heading [Context: "#This file fails some rules."]
bad.md: 1: MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "#bad.md"]
```
Or as a synchronous call: Or as a synchronous call:
```javascript ```javascript
import { lint as lintSync } from "markdownlint/sync"; import { lint as lintSync } from "markdownlint/sync";
const results = lintSync(options); const results = lintSync(options);
console.log(results.toString()); console.dir(results, { "colors": true, "depth": null });
``` ```
To examine the `result` object directly via a `Promise`-based call: Or as a `Promise`-based call:
```javascript ```javascript
import { lint as lintPromise } from "markdownlint/promise"; import { lint as lintPromise } from "markdownlint/promise";
@ -888,7 +875,7 @@ const results = await lintPromise(options);
console.dir(results, { "colors": true, "depth": null }); console.dir(results, { "colors": true, "depth": null });
``` ```
Output: All of which return an object like:
```json ```json
{ {
@ -900,38 +887,36 @@ Output:
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md010.md", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md010.md",
"errorDetail": "Column: 17", "errorDetail": "Column: 17",
"errorContext": null, "errorContext": null,
"errorRange": [ 17, 1 ] }, "errorRange": [ 17, 1 ],
"fixInfo": { "editColumn": 17, "deleteCount": 1, "insertText": ' ' } }
{ "lineNumber": 1, { "lineNumber": 1,
"ruleNames": [ "MD018", "no-missing-space-atx" ], "ruleNames": [ "MD018", "no-missing-space-atx" ],
"ruleDescription": "No space after hash on atx style heading", "ruleDescription": "No space after hash on atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md018.md", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md018.md",
"errorDetail": null, "errorDetail": null,
"errorContext": "#bad.md", "errorContext": "#bad.md",
"errorRange": [ 1, 2 ] }, "errorRange": [ 1, 2 ],
"fixInfo": { "editColumn": 2, "insertText": ' ' } }
{ "lineNumber": 3, { "lineNumber": 3,
"ruleNames": [ "MD018", "no-missing-space-atx" ], "ruleNames": [ "MD018", "no-missing-space-atx" ],
"ruleDescription": "No space after hash on atx style heading", "ruleDescription": "No space after hash on atx style heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md018.md", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md018.md",
"errorDetail": null, "errorDetail": null,
"errorContext": "#This file fails\tsome rules.", "errorContext": "#This file fails\tsome rules.",
"errorRange": [ 1, 2 ] }, "errorRange": [ 1, 2 ],
"fixInfo": { "editColumn": 2, "insertText": ' ' } }
{ "lineNumber": 1, { "lineNumber": 1,
"ruleNames": [ "MD041", "first-line-heading", "first-line-h1" ], "ruleNames": [ "MD041", "first-line-heading", "first-line-h1" ],
"ruleDescription": "First line in a file should be a top-level heading", "ruleDescription": "First line in a file should be a top-level heading",
"ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md041.md", "ruleInformation": "https://github.com/DavidAnson/markdownlint/blob/v0.0.0/doc/md041.md",
"errorDetail": null, "errorDetail": null,
"errorContext": "#bad.md", "errorContext": "#bad.md",
"errorRange": null } "errorRange": null,
"fixInfo": null }
] ]
} }
``` ```
Integration with the [gulp](https://gulpjs.com/) build system is
straightforward: [`gulpfile.cjs`](example/gulpfile.cjs).
Integration with the [Grunt](https://gruntjs.com/) build system is similar:
[`Gruntfile.cjs`](example/Gruntfile.cjs).
## Browser ## Browser
`markdownlint` also works in the browser. `markdownlint` also works in the browser.
@ -957,7 +942,7 @@ const options = {
} }
}; };
const results = globalThis.markdownlint.lintSync(options).toString(); const results = globalThis.markdownlint.lintSync(options);
``` ```
## Examples ## Examples

View file

@ -1,28 +0,0 @@
// @ts-check
"use strict";
module.exports = function wrapper(grunt) {
grunt.initConfig({
"markdownlint": {
"example": {
"src": [ "*.md" ]
}
}
});
grunt.registerMultiTask("markdownlint", function task() {
const done = this.async();
import("markdownlint/async").then(({ lint }) => {
lint(
{ "files": this.filesSrc },
function callback(err, result) {
const resultString = err || ((result || "").toString());
if (resultString) {
grunt.fail.warn("\n" + resultString + "\n");
}
done(!err || !resultString);
});
}).catch(done);
});
};

View file

@ -1,24 +0,0 @@
// @ts-check
"use strict";
const gulp = require("gulp");
const through2 = require("through2");
// Simple task wrapper
gulp.task("markdownlint", function task() {
return gulp.src("*.md", { "read": false })
.pipe(through2.obj(function obj(file, enc, next) {
import("markdownlint/async").then(({ lint }) => {
lint(
{ "files": [ file.relative ] },
function callback(err, result) {
const resultString = (result || "").toString();
if (resultString) {
console.log(resultString);
}
next(err, file);
});
}).catch(next);
}));
});

View file

@ -18,18 +18,18 @@ const options = {
if (true) { if (true) {
// Makes a synchronous call, uses result.toString for pretty formatting // Makes a synchronous call
const results = lintSync(options); const results = lintSync(options);
console.log(results.toString()); console.dir(results, { "colors": true, "depth": null });
} }
if (true) { if (true) {
// Makes an asynchronous call, uses result.toString for pretty formatting // Makes an asynchronous call
lintAsync(options, function callback(error, results) { lintAsync(options, function callback(error, results) {
if (!error && results) { if (!error && results) {
console.log(results.toString()); console.dir(results, { "colors": true, "depth": null });
} }
}); });
@ -37,7 +37,7 @@ if (true) {
if (true) { if (true) {
// Makes a Promise-based asynchronous call, displays the result object directly // Makes a Promise-based asynchronous call
const results = await lintPromise(options); const results = await lintPromise(options);
console.dir(results, { "colors": true, "depth": null }); console.dir(results, { "colors": true, "depth": null });

View file

@ -171,13 +171,15 @@ lintAsync(options, assertLintResultsCallback);
assertLintResultsCallback(null, await lintPromise(options)); assertLintResultsCallback(null, await lintPromise(options));
})(); })();
const needsFixing = "# Heading\n";
assert.equal( assert.equal(
applyFix( applyFix(
"# Fixing\n", needsFixing,
{ {
"insertText": "Head", "insertText": " ",
"editColumn": 3, "editColumn": 2,
"deleteCount": 3 "deleteCount": 2
}, },
"\n" "\n"
), ),
@ -186,17 +188,12 @@ assert.equal(
assert.equal( assert.equal(
applyFixes( applyFixes(
"# Fixing\n", needsFixing,
[ lintSync({
{ "strings": {
"lineNumber": 1, needsFixing
"fixInfo": {
"insertText": "Head",
"editColumn": 3,
"deleteCount": 3
}
} }
] })["needsFixing"]
), ),
"# Heading\n" "# Heading\n"
); );

View file

@ -554,6 +554,7 @@ function convertLintErrorsVersion3To2(errors) {
"lineNumber": -1 "lineNumber": -1
}; };
return errors.filter((error, index, array) => { return errors.filter((error, index, array) => {
// @ts-ignore
delete error.fixInfo; delete error.fixInfo;
const previous = array[index - 1] || noPrevious; const previous = array[index - 1] || noPrevious;
return ( return (
@ -646,3 +647,31 @@ module.exports.convertToResultVersion1 = function convertToResultVersion1(result
module.exports.convertToResultVersion2 = function convertToResultVersion2(results) { module.exports.convertToResultVersion2 = function convertToResultVersion2(results) {
return copyAndTransformResults(results, convertLintErrorsVersion3To2); return copyAndTransformResults(results, convertLintErrorsVersion3To2);
}; };
/**
* Formats lint results to an array of strings.
*
* @param {LintResults|undefined} lintResults Lint results.
* @returns {string[]} Lint error strings.
*/
module.exports.formatLintResults = function formatLintResults(lintResults) {
const results = [];
const entries = Object.entries(lintResults || {});
entries.sort((a, b) => a[0].localeCompare(b[0]));
for (const [ source, lintErrors ] of entries) {
for (const lintError of lintErrors) {
results.push(
source + ": " +
lintError.lineNumber + ": " +
lintError.ruleNames.join("/") + " " +
lintError.ruleDescription +
(lintError.errorDetail ?
" [" + lintError.errorDetail + "]" :
"") +
(lintError.errorContext ?
" [Context: \"" + lintError.errorContext + "\"]" :
""));
}
}
return results;
};

View file

@ -3,6 +3,7 @@ export type Configuration = import("./markdownlint.mjs").Configuration;
export type ConfigurationParser = import("./markdownlint.mjs").ConfigurationParser; export type ConfigurationParser = import("./markdownlint.mjs").ConfigurationParser;
export type ConfigurationStrict = import("./markdownlint.mjs").ConfigurationStrict; export type ConfigurationStrict = import("./markdownlint.mjs").ConfigurationStrict;
export type FixInfo = import("./markdownlint.mjs").FixInfo; export type FixInfo = import("./markdownlint.mjs").FixInfo;
export type FixInfoNormalized = import("./markdownlint.mjs").FixInfoNormalized;
export type LintCallback = import("./markdownlint.mjs").LintCallback; export type LintCallback = import("./markdownlint.mjs").LintCallback;
export type LintContentCallback = import("./markdownlint.mjs").LintContentCallback; export type LintContentCallback = import("./markdownlint.mjs").LintContentCallback;
export type LintError = import("./markdownlint.mjs").LintError; export type LintError = import("./markdownlint.mjs").LintError;
@ -23,8 +24,6 @@ export type RuleConfiguration = import("./markdownlint.mjs").RuleConfiguration;
export type RuleFunction = import("./markdownlint.mjs").RuleFunction; export type RuleFunction = import("./markdownlint.mjs").RuleFunction;
export type RuleOnError = import("./markdownlint.mjs").RuleOnError; export type RuleOnError = import("./markdownlint.mjs").RuleOnError;
export type RuleOnErrorFixInfo = import("./markdownlint.mjs").RuleOnErrorFixInfo; export type RuleOnErrorFixInfo = import("./markdownlint.mjs").RuleOnErrorFixInfo;
export type RuleOnErrorFixInfoNormalized = import("./markdownlint.mjs").RuleOnErrorFixInfoNormalized;
export type RuleOnErrorInfo = import("./markdownlint.mjs").RuleOnErrorInfo; export type RuleOnErrorInfo = import("./markdownlint.mjs").RuleOnErrorInfo;
export type RuleParams = import("./markdownlint.mjs").RuleParams; export type RuleParams = import("./markdownlint.mjs").RuleParams;
export type ToStringCallback = import("./markdownlint.mjs").ToStringCallback;
export { applyFix, applyFixes, getVersion } from "./markdownlint.mjs"; export { applyFix, applyFixes, getVersion } from "./markdownlint.mjs";

View file

@ -7,6 +7,7 @@ export { resolveModule } from "./resolve-module.cjs";
/** @typedef {import("./markdownlint.mjs").ConfigurationParser} ConfigurationParser */ /** @typedef {import("./markdownlint.mjs").ConfigurationParser} ConfigurationParser */
/** @typedef {import("./markdownlint.mjs").ConfigurationStrict} ConfigurationStrict */ /** @typedef {import("./markdownlint.mjs").ConfigurationStrict} ConfigurationStrict */
/** @typedef {import("./markdownlint.mjs").FixInfo} FixInfo */ /** @typedef {import("./markdownlint.mjs").FixInfo} FixInfo */
/** @typedef {import("./markdownlint.mjs").FixInfoNormalized} FixInfoNormalized */
/** @typedef {import("./markdownlint.mjs").LintCallback} LintCallback */ /** @typedef {import("./markdownlint.mjs").LintCallback} LintCallback */
/** @typedef {import("./markdownlint.mjs").LintContentCallback} LintContentCallback */ /** @typedef {import("./markdownlint.mjs").LintContentCallback} LintContentCallback */
/** @typedef {import("./markdownlint.mjs").LintError} LintError */ /** @typedef {import("./markdownlint.mjs").LintError} LintError */
@ -27,7 +28,5 @@ export { resolveModule } from "./resolve-module.cjs";
/** @typedef {import("./markdownlint.mjs").RuleFunction} RuleFunction */ /** @typedef {import("./markdownlint.mjs").RuleFunction} RuleFunction */
/** @typedef {import("./markdownlint.mjs").RuleOnError} RuleOnError */ /** @typedef {import("./markdownlint.mjs").RuleOnError} RuleOnError */
/** @typedef {import("./markdownlint.mjs").RuleOnErrorFixInfo} RuleOnErrorFixInfo */ /** @typedef {import("./markdownlint.mjs").RuleOnErrorFixInfo} RuleOnErrorFixInfo */
/** @typedef {import("./markdownlint.mjs").RuleOnErrorFixInfoNormalized} RuleOnErrorFixInfoNormalized */
/** @typedef {import("./markdownlint.mjs").RuleOnErrorInfo} RuleOnErrorInfo */ /** @typedef {import("./markdownlint.mjs").RuleOnErrorInfo} RuleOnErrorInfo */
/** @typedef {import("./markdownlint.mjs").RuleParams} RuleParams */ /** @typedef {import("./markdownlint.mjs").RuleParams} RuleParams */
/** @typedef {import("./markdownlint.mjs").ToStringCallback} ToStringCallback */

View file

@ -63,19 +63,19 @@ export function readConfigSync(file: string, parsers?: ConfigurationParser[], fs
* Applies the specified fix to a Markdown content line. * Applies the specified fix to a Markdown content line.
* *
* @param {string} line Line of Markdown content. * @param {string} line Line of Markdown content.
* @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance. * @param {FixInfo} fixInfo FixInfo instance.
* @param {string} [lineEnding] Line ending to use. * @param {string} [lineEnding] Line ending to use.
* @returns {string | null} Fixed content or null if deleted. * @returns {string | null} Fixed content or null if deleted.
*/ */
export function applyFix(line: string, fixInfo: RuleOnErrorFixInfo, lineEnding?: string): string | null; export function applyFix(line: string, fixInfo: FixInfo, lineEnding?: string): string | null;
/** /**
* Applies as many of the specified fixes as possible to Markdown content. * Applies as many of the specified fixes as possible to Markdown content.
* *
* @param {string} input Lines of Markdown content. * @param {string} input Lines of Markdown content.
* @param {RuleOnErrorInfo[]} errors RuleOnErrorInfo instances. * @param {LintError[]} errors LintError instances.
* @returns {string} Fixed content. * @returns {string} Fixed content.
*/ */
export function applyFixes(input: string, errors: RuleOnErrorInfo[]): string; export function applyFixes(input: string, errors: LintError[]): string;
/** /**
* Gets the (semantic) version of the library. * Gets the (semantic) version of the library.
* *
@ -320,27 +320,6 @@ export type RuleOnErrorFixInfo = {
*/ */
insertText?: string; insertText?: string;
}; };
/**
* RuleOnErrorInfo with all optional properties present.
*/
export type RuleOnErrorFixInfoNormalized = {
/**
* Line number (1-based).
*/
lineNumber: number;
/**
* Column of the fix (1-based).
*/
editColumn: number;
/**
* Count of characters to delete.
*/
deleteCount: number;
/**
* Text to insert (after deleting).
*/
insertText: string;
};
/** /**
* Rule definition. * Rule definition.
*/ */
@ -442,10 +421,6 @@ export type Options = {
* A markdown-it plugin. * A markdown-it plugin.
*/ */
export type Plugin = any[]; export type Plugin = any[];
/**
* Function to pretty-print lint results.
*/
export type ToStringCallback = (ruleAliases?: boolean) => string;
/** /**
* Lint results. * Lint results.
*/ */
@ -483,11 +458,11 @@ export type LintError = {
/** /**
* Column number (1-based) and length. * Column number (1-based) and length.
*/ */
errorRange: number[]; errorRange: number[] | null;
/** /**
* Fix information. * Fix information.
*/ */
fixInfo?: FixInfo; fixInfo: FixInfo | null;
}; };
/** /**
* Fix information. * Fix information.
@ -510,6 +485,27 @@ export type FixInfo = {
*/ */
insertText?: string; insertText?: string;
}; };
/**
* FixInfo with all optional properties present.
*/
export type FixInfoNormalized = {
/**
* Line number (1-based).
*/
lineNumber: number;
/**
* Column of the fix (1-based).
*/
editColumn: number;
/**
* Count of characters to delete.
*/
deleteCount: number;
/**
* Text to insert (after deleting).
*/
insertText: string;
};
/** /**
* Called with the result of linting a string or document. * Called with the result of linting a string or document.
*/ */

View file

@ -522,6 +522,7 @@ function lintContent(
"config": null "config": null
}); });
// Function to run for each rule // Function to run for each rule
/** @type {LintError[]} */
const results = []; const results = [];
/** /**
* @param {Rule} rule Rule. * @param {Rule} rule Rule.
@ -1194,9 +1195,9 @@ export function readConfigSync(file, parsers, fs) {
/** /**
* Normalizes the fields of a RuleOnErrorFixInfo instance. * Normalizes the fields of a RuleOnErrorFixInfo instance.
* *
* @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance. * @param {FixInfo} fixInfo RuleOnErrorFixInfo instance.
* @param {number} [lineNumber] Line number. * @param {number} [lineNumber] Line number.
* @returns {RuleOnErrorFixInfoNormalized} Normalized RuleOnErrorFixInfo instance. * @returns {FixInfoNormalized} Normalized RuleOnErrorFixInfo instance.
*/ */
function normalizeFixInfo(fixInfo, lineNumber = 0) { function normalizeFixInfo(fixInfo, lineNumber = 0) {
return { return {
@ -1211,7 +1212,7 @@ function normalizeFixInfo(fixInfo, lineNumber = 0) {
* Applies the specified fix to a Markdown content line. * Applies the specified fix to a Markdown content line.
* *
* @param {string} line Line of Markdown content. * @param {string} line Line of Markdown content.
* @param {RuleOnErrorFixInfo} fixInfo RuleOnErrorFixInfo instance. * @param {FixInfo} fixInfo FixInfo instance.
* @param {string} [lineEnding] Line ending to use. * @param {string} [lineEnding] Line ending to use.
* @returns {string | null} Fixed content or null if deleted. * @returns {string | null} Fixed content or null if deleted.
*/ */
@ -1227,7 +1228,7 @@ export function applyFix(line, fixInfo, lineEnding = "\n") {
* Applies as many of the specified fixes as possible to Markdown content. * Applies as many of the specified fixes as possible to Markdown content.
* *
* @param {string} input Lines of Markdown content. * @param {string} input Lines of Markdown content.
* @param {RuleOnErrorInfo[]} errors RuleOnErrorInfo instances. * @param {LintError[]} errors LintError instances.
* @returns {string} Fixed content. * @returns {string} Fixed content.
*/ */
export function applyFixes(input, errors) { export function applyFixes(input, errors) {
@ -1424,16 +1425,6 @@ export function getVersion() {
* @property {string} [insertText] Text to insert (after deleting). * @property {string} [insertText] Text to insert (after deleting).
*/ */
/**
* RuleOnErrorInfo with all optional properties present.
*
* @typedef {Object} RuleOnErrorFixInfoNormalized
* @property {number} lineNumber Line number (1-based).
* @property {number} editColumn Column of the fix (1-based).
* @property {number} deleteCount Count of characters to delete.
* @property {string} insertText Text to insert (after deleting).
*/
/** /**
* Rule definition. * Rule definition.
* *
@ -1492,19 +1483,10 @@ export function getVersion() {
* @typedef {Array} Plugin * @typedef {Array} Plugin
*/ */
/**
* Function to pretty-print lint results.
*
* @callback ToStringCallback
* @param {boolean} [ruleAliases] True to use rule aliases.
* @returns {string} Pretty-printed results.
*/
/** /**
* Lint results. * Lint results.
* *
* @typedef {Object.<string, LintError[]>} LintResults * @typedef {Object.<string, LintError[]>} LintResults
* @property {ToStringCallback} toString String representation.
*/ */
/** /**
@ -1517,8 +1499,8 @@ export function getVersion() {
* @property {string} ruleInformation Link to more information. * @property {string} ruleInformation Link to more information.
* @property {string} errorDetail Detail about the error. * @property {string} errorDetail Detail about the error.
* @property {string} errorContext Context for the error. * @property {string} errorContext Context for the error.
* @property {number[]} errorRange Column number (1-based) and length. * @property {number[]|null} errorRange Column number (1-based) and length.
* @property {FixInfo} [fixInfo] Fix information. * @property {FixInfo|null} fixInfo Fix information.
*/ */
/** /**
@ -1531,6 +1513,16 @@ export function getVersion() {
* @property {string} [insertText] Text to insert (after deleting). * @property {string} [insertText] Text to insert (after deleting).
*/ */
/**
* FixInfo with all optional properties present.
*
* @typedef {Object} FixInfoNormalized
* @property {number} lineNumber Line number (1-based).
* @property {number} editColumn Column of the fix (1-based).
* @property {number} deleteCount Count of characters to delete.
* @property {string} insertText Text to insert (after deleting).
*/
/** /**
* Called with the result of linting a string or document. * Called with the result of linting a string or document.
* *

View file

@ -39,7 +39,6 @@
"build-declaration": "tsc --allowJs --checkJs --declaration --emitDeclarationOnly --module nodenext --outDir dts --rootDir . --target es2015 lib/exports.mjs lib/exports-async.mjs lib/exports-promise.mjs lib/exports-sync.mjs lib/markdownlint.mjs lib/resolve-module.cjs && node scripts/index.mjs copy dts/lib/exports.d.mts lib/exports.d.mts && node scripts/index.mjs copy dts/lib/exports-async.d.mts lib/exports-async.d.mts && node scripts/index.mjs copy dts/lib/exports-promise.d.mts lib/exports-promise.d.mts && node scripts/index.mjs copy dts/lib/exports-sync.d.mts lib/exports-sync.d.mts && node scripts/index.mjs copy dts/lib/markdownlint.d.mts lib/markdownlint.d.mts && node scripts/index.mjs copy dts/lib/resolve-module.d.cts lib/resolve-module.d.cts && node scripts/index.mjs remove dts", "build-declaration": "tsc --allowJs --checkJs --declaration --emitDeclarationOnly --module nodenext --outDir dts --rootDir . --target es2015 lib/exports.mjs lib/exports-async.mjs lib/exports-promise.mjs lib/exports-sync.mjs lib/markdownlint.mjs lib/resolve-module.cjs && node scripts/index.mjs copy dts/lib/exports.d.mts lib/exports.d.mts && node scripts/index.mjs copy dts/lib/exports-async.d.mts lib/exports-async.d.mts && node scripts/index.mjs copy dts/lib/exports-promise.d.mts lib/exports-promise.d.mts && node scripts/index.mjs copy dts/lib/exports-sync.d.mts lib/exports-sync.d.mts && node scripts/index.mjs copy dts/lib/markdownlint.d.mts lib/markdownlint.d.mts && node scripts/index.mjs copy dts/lib/resolve-module.d.cts lib/resolve-module.d.cts && node scripts/index.mjs remove dts",
"build-demo": "node scripts/index.mjs copy node_modules/markdown-it/dist/markdown-it.min.js demo/markdown-it.min.js && cd demo && webpack --no-stats", "build-demo": "node scripts/index.mjs copy node_modules/markdown-it/dist/markdown-it.min.js demo/markdown-it.min.js && cd demo && webpack --no-stats",
"build-docs": "node doc-build/build-rules.mjs", "build-docs": "node doc-build/build-rules.mjs",
"build-example": "npm install --no-save --ignore-scripts grunt grunt-cli gulp through2",
"ci": "npm-run-all --continue-on-error --parallel build-demo lint serial-config-docs serial-declaration test-cover && git diff --exit-code", "ci": "npm-run-all --continue-on-error --parallel build-demo lint serial-config-docs serial-declaration test-cover && git diff --exit-code",
"clone-test-repos-apache-airflow": "cd test-repos && git clone https://github.com/apache/airflow apache-airflow --depth 1 --no-tags --quiet", "clone-test-repos-apache-airflow": "cd test-repos && git clone https://github.com/apache/airflow apache-airflow --depth 1 --no-tags --quiet",
"clone-test-repos-dotnet-docs": "cd test-repos && git clone https://github.com/dotnet/docs dotnet-docs --depth 1 --no-tags --quiet", "clone-test-repos-dotnet-docs": "cd test-repos && git clone https://github.com/dotnet/docs dotnet-docs --depth 1 --no-tags --quiet",
@ -54,7 +53,7 @@
"clone-test-repos-webpack-webpack-js-org": "cd test-repos && git clone https://github.com/webpack/webpack.js.org webpack-webpack-js-org --depth 1 --no-tags --quiet", "clone-test-repos-webpack-webpack-js-org": "cd test-repos && git clone https://github.com/webpack/webpack.js.org webpack-webpack-js-org --depth 1 --no-tags --quiet",
"clone-test-repos": "mkdir test-repos && cd test-repos && npm run clone-test-repos-apache-airflow && npm run clone-test-repos-dotnet-docs && npm run clone-test-repos-electron-electron && npm run clone-test-repos-eslint-eslint && npm run clone-test-repos-mdn-content && npm run clone-test-repos-mkdocs-mkdocs && npm run clone-test-repos-mochajs-mocha && npm run clone-test-repos-pi-hole-docs && npm run clone-test-repos-v8-v8-dev && npm run clone-test-repos-webhintio-hint && npm run clone-test-repos-webpack-webpack-js-org", "clone-test-repos": "mkdir test-repos && cd test-repos && npm run clone-test-repos-apache-airflow && npm run clone-test-repos-dotnet-docs && npm run clone-test-repos-electron-electron && npm run clone-test-repos-eslint-eslint && npm run clone-test-repos-mdn-content && npm run clone-test-repos-mkdocs-mkdocs && npm run clone-test-repos-mochajs-mocha && npm run clone-test-repos-pi-hole-docs && npm run clone-test-repos-v8-v8-dev && npm run clone-test-repos-webhintio-hint && npm run clone-test-repos-webpack-webpack-js-org",
"declaration": "npm run build-declaration && npm run test-declaration", "declaration": "npm run build-declaration && npm run test-declaration",
"example": "cd example && node standalone.mjs && grunt markdownlint --force && gulp markdownlint", "example": "cd example && node standalone.mjs",
"lint": "eslint --max-warnings 0", "lint": "eslint --max-warnings 0",
"lint-test-repos": "ava --timeout=10m test/markdownlint-test-repos-*.mjs", "lint-test-repos": "ava --timeout=10m test/markdownlint-test-repos-*.mjs",
"serial-config-docs": "npm run build-config && npm run build-docs", "serial-config-docs": "npm run build-config && npm run build-docs",

View file

@ -5,7 +5,7 @@ import path from "node:path";
import test from "ava"; import test from "ava";
import { characterEntities } from "character-entities"; import { characterEntities } from "character-entities";
import { gemoji } from "gemoji"; import { gemoji } from "gemoji";
import helpers from "../helpers/helpers.cjs"; import helpers, { formatLintResults } from "../helpers/helpers.cjs";
import { lint } from "markdownlint/promise"; import { lint } from "markdownlint/promise";
import { forEachInlineCodeSpan } from "../lib/markdownit.cjs"; import { forEachInlineCodeSpan } from "../lib/markdownit.cjs";
import { getReferenceLinkImageData } from "../lib/cache.mjs"; import { getReferenceLinkImageData } from "../lib/cache.mjs";
@ -529,3 +529,17 @@ test("hasOverlap", (t) => {
t.false(helpers.hasOverlap(rangeB, rangeA), JSON.stringify({ rangeB, rangeA })); t.false(helpers.hasOverlap(rangeB, rangeA), JSON.stringify({ rangeB, rangeA }));
} }
}); });
test("formatLintResults", async(t) => {
t.plan(2);
t.deepEqual(formatLintResults(undefined), []);
const lintResults = await lint({ "strings": { "content": "# Heading <br/>" } });
t.deepEqual(
formatLintResults(lintResults),
[
"content: 1: MD019/no-multiple-space-atx Multiple spaces after hash on atx style heading [Context: \"# Heading <br/>\"]",
"content: 1: MD033/no-inline-html Inline HTML [Element: br]",
"content: 1: MD047/single-trailing-newline Files should end with a single newline character"
]
);
});

View file

@ -5,6 +5,7 @@ const { join } = path.posix;
import { globby } from "globby"; import { globby } from "globby";
import jsoncParser from "jsonc-parser"; import jsoncParser from "jsonc-parser";
import jsYaml from "js-yaml"; import jsYaml from "js-yaml";
import { formatLintResults } from "markdownlint/helpers";
import { lint, readConfig } from "markdownlint/promise"; import { lint, readConfig } from "markdownlint/promise";
import { markdownlintParallel } from "./markdownlint-test-parallel.mjs"; import { markdownlintParallel } from "./markdownlint-test-parallel.mjs";
@ -49,9 +50,8 @@ export function lintTestRepo(t, globPatterns, configPath, configOverrides, paral
files, files,
config config
}).then((results) => { }).then((results) => {
const resultsString = results.toString();
t.snapshot( t.snapshot(
resultsString, formatLintResults(results).join("\n"),
"Expected linting violations" "Expected linting violations"
); );
}); });

View file

@ -49,46 +49,42 @@ function getMarkdownItFactory(markdownItPlugins) {
} }
test("simpleAsync", (t) => new Promise((resolve) => { test("simpleAsync", (t) => new Promise((resolve) => {
t.plan(3);
const options = {
"strings": {
"content": "# Heading"
}
};
lintAsync(options, (err, actual) => {
t.falsy(err);
t.is(actual?.content.length, 1);
t.is(actual?.content[0].ruleNames[0], "MD047");
resolve();
});
}));
test("simpleSync", (t) => {
t.plan(2); t.plan(2);
const options = { const options = {
"strings": { "strings": {
"content": "# Heading" "content": "# Heading"
} }
}; };
const expected = "content: 1: MD047/single-trailing-newline " + const actual = lintSync(options);
"Files should end with a single newline character"; t.is(actual.content.length, 1);
lintAsync(options, (err, actual) => { t.is(actual.content[0].ruleNames[0], "MD047");
t.falsy(err);
// @ts-ignore
t.is(actual.toString(), expected, "Unexpected results.");
resolve();
});
}));
test("simpleSync", (t) => {
t.plan(1);
const options = {
"strings": {
"content": "# Heading"
}
};
const expected = "content: 1: MD047/single-trailing-newline " +
"Files should end with a single newline character";
const actual = lintSync(options).toString();
t.is(actual, expected, "Unexpected results.");
}); });
test("simplePromise", (t) => { test("simplePromise", (t) => {
t.plan(1); t.plan(2);
const options = { const options = {
"strings": { "strings": {
"content": "# Heading" "content": "# Heading"
} }
}; };
const expected = "content: 1: MD047/single-trailing-newline " +
"Files should end with a single newline character";
return lintPromise(options).then((actual) => { return lintPromise(options).then((actual) => {
t.is(actual.toString(), expected, "Unexpected results."); t.is(actual.content.length, 1);
t.is(actual.content[0].ruleNames[0], "MD047");
}); });
}); });
@ -1281,7 +1277,7 @@ test("token-map-spans", (t) => {
}); });
test("configParsersInvalid", async(t) => { test("configParsersInvalid", async(t) => {
t.plan(1); t.plan(2);
const options = { const options = {
"strings": { "strings": {
"content": [ "content": [
@ -1294,10 +1290,9 @@ test("configParsersInvalid", async(t) => {
].join("\n") ].join("\n")
} }
}; };
const expected = "content: 1: MD041/first-line-heading/first-line-h1 " +
"First line in a file should be a top-level heading [Context: \"Text\"]";
const actual = await lintPromise(options); const actual = await lintPromise(options);
t.is(actual.toString(), expected, "Unexpected results."); t.is(actual.content.length, 1);
t.is(actual.content[0].ruleNames[0], "MD041");
}); });
test("configParsersJSON", async(t) => { test("configParsersJSON", async(t) => {
@ -1317,7 +1312,7 @@ test("configParsersJSON", async(t) => {
} }
}; };
const actual = await lintPromise(options); const actual = await lintPromise(options);
t.is(actual.toString(), "", "Unexpected results."); t.is(actual.content.length, 0);
}); });
test("configParsersJSONC", async(t) => { test("configParsersJSONC", async(t) => {
@ -1339,7 +1334,7 @@ test("configParsersJSONC", async(t) => {
"configParsers": [ jsoncParser.parse ] "configParsers": [ jsoncParser.parse ]
}; };
const actual = await lintPromise(options); const actual = await lintPromise(options);
t.is(actual.toString(), "", "Unexpected results."); t.is(actual.content.length, 0);
}); });
test("configParsersYAML", async(t) => { test("configParsersYAML", async(t) => {
@ -1360,7 +1355,7 @@ test("configParsersYAML", async(t) => {
}; };
// @ts-ignore // @ts-ignore
const actual = await lintPromise(options); const actual = await lintPromise(options);
t.is(actual.toString(), "", "Unexpected results."); t.is(actual.content.length, 0);
}); });
test("configParsersTOML", async(t) => { test("configParsersTOML", async(t) => {
@ -1382,7 +1377,7 @@ test("configParsersTOML", async(t) => {
] ]
}; };
const actual = await lintPromise(options); const actual = await lintPromise(options);
t.is(actual.toString(), "", "Unexpected results."); t.is(actual.content.length, 0);
}); });
test("getVersion", (t) => { test("getVersion", (t) => {

View file

@ -37,6 +37,7 @@ Generated by [AVA](https://avajs.dev).
'endOfLineHtmlEntityRe', 'endOfLineHtmlEntityRe',
'escapeForRegExp', 'escapeForRegExp',
'expandTildePath', 'expandTildePath',
'formatLintResults',
'frontMatterHasTitle', 'frontMatterHasTitle',
'frontMatterRe', 'frontMatterRe',
'getHtmlAttributeRe', 'getHtmlAttributeRe',

View file

@ -320,9 +320,6 @@ Generated by [AVA](https://avajs.dev).
`test-repos/mochajs-mocha/.github/CODE_OF_CONDUCT.md: 63: MD034/no-bare-urls Bare URL used [Context: "report@lists.openjsf.org"]␊ `test-repos/mochajs-mocha/.github/CODE_OF_CONDUCT.md: 63: MD034/no-bare-urls Bare URL used [Context: "report@lists.openjsf.org"]␊
test-repos/mochajs-mocha/.github/CONTRIBUTING.md: 42: MD051/link-fragments Link fragments should be valid [Context: "[⚽️ About Project Goals](#⚽️-about-project-goals)"]␊ test-repos/mochajs-mocha/.github/CONTRIBUTING.md: 42: MD051/link-fragments Link fragments should be valid [Context: "[⚽️ About Project Goals](#⚽️-about-project-goals)"]␊
test-repos/mochajs-mocha/PROJECT_CHARTER.md: 51: MD051/link-fragments Link fragments should be valid [Context: "[§2: Scope](#%c2%a72-scope)"]␊
test-repos/mochajs-mocha/PROJECT_CHARTER.md: 56: MD051/link-fragments Link fragments should be valid [Context: "[§2: Scope](#%c2%a72-scope)"]␊
test-repos/mochajs-mocha/README.md: 39: MD045/no-alt-text Images should have alternate text (alt text)␊
test-repos/mochajs-mocha/docs/changelogs/CHANGELOG_V3_older.md: 207: MD059/descriptive-link-text Link text should be descriptive [Context: "[more]"]␊ test-repos/mochajs-mocha/docs/changelogs/CHANGELOG_V3_older.md: 207: MD059/descriptive-link-text Link text should be descriptive [Context: "[more]"]␊
test-repos/mochajs-mocha/docs/index.md: 34: MD051/link-fragments Link fragments should be valid [Context: "[global variable leak detection](#-check-leaks)"]␊ test-repos/mochajs-mocha/docs/index.md: 34: MD051/link-fragments Link fragments should be valid [Context: "[global variable leak detection](#-check-leaks)"]␊
test-repos/mochajs-mocha/docs/index.md: 35: MD051/link-fragments Link fragments should be valid [Context: "[optionally run tests that match a regexp](#-grep-regexp-g-regexp)"]␊ test-repos/mochajs-mocha/docs/index.md: 35: MD051/link-fragments Link fragments should be valid [Context: "[optionally run tests that match a regexp](#-grep-regexp-g-regexp)"]␊
@ -350,7 +347,10 @@ Generated by [AVA](https://avajs.dev).
test-repos/mochajs-mocha/docs/index.md: 2432: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "caniuse-promises"] [Context: "[caniuse-promises]: https://ca..."]␊ test-repos/mochajs-mocha/docs/index.md: 2432: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "caniuse-promises"] [Context: "[caniuse-promises]: https://ca..."]␊
test-repos/mochajs-mocha/docs/index.md: 2463: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "mocha-website"] [Context: "[mocha-website]: https://mocha..."]␊ test-repos/mochajs-mocha/docs/index.md: 2463: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "mocha-website"] [Context: "[mocha-website]: https://mocha..."]␊
test-repos/mochajs-mocha/docs/index.md: 2230: MD059/descriptive-link-text Link text should be descriptive [Context: "[here]"]␊ test-repos/mochajs-mocha/docs/index.md: 2230: MD059/descriptive-link-text Link text should be descriptive [Context: "[here]"]␊
test-repos/mochajs-mocha/docs/index.md: 2302: MD059/descriptive-link-text Link text should be descriptive [Context: "[here]"]` test-repos/mochajs-mocha/docs/index.md: 2302: MD059/descriptive-link-text Link text should be descriptive [Context: "[here]"]␊
test-repos/mochajs-mocha/PROJECT_CHARTER.md: 51: MD051/link-fragments Link fragments should be valid [Context: "[§2: Scope](#%c2%a72-scope)"]␊
test-repos/mochajs-mocha/PROJECT_CHARTER.md: 56: MD051/link-fragments Link fragments should be valid [Context: "[§2: Scope](#%c2%a72-scope)"]␊
test-repos/mochajs-mocha/README.md: 39: MD045/no-alt-text Images should have alternate text (alt text)`
## https://github.com/pi-hole/docs ## https://github.com/pi-hole/docs
@ -738,12 +738,12 @@ Generated by [AVA](https://avajs.dev).
`test-repos/webhintio-hint/packages/hint-apple-touch-icons/README.md: 198: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "icon scaling"] [Context: "[icon scaling]: https://realfa..."]␊ `test-repos/webhintio-hint/packages/hint-apple-touch-icons/README.md: 198: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "icon scaling"] [Context: "[icon scaling]: https://realfa..."]␊
test-repos/webhintio-hint/packages/hint-apple-touch-icons/README.md: 202: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "web app manifest spec"] [Context: "[web app manifest spec]: https..."]␊ test-repos/webhintio-hint/packages/hint-apple-touch-icons/README.md: 202: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "web app manifest spec"] [Context: "[web app manifest spec]: https..."]␊
test-repos/webhintio-hint/packages/hint-axe/README.md: 170: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "axe rules"] [Context: "[axe rules]: https://github.co..."]␊ test-repos/webhintio-hint/packages/hint-axe/README.md: 170: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "axe rules"] [Context: "[axe rules]: https://github.co..."]␊
test-repos/webhintio-hint/packages/hint-compat-api/README.md: 48: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "npm docs"] [Context: "[npm docs]: https://docs.npmjs..."]␊
test-repos/webhintio-hint/packages/hint-compat-api/docs/html.md: 153: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "global-attr"] [Context: "[global-attr]: https://develop..."]␊ test-repos/webhintio-hint/packages/hint-compat-api/docs/html.md: 153: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "global-attr"] [Context: "[global-attr]: https://develop..."]␊
test-repos/webhintio-hint/packages/hint-detect-css-reflows/README.md: 96: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊ test-repos/webhintio-hint/packages/hint-compat-api/README.md: 48: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "npm docs"] [Context: "[npm docs]: https://docs.npmjs..."]␊
test-repos/webhintio-hint/packages/hint-detect-css-reflows/docs/composite.md: 73: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊ test-repos/webhintio-hint/packages/hint-detect-css-reflows/docs/composite.md: 73: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊
test-repos/webhintio-hint/packages/hint-detect-css-reflows/docs/layout.md: 73: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊ test-repos/webhintio-hint/packages/hint-detect-css-reflows/docs/layout.md: 73: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊
test-repos/webhintio-hint/packages/hint-detect-css-reflows/docs/paint.md: 73: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊ test-repos/webhintio-hint/packages/hint-detect-css-reflows/docs/paint.md: 73: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊
test-repos/webhintio-hint/packages/hint-detect-css-reflows/README.md: 96: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "understanding-critical-path"] [Context: "[understanding-critical-path]:..."]␊
test-repos/webhintio-hint/packages/hint-doctype/README.md: 130: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "npm docs"] [Context: "[npm docs]: https://docs.npmjs..."]␊ test-repos/webhintio-hint/packages/hint-doctype/README.md: 130: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "npm docs"] [Context: "[npm docs]: https://docs.npmjs..."]␊
test-repos/webhintio-hint/packages/hint-highest-available-document-mode/README.md: 420: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "mod_mime"] [Context: "[mod_mime]: https://httpd.apac..."]␊ test-repos/webhintio-hint/packages/hint-highest-available-document-mode/README.md: 420: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "mod_mime"] [Context: "[mod_mime]: https://httpd.apac..."]␊
test-repos/webhintio-hint/packages/hint-http-compression/README.md: 1087: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "gzip is not enough"] [Context: "[gzip is not enough]: https://..."]␊ test-repos/webhintio-hint/packages/hint-http-compression/README.md: 1087: MD053/link-image-reference-definitions Link and image reference definitions should be needed [Unused link or image reference definition: "gzip is not enough"] [Context: "[gzip is not enough]: https://..."]␊