mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-09-22 05:40:48 +02:00
Move markdown-it token manipulation code into a separate file which is only require()-ed if/when needed.
This commit is contained in:
parent
a65e05bff2
commit
98ff66209d
6 changed files with 384 additions and 344 deletions
|
@ -267,67 +267,6 @@ module.exports.emphasisOrStrongStyleFor =
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback InlineCodeSpanCallback
|
||||
* @param {string} code Code content.
|
||||
* @param {number} lineIndex Line index (0-based).
|
||||
* @param {number} columnIndex Column index (0-based).
|
||||
* @param {number} ticks Count of backticks.
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calls the provided function for each inline code span's content.
|
||||
*
|
||||
* @param {string} input Markdown content.
|
||||
* @param {InlineCodeSpanCallback} handler Callback function taking (code,
|
||||
* lineIndex, columnIndex, ticks).
|
||||
* @returns {void}
|
||||
*/
|
||||
function forEachInlineCodeSpan(input, handler) {
|
||||
const backtickRe = /`+/g;
|
||||
let match = null;
|
||||
const backticksLengthAndIndex = [];
|
||||
while ((match = backtickRe.exec(input)) !== null) {
|
||||
backticksLengthAndIndex.push([ match[0].length, match.index ]);
|
||||
}
|
||||
const newLinesIndex = [];
|
||||
while ((match = newLineRe.exec(input)) !== null) {
|
||||
newLinesIndex.push(match.index);
|
||||
}
|
||||
let lineIndex = 0;
|
||||
let lineStartIndex = 0;
|
||||
let k = 0;
|
||||
for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
|
||||
const [ startLength, startIndex ] = backticksLengthAndIndex[i];
|
||||
if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
|
||||
for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
|
||||
const [ endLength, endIndex ] = backticksLengthAndIndex[j];
|
||||
if (startLength === endLength) {
|
||||
for (; k < newLinesIndex.length; k++) {
|
||||
const newLineIndex = newLinesIndex[k];
|
||||
if (startIndex < newLineIndex) {
|
||||
break;
|
||||
}
|
||||
lineIndex++;
|
||||
lineStartIndex = newLineIndex + 1;
|
||||
}
|
||||
const columnIndex = startIndex - lineStartIndex + startLength;
|
||||
handler(
|
||||
input.slice(startIndex + startLength, endIndex),
|
||||
lineIndex,
|
||||
columnIndex,
|
||||
startLength
|
||||
);
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports.forEachInlineCodeSpan = forEachInlineCodeSpan;
|
||||
|
||||
/**
|
||||
* Adds ellipsis to the left/right/middle of the specified text.
|
||||
*
|
||||
|
@ -1597,6 +1536,189 @@ module.exports.homepage = "https://github.com/DavidAnson/markdownlint";
|
|||
module.exports.version = "0.35.0";
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "../lib/markdownit.cjs":
|
||||
/*!*****************************!*\
|
||||
!*** ../lib/markdownit.cjs ***!
|
||||
\*****************************/
|
||||
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
||||
|
||||
"use strict";
|
||||
// @ts-check
|
||||
|
||||
|
||||
|
||||
const { newLineRe } = __webpack_require__(/*! ../helpers */ "../helpers/helpers.js");
|
||||
|
||||
/**
|
||||
* @callback InlineCodeSpanCallback
|
||||
* @param {string} code Code content.
|
||||
* @param {number} lineIndex Line index (0-based).
|
||||
* @param {number} columnIndex Column index (0-based).
|
||||
* @param {number} ticks Count of backticks.
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calls the provided function for each inline code span's content.
|
||||
*
|
||||
* @param {string} input Markdown content.
|
||||
* @param {InlineCodeSpanCallback} handler Callback function taking (code,
|
||||
* lineIndex, columnIndex, ticks).
|
||||
* @returns {void}
|
||||
*/
|
||||
function forEachInlineCodeSpan(input, handler) {
|
||||
const backtickRe = /`+/g;
|
||||
let match = null;
|
||||
const backticksLengthAndIndex = [];
|
||||
while ((match = backtickRe.exec(input)) !== null) {
|
||||
backticksLengthAndIndex.push([ match[0].length, match.index ]);
|
||||
}
|
||||
const newLinesIndex = [];
|
||||
while ((match = newLineRe.exec(input)) !== null) {
|
||||
newLinesIndex.push(match.index);
|
||||
}
|
||||
let lineIndex = 0;
|
||||
let lineStartIndex = 0;
|
||||
let k = 0;
|
||||
for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
|
||||
const [ startLength, startIndex ] = backticksLengthAndIndex[i];
|
||||
if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
|
||||
for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
|
||||
const [ endLength, endIndex ] = backticksLengthAndIndex[j];
|
||||
if (startLength === endLength) {
|
||||
for (; k < newLinesIndex.length; k++) {
|
||||
const newLineIndex = newLinesIndex[k];
|
||||
if (startIndex < newLineIndex) {
|
||||
break;
|
||||
}
|
||||
lineIndex++;
|
||||
lineStartIndex = newLineIndex + 1;
|
||||
}
|
||||
const columnIndex = startIndex - lineStartIndex + startLength;
|
||||
handler(
|
||||
input.slice(startIndex + startLength, endIndex),
|
||||
lineIndex,
|
||||
columnIndex,
|
||||
startLength
|
||||
);
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze all freeze-able members of a token and its children.
|
||||
*
|
||||
* @param {import("./markdownlint").MarkdownItToken} token A markdown-it token.
|
||||
* @returns {void}
|
||||
*/
|
||||
function freezeToken(token) {
|
||||
if (token.attrs) {
|
||||
for (const attr of token.attrs) {
|
||||
Object.freeze(attr);
|
||||
}
|
||||
Object.freeze(token.attrs);
|
||||
}
|
||||
if (token.children) {
|
||||
for (const child of token.children) {
|
||||
freezeToken(child);
|
||||
}
|
||||
Object.freeze(token.children);
|
||||
}
|
||||
if (token.map) {
|
||||
Object.freeze(token.map);
|
||||
}
|
||||
Object.freeze(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotate tokens with line/lineNumber and freeze them.
|
||||
*
|
||||
* @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
|
||||
* @param {string[]} lines Lines of Markdown content.
|
||||
* @returns {void}
|
||||
*/
|
||||
function annotateAndFreezeTokens(tokens, lines) {
|
||||
let trMap = null;
|
||||
// eslint-disable-next-line jsdoc/valid-types
|
||||
/** @type import("./markdownlint").MarkdownItToken[] */
|
||||
// @ts-ignore
|
||||
const markdownItTokens = tokens;
|
||||
for (const token of markdownItTokens) {
|
||||
// Provide missing maps for table content
|
||||
if (token.type === "tr_open") {
|
||||
trMap = token.map;
|
||||
} else if (token.type === "tr_close") {
|
||||
trMap = null;
|
||||
}
|
||||
if (!token.map && trMap) {
|
||||
token.map = [ ...trMap ];
|
||||
}
|
||||
// Update token metadata
|
||||
if (token.map) {
|
||||
token.line = lines[token.map[0]];
|
||||
token.lineNumber = token.map[0] + 1;
|
||||
// Trim bottom of token to exclude whitespace lines
|
||||
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
||||
token.map[1]--;
|
||||
}
|
||||
}
|
||||
// Annotate children with lineNumber
|
||||
if (token.children) {
|
||||
const codeSpanExtraLines = [];
|
||||
if (token.children.some((child) => child.type === "code_inline")) {
|
||||
forEachInlineCodeSpan(token.content, (code) => {
|
||||
codeSpanExtraLines.push(code.split(newLineRe).length - 1);
|
||||
});
|
||||
}
|
||||
let lineNumber = token.lineNumber;
|
||||
for (const child of token.children) {
|
||||
child.lineNumber = lineNumber;
|
||||
child.line = lines[lineNumber - 1];
|
||||
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
|
||||
lineNumber++;
|
||||
} else if (child.type === "code_inline") {
|
||||
lineNumber += codeSpanExtraLines.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
freezeToken(token);
|
||||
}
|
||||
Object.freeze(tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of markdown-it tokens for the input.
|
||||
*
|
||||
* @param {import("./markdownlint").Plugin[]} markdownItPlugins Additional plugins.
|
||||
* @param {string} content Markdown content.
|
||||
* @param {string[]} lines Lines of Markdown content.
|
||||
* @returns {import("markdown-it").Token[]} Array of markdown-it tokens.
|
||||
*/
|
||||
function getMarkdownItTokens(markdownItPlugins, content, lines) {
|
||||
const markdownit = __webpack_require__(/*! markdown-it */ "markdown-it");
|
||||
const md = markdownit({ "html": true });
|
||||
// const markdownItPlugins = options.markdownItPlugins || [];
|
||||
for (const plugin of markdownItPlugins) {
|
||||
// @ts-ignore
|
||||
md.use(...plugin);
|
||||
}
|
||||
const tokens = md.parse(content, {});
|
||||
annotateAndFreezeTokens(tokens, lines);
|
||||
return tokens;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
forEachInlineCodeSpan,
|
||||
getMarkdownItTokens
|
||||
};
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "../lib/markdownlint.js":
|
||||
|
@ -1805,87 +1927,6 @@ function removeFrontMatter(content, frontMatter) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze all freeze-able members of a token and its children.
|
||||
*
|
||||
* @param {MarkdownItToken} token A markdown-it token.
|
||||
* @returns {void}
|
||||
*/
|
||||
function freezeToken(token) {
|
||||
if (token.attrs) {
|
||||
for (const attr of token.attrs) {
|
||||
Object.freeze(attr);
|
||||
}
|
||||
Object.freeze(token.attrs);
|
||||
}
|
||||
if (token.children) {
|
||||
for (const child of token.children) {
|
||||
freezeToken(child);
|
||||
}
|
||||
Object.freeze(token.children);
|
||||
}
|
||||
if (token.map) {
|
||||
Object.freeze(token.map);
|
||||
}
|
||||
Object.freeze(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotate tokens with line/lineNumber and freeze them.
|
||||
*
|
||||
* @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
|
||||
* @param {string[]} lines Lines of Markdown content.
|
||||
* @returns {void}
|
||||
*/
|
||||
function annotateAndFreezeTokens(tokens, lines) {
|
||||
let trMap = null;
|
||||
// eslint-disable-next-line jsdoc/valid-types
|
||||
/** @type MarkdownItToken[] */
|
||||
// @ts-ignore
|
||||
const markdownItTokens = tokens;
|
||||
for (const token of markdownItTokens) {
|
||||
// Provide missing maps for table content
|
||||
if (token.type === "tr_open") {
|
||||
trMap = token.map;
|
||||
} else if (token.type === "tr_close") {
|
||||
trMap = null;
|
||||
}
|
||||
if (!token.map && trMap) {
|
||||
token.map = [ ...trMap ];
|
||||
}
|
||||
// Update token metadata
|
||||
if (token.map) {
|
||||
token.line = lines[token.map[0]];
|
||||
token.lineNumber = token.map[0] + 1;
|
||||
// Trim bottom of token to exclude whitespace lines
|
||||
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
||||
token.map[1]--;
|
||||
}
|
||||
}
|
||||
// Annotate children with lineNumber
|
||||
if (token.children) {
|
||||
const codeSpanExtraLines = [];
|
||||
if (token.children.some((child) => child.type === "code_inline")) {
|
||||
helpers.forEachInlineCodeSpan(token.content, (code) => {
|
||||
codeSpanExtraLines.push(code.split(helpers.newLineRe).length - 1);
|
||||
});
|
||||
}
|
||||
let lineNumber = token.lineNumber;
|
||||
for (const child of token.children) {
|
||||
child.lineNumber = lineNumber;
|
||||
child.line = lines[lineNumber - 1];
|
||||
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
|
||||
lineNumber++;
|
||||
} else if (child.type === "code_inline") {
|
||||
lineNumber += codeSpanExtraLines.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
freezeToken(token);
|
||||
}
|
||||
Object.freeze(tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map rule names/tags to canonical rule name.
|
||||
*
|
||||
|
@ -2141,7 +2182,7 @@ function getEnabledRulesPerLineNumber(
|
|||
* names.
|
||||
* @param {string} name Identifier for the content.
|
||||
* @param {string} content Markdown content.
|
||||
* @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
|
||||
* @param {Plugin[]} markdownItPlugins Additional plugins.
|
||||
* @param {Configuration} config Configuration object.
|
||||
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
||||
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
||||
|
@ -2156,7 +2197,7 @@ function lintContent(
|
|||
aliasToRuleNames,
|
||||
name,
|
||||
content,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -2185,13 +2226,15 @@ function lintContent(
|
|||
(rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
|
||||
);
|
||||
// Parse content into parser tokens
|
||||
const markdownitTokens = needMarkdownItTokens ? getMarkdownIt().parse(content, {}) : [];
|
||||
const micromarkTokens = micromark.parse(content);
|
||||
// Hide the content of HTML comments from rules
|
||||
const preClearedContent = content;
|
||||
content = helpers.clearHtmlCommentText(content);
|
||||
// Parse content into lines and update markdown-it tokens
|
||||
// Parse content into lines and get markdown-it tokens
|
||||
const lines = content.split(helpers.newLineRe);
|
||||
annotateAndFreezeTokens(markdownitTokens, lines);
|
||||
const markdownitTokens = needMarkdownItTokens ?
|
||||
(__webpack_require__(/*! ./markdownit.cjs */ "../lib/markdownit.cjs").getMarkdownItTokens)(markdownItPlugins, preClearedContent, lines) :
|
||||
[];
|
||||
// Create (frozen) parameters for rules
|
||||
/** @type {MarkdownParsers} */
|
||||
// @ts-ignore
|
||||
|
@ -2444,7 +2487,7 @@ function lintContent(
|
|||
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
||||
* names.
|
||||
* @param {string} file Path of file to lint.
|
||||
* @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
|
||||
* @param {Plugin[]} markdownItPlugins Additional plugins.
|
||||
* @param {Configuration} config Configuration object.
|
||||
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
||||
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
||||
|
@ -2460,7 +2503,7 @@ function lintFile(
|
|||
ruleList,
|
||||
aliasToRuleNames,
|
||||
file,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -2480,7 +2523,7 @@ function lintFile(
|
|||
aliasToRuleNames,
|
||||
file,
|
||||
content,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -2547,16 +2590,7 @@ function lintInput(options, synchronous, callback) {
|
|||
const resultVersion = (options.resultVersion === undefined) ?
|
||||
3 :
|
||||
options.resultVersion;
|
||||
const getMarkdownIt = () => {
|
||||
const markdownit = __webpack_require__(/*! markdown-it */ "markdown-it");
|
||||
const md = markdownit({ "html": true });
|
||||
const markdownItPlugins = options.markdownItPlugins || [];
|
||||
for (const plugin of markdownItPlugins) {
|
||||
// @ts-ignore
|
||||
md.use(...plugin);
|
||||
}
|
||||
return md;
|
||||
};
|
||||
const markdownItPlugins = options.markdownItPlugins || [];
|
||||
const fs = options.fs || __webpack_require__(/*! node:fs */ "?d0ee");
|
||||
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
||||
const results = newResults(ruleList);
|
||||
|
@ -2588,7 +2622,7 @@ function lintInput(options, synchronous, callback) {
|
|||
ruleList,
|
||||
aliasToRuleNames,
|
||||
currentItem,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -2607,7 +2641,7 @@ function lintInput(options, synchronous, callback) {
|
|||
aliasToRuleNames,
|
||||
currentItem,
|
||||
strings[currentItem] || "",
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -2922,13 +2956,6 @@ module.exports = markdownlint;
|
|||
|
||||
// Type declarations
|
||||
|
||||
/**
|
||||
* Function to get an instance of the markdown-it parser.
|
||||
*
|
||||
* @callback GetMarkdownIt
|
||||
* @returns {import("markdown-it")}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function to implement rule logic.
|
||||
*
|
||||
|
|
|
@ -255,67 +255,6 @@ module.exports.emphasisOrStrongStyleFor =
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback InlineCodeSpanCallback
|
||||
* @param {string} code Code content.
|
||||
* @param {number} lineIndex Line index (0-based).
|
||||
* @param {number} columnIndex Column index (0-based).
|
||||
* @param {number} ticks Count of backticks.
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calls the provided function for each inline code span's content.
|
||||
*
|
||||
* @param {string} input Markdown content.
|
||||
* @param {InlineCodeSpanCallback} handler Callback function taking (code,
|
||||
* lineIndex, columnIndex, ticks).
|
||||
* @returns {void}
|
||||
*/
|
||||
function forEachInlineCodeSpan(input, handler) {
|
||||
const backtickRe = /`+/g;
|
||||
let match = null;
|
||||
const backticksLengthAndIndex = [];
|
||||
while ((match = backtickRe.exec(input)) !== null) {
|
||||
backticksLengthAndIndex.push([ match[0].length, match.index ]);
|
||||
}
|
||||
const newLinesIndex = [];
|
||||
while ((match = newLineRe.exec(input)) !== null) {
|
||||
newLinesIndex.push(match.index);
|
||||
}
|
||||
let lineIndex = 0;
|
||||
let lineStartIndex = 0;
|
||||
let k = 0;
|
||||
for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
|
||||
const [ startLength, startIndex ] = backticksLengthAndIndex[i];
|
||||
if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
|
||||
for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
|
||||
const [ endLength, endIndex ] = backticksLengthAndIndex[j];
|
||||
if (startLength === endLength) {
|
||||
for (; k < newLinesIndex.length; k++) {
|
||||
const newLineIndex = newLinesIndex[k];
|
||||
if (startIndex < newLineIndex) {
|
||||
break;
|
||||
}
|
||||
lineIndex++;
|
||||
lineStartIndex = newLineIndex + 1;
|
||||
}
|
||||
const columnIndex = startIndex - lineStartIndex + startLength;
|
||||
handler(
|
||||
input.slice(startIndex + startLength, endIndex),
|
||||
lineIndex,
|
||||
columnIndex,
|
||||
startLength
|
||||
);
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports.forEachInlineCodeSpan = forEachInlineCodeSpan;
|
||||
|
||||
/**
|
||||
* Adds ellipsis to the left/right/middle of the specified text.
|
||||
*
|
||||
|
|
172
lib/markdownit.cjs
Normal file
172
lib/markdownit.cjs
Normal file
|
@ -0,0 +1,172 @@
|
|||
// @ts-check
|
||||
|
||||
"use strict";
|
||||
|
||||
const { newLineRe } = require("../helpers");
|
||||
|
||||
/**
|
||||
* @callback InlineCodeSpanCallback
|
||||
* @param {string} code Code content.
|
||||
* @param {number} lineIndex Line index (0-based).
|
||||
* @param {number} columnIndex Column index (0-based).
|
||||
* @param {number} ticks Count of backticks.
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calls the provided function for each inline code span's content.
|
||||
*
|
||||
* @param {string} input Markdown content.
|
||||
* @param {InlineCodeSpanCallback} handler Callback function taking (code,
|
||||
* lineIndex, columnIndex, ticks).
|
||||
* @returns {void}
|
||||
*/
|
||||
function forEachInlineCodeSpan(input, handler) {
|
||||
const backtickRe = /`+/g;
|
||||
let match = null;
|
||||
const backticksLengthAndIndex = [];
|
||||
while ((match = backtickRe.exec(input)) !== null) {
|
||||
backticksLengthAndIndex.push([ match[0].length, match.index ]);
|
||||
}
|
||||
const newLinesIndex = [];
|
||||
while ((match = newLineRe.exec(input)) !== null) {
|
||||
newLinesIndex.push(match.index);
|
||||
}
|
||||
let lineIndex = 0;
|
||||
let lineStartIndex = 0;
|
||||
let k = 0;
|
||||
for (let i = 0; i < backticksLengthAndIndex.length - 1; i++) {
|
||||
const [ startLength, startIndex ] = backticksLengthAndIndex[i];
|
||||
if ((startIndex === 0) || (input[startIndex - 1] !== "\\")) {
|
||||
for (let j = i + 1; j < backticksLengthAndIndex.length; j++) {
|
||||
const [ endLength, endIndex ] = backticksLengthAndIndex[j];
|
||||
if (startLength === endLength) {
|
||||
for (; k < newLinesIndex.length; k++) {
|
||||
const newLineIndex = newLinesIndex[k];
|
||||
if (startIndex < newLineIndex) {
|
||||
break;
|
||||
}
|
||||
lineIndex++;
|
||||
lineStartIndex = newLineIndex + 1;
|
||||
}
|
||||
const columnIndex = startIndex - lineStartIndex + startLength;
|
||||
handler(
|
||||
input.slice(startIndex + startLength, endIndex),
|
||||
lineIndex,
|
||||
columnIndex,
|
||||
startLength
|
||||
);
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze all freeze-able members of a token and its children.
|
||||
*
|
||||
* @param {import("./markdownlint").MarkdownItToken} token A markdown-it token.
|
||||
* @returns {void}
|
||||
*/
|
||||
function freezeToken(token) {
|
||||
if (token.attrs) {
|
||||
for (const attr of token.attrs) {
|
||||
Object.freeze(attr);
|
||||
}
|
||||
Object.freeze(token.attrs);
|
||||
}
|
||||
if (token.children) {
|
||||
for (const child of token.children) {
|
||||
freezeToken(child);
|
||||
}
|
||||
Object.freeze(token.children);
|
||||
}
|
||||
if (token.map) {
|
||||
Object.freeze(token.map);
|
||||
}
|
||||
Object.freeze(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotate tokens with line/lineNumber and freeze them.
|
||||
*
|
||||
* @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
|
||||
* @param {string[]} lines Lines of Markdown content.
|
||||
* @returns {void}
|
||||
*/
|
||||
function annotateAndFreezeTokens(tokens, lines) {
|
||||
let trMap = null;
|
||||
// eslint-disable-next-line jsdoc/valid-types
|
||||
/** @type import("./markdownlint").MarkdownItToken[] */
|
||||
// @ts-ignore
|
||||
const markdownItTokens = tokens;
|
||||
for (const token of markdownItTokens) {
|
||||
// Provide missing maps for table content
|
||||
if (token.type === "tr_open") {
|
||||
trMap = token.map;
|
||||
} else if (token.type === "tr_close") {
|
||||
trMap = null;
|
||||
}
|
||||
if (!token.map && trMap) {
|
||||
token.map = [ ...trMap ];
|
||||
}
|
||||
// Update token metadata
|
||||
if (token.map) {
|
||||
token.line = lines[token.map[0]];
|
||||
token.lineNumber = token.map[0] + 1;
|
||||
// Trim bottom of token to exclude whitespace lines
|
||||
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
||||
token.map[1]--;
|
||||
}
|
||||
}
|
||||
// Annotate children with lineNumber
|
||||
if (token.children) {
|
||||
const codeSpanExtraLines = [];
|
||||
if (token.children.some((child) => child.type === "code_inline")) {
|
||||
forEachInlineCodeSpan(token.content, (code) => {
|
||||
codeSpanExtraLines.push(code.split(newLineRe).length - 1);
|
||||
});
|
||||
}
|
||||
let lineNumber = token.lineNumber;
|
||||
for (const child of token.children) {
|
||||
child.lineNumber = lineNumber;
|
||||
child.line = lines[lineNumber - 1];
|
||||
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
|
||||
lineNumber++;
|
||||
} else if (child.type === "code_inline") {
|
||||
lineNumber += codeSpanExtraLines.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
freezeToken(token);
|
||||
}
|
||||
Object.freeze(tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of markdown-it tokens for the input.
|
||||
*
|
||||
* @param {import("./markdownlint").Plugin[]} markdownItPlugins Additional plugins.
|
||||
* @param {string} content Markdown content.
|
||||
* @param {string[]} lines Lines of Markdown content.
|
||||
* @returns {import("markdown-it").Token[]} Array of markdown-it tokens.
|
||||
*/
|
||||
function getMarkdownItTokens(markdownItPlugins, content, lines) {
|
||||
const markdownit = require("markdown-it");
|
||||
const md = markdownit({ "html": true });
|
||||
// const markdownItPlugins = options.markdownItPlugins || [];
|
||||
for (const plugin of markdownItPlugins) {
|
||||
// @ts-ignore
|
||||
md.use(...plugin);
|
||||
}
|
||||
const tokens = md.parse(content, {});
|
||||
annotateAndFreezeTokens(tokens, lines);
|
||||
return tokens;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
forEachInlineCodeSpan,
|
||||
getMarkdownItTokens
|
||||
};
|
6
lib/markdownlint.d.ts
vendored
6
lib/markdownlint.d.ts
vendored
|
@ -8,7 +8,7 @@ export = markdownlint;
|
|||
*/
|
||||
declare function markdownlint(options: Options | null, callback: LintCallback): void;
|
||||
declare namespace markdownlint {
|
||||
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, GetMarkdownIt, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, ConfigurationStrict, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
|
||||
export { markdownlintSync as sync, readConfig, readConfigSync, getVersion, promises, RuleFunction, RuleParams, MarkdownParsers, ParserMarkdownIt, ParserMicromark, MarkdownItToken, MicromarkTokenType, MicromarkToken, RuleOnError, RuleOnErrorInfo, RuleOnErrorFixInfo, Rule, Options, Plugin, ToStringCallback, LintResults, LintError, FixInfo, LintContentCallback, LintCallback, Configuration, ConfigurationStrict, RuleConfiguration, ConfigurationParser, ReadConfigCallback, ResolveConfigExtendsCallback };
|
||||
}
|
||||
/**
|
||||
* Lint specified Markdown files synchronously.
|
||||
|
@ -49,10 +49,6 @@ declare namespace promises {
|
|||
export { extendConfigPromise as extendConfig };
|
||||
export { readConfigPromise as readConfig };
|
||||
}
|
||||
/**
|
||||
* Function to get an instance of the markdown-it parser.
|
||||
*/
|
||||
type GetMarkdownIt = () => any;
|
||||
/**
|
||||
* Function to implement rule logic.
|
||||
*/
|
||||
|
|
|
@ -197,87 +197,6 @@ function removeFrontMatter(content, frontMatter) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze all freeze-able members of a token and its children.
|
||||
*
|
||||
* @param {MarkdownItToken} token A markdown-it token.
|
||||
* @returns {void}
|
||||
*/
|
||||
function freezeToken(token) {
|
||||
if (token.attrs) {
|
||||
for (const attr of token.attrs) {
|
||||
Object.freeze(attr);
|
||||
}
|
||||
Object.freeze(token.attrs);
|
||||
}
|
||||
if (token.children) {
|
||||
for (const child of token.children) {
|
||||
freezeToken(child);
|
||||
}
|
||||
Object.freeze(token.children);
|
||||
}
|
||||
if (token.map) {
|
||||
Object.freeze(token.map);
|
||||
}
|
||||
Object.freeze(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotate tokens with line/lineNumber and freeze them.
|
||||
*
|
||||
* @param {import("markdown-it").Token[]} tokens Array of markdown-it tokens.
|
||||
* @param {string[]} lines Lines of Markdown content.
|
||||
* @returns {void}
|
||||
*/
|
||||
function annotateAndFreezeTokens(tokens, lines) {
|
||||
let trMap = null;
|
||||
// eslint-disable-next-line jsdoc/valid-types
|
||||
/** @type MarkdownItToken[] */
|
||||
// @ts-ignore
|
||||
const markdownItTokens = tokens;
|
||||
for (const token of markdownItTokens) {
|
||||
// Provide missing maps for table content
|
||||
if (token.type === "tr_open") {
|
||||
trMap = token.map;
|
||||
} else if (token.type === "tr_close") {
|
||||
trMap = null;
|
||||
}
|
||||
if (!token.map && trMap) {
|
||||
token.map = [ ...trMap ];
|
||||
}
|
||||
// Update token metadata
|
||||
if (token.map) {
|
||||
token.line = lines[token.map[0]];
|
||||
token.lineNumber = token.map[0] + 1;
|
||||
// Trim bottom of token to exclude whitespace lines
|
||||
while (token.map[1] && !((lines[token.map[1] - 1] || "").trim())) {
|
||||
token.map[1]--;
|
||||
}
|
||||
}
|
||||
// Annotate children with lineNumber
|
||||
if (token.children) {
|
||||
const codeSpanExtraLines = [];
|
||||
if (token.children.some((child) => child.type === "code_inline")) {
|
||||
helpers.forEachInlineCodeSpan(token.content, (code) => {
|
||||
codeSpanExtraLines.push(code.split(helpers.newLineRe).length - 1);
|
||||
});
|
||||
}
|
||||
let lineNumber = token.lineNumber;
|
||||
for (const child of token.children) {
|
||||
child.lineNumber = lineNumber;
|
||||
child.line = lines[lineNumber - 1];
|
||||
if ((child.type === "softbreak") || (child.type === "hardbreak")) {
|
||||
lineNumber++;
|
||||
} else if (child.type === "code_inline") {
|
||||
lineNumber += codeSpanExtraLines.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
freezeToken(token);
|
||||
}
|
||||
Object.freeze(tokens);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map rule names/tags to canonical rule name.
|
||||
*
|
||||
|
@ -533,7 +452,7 @@ function getEnabledRulesPerLineNumber(
|
|||
* names.
|
||||
* @param {string} name Identifier for the content.
|
||||
* @param {string} content Markdown content.
|
||||
* @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
|
||||
* @param {Plugin[]} markdownItPlugins Additional plugins.
|
||||
* @param {Configuration} config Configuration object.
|
||||
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
||||
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
||||
|
@ -548,7 +467,7 @@ function lintContent(
|
|||
aliasToRuleNames,
|
||||
name,
|
||||
content,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -577,13 +496,15 @@ function lintContent(
|
|||
(rule) => (rule.parser === "markdownit") || (rule.parser === undefined)
|
||||
);
|
||||
// Parse content into parser tokens
|
||||
const markdownitTokens = needMarkdownItTokens ? getMarkdownIt().parse(content, {}) : [];
|
||||
const micromarkTokens = micromark.parse(content);
|
||||
// Hide the content of HTML comments from rules
|
||||
const preClearedContent = content;
|
||||
content = helpers.clearHtmlCommentText(content);
|
||||
// Parse content into lines and update markdown-it tokens
|
||||
// Parse content into lines and get markdown-it tokens
|
||||
const lines = content.split(helpers.newLineRe);
|
||||
annotateAndFreezeTokens(markdownitTokens, lines);
|
||||
const markdownitTokens = needMarkdownItTokens ?
|
||||
require("./markdownit.cjs").getMarkdownItTokens(markdownItPlugins, preClearedContent, lines) :
|
||||
[];
|
||||
// Create (frozen) parameters for rules
|
||||
/** @type {MarkdownParsers} */
|
||||
// @ts-ignore
|
||||
|
@ -836,7 +757,7 @@ function lintContent(
|
|||
* @param {Object.<string, string[]>} aliasToRuleNames Map of alias to rule
|
||||
* names.
|
||||
* @param {string} file Path of file to lint.
|
||||
* @param {GetMarkdownIt} getMarkdownIt Getter for instance of markdown-it.
|
||||
* @param {Plugin[]} markdownItPlugins Additional plugins.
|
||||
* @param {Configuration} config Configuration object.
|
||||
* @param {ConfigurationParser[] | null} configParsers Configuration parsers.
|
||||
* @param {RegExp | null} frontMatter Regular expression for front matter.
|
||||
|
@ -852,7 +773,7 @@ function lintFile(
|
|||
ruleList,
|
||||
aliasToRuleNames,
|
||||
file,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -872,7 +793,7 @@ function lintFile(
|
|||
aliasToRuleNames,
|
||||
file,
|
||||
content,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -939,16 +860,7 @@ function lintInput(options, synchronous, callback) {
|
|||
const resultVersion = (options.resultVersion === undefined) ?
|
||||
3 :
|
||||
options.resultVersion;
|
||||
const getMarkdownIt = () => {
|
||||
const markdownit = require("markdown-it");
|
||||
const md = markdownit({ "html": true });
|
||||
const markdownItPlugins = options.markdownItPlugins || [];
|
||||
for (const plugin of markdownItPlugins) {
|
||||
// @ts-ignore
|
||||
md.use(...plugin);
|
||||
}
|
||||
return md;
|
||||
};
|
||||
const markdownItPlugins = options.markdownItPlugins || [];
|
||||
const fs = options.fs || require("node:fs");
|
||||
const aliasToRuleNames = mapAliasToRuleNames(ruleList);
|
||||
const results = newResults(ruleList);
|
||||
|
@ -980,7 +892,7 @@ function lintInput(options, synchronous, callback) {
|
|||
ruleList,
|
||||
aliasToRuleNames,
|
||||
currentItem,
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -999,7 +911,7 @@ function lintInput(options, synchronous, callback) {
|
|||
aliasToRuleNames,
|
||||
currentItem,
|
||||
strings[currentItem] || "",
|
||||
getMarkdownIt,
|
||||
markdownItPlugins,
|
||||
config,
|
||||
configParsers,
|
||||
frontMatter,
|
||||
|
@ -1314,13 +1226,6 @@ module.exports = markdownlint;
|
|||
|
||||
// Type declarations
|
||||
|
||||
/**
|
||||
* Function to get an instance of the markdown-it parser.
|
||||
*
|
||||
* @callback GetMarkdownIt
|
||||
* @returns {import("markdown-it")}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Function to implement rule logic.
|
||||
*
|
||||
|
|
|
@ -7,6 +7,7 @@ const path = require("node:path");
|
|||
const test = require("ava").default;
|
||||
const helpers = require("../helpers");
|
||||
const { markdownlint } = require("../lib/markdownlint").promises;
|
||||
const { forEachInlineCodeSpan } = require("../lib/markdownit.cjs");
|
||||
|
||||
test("clearHtmlCommentTextValid", (t) => {
|
||||
t.plan(1);
|
||||
|
@ -306,7 +307,7 @@ test("forEachInlineCodeSpan", (t) => {
|
|||
];
|
||||
for (const testCase of testCases) {
|
||||
const { input, expecteds } = testCase;
|
||||
helpers.forEachInlineCodeSpan(input, (code, line, column, ticks) => {
|
||||
forEachInlineCodeSpan(input, (code, line, column, ticks) => {
|
||||
// @ts-ignore
|
||||
const [ expectedCode, expectedLine, expectedColumn, expectedTicks ] =
|
||||
expecteds.shift();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue