diff --git a/README.md b/README.md index 8b0c90c9a..950875b5b 100644 --- a/README.md +++ b/README.md @@ -567,28 +567,19 @@ This setting can be useful in the presence of (custom) rules that encounter unexpected syntax and fail. By enabling this option, the linting process is allowed to continue and report any violations that were found. -##### options.markdownItPlugins +##### options.markdownItFactory -Type: `Array` of `Array` of `Function` and plugin parameters +Type: `Function` returning ... -Specifies additional [`markdown-it` plugins][markdown-it-plugin] to use when -parsing input. Plugins can be used to support additional syntax and features for -advanced scenarios. *Deprecated.* +Specifies ... [`markdown-it` plugins][markdown-it-plugin] -[markdown-it-plugin]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin - -Each item in the top-level `Array` should be of the form: - -```javascript -[ require("markdown-it-plugin"), plugin_param_0, plugin_param_1, ... ] -``` - -> Note that `markdown-it` plugins are only called when the `markdown-it` parser +> Note that `markdown` is only used when ... the `markdown-it` parser > is invoked. None of the built-in rules use the `markdown-it` parser, so > `markdown-it` plugins will only be invoked when one or more > [custom rules][custom-rules] that use the `markdown-it` parser are present. [custom-rules]: #custom-rules +[markdown-it-plugin]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin ##### options.noInlineConfig diff --git a/example/typescript/type-check.ts b/example/typescript/type-check.ts index 725deade2..7f30ebb28 100644 --- a/example/typescript/type-check.ts +++ b/example/typescript/type-check.ts @@ -99,7 +99,7 @@ options = { "frontMatter": /---/, "handleRuleFailures": false, "noInlineConfig": false, - "markdownItPlugins": [ [ markdownItSub ] ] + "markdownItFactory": null }; assertLintResults(lintSync(options)); diff --git a/lib/markdownit.cjs b/lib/markdownit.cjs index 62db38262..29ac03973 100644 --- a/lib/markdownit.cjs +++ b/lib/markdownit.cjs @@ -152,21 +152,15 @@ function annotateAndFreezeTokens(tokens, lines) { /** * Gets an array of markdown-it tokens for the input. * - * @param {Plugin[]} markdownItPlugins Additional plugins. + * @param {Function} markdownItFactory ... * @param {string} content Markdown content. * @param {string[]} lines Lines of Markdown content. * @returns {MarkdownItToken} Array of markdown-it tokens. */ -function getMarkdownItTokens(markdownItPlugins, content, lines) { - const markdownit = require("markdown-it"); - const md = markdownit({ "html": true }); - for (const plugin of markdownItPlugins) { - // @ts-ignore - md.use(...plugin); - } - const tokens = md.parse(content, {}); +function getMarkdownItTokens(markdownItFactory, content, lines) { + const markdownIt = markdownItFactory(); + const tokens = markdownIt.parse(content, {}); annotateAndFreezeTokens(tokens, lines); - // @ts-ignore return tokens; }; diff --git a/lib/markdownlint.d.mts b/lib/markdownlint.d.mts index e4d01230e..7c2eff9b8 100644 --- a/lib/markdownlint.d.mts +++ b/lib/markdownlint.d.mts @@ -357,6 +357,10 @@ export type Rule = { */ function: RuleFunction; }; +/** + * ... + */ +export type MarkdownItFactory = () => any; /** * Configuration options. */ @@ -390,9 +394,9 @@ export type Options = { */ handleRuleFailures?: boolean; /** - * Additional plugins. + * ... */ - markdownItPlugins?: Plugin[]; + markdownItFactory?: MarkdownItFactory; /** * True to ignore HTML directives. */ diff --git a/lib/markdownlint.mjs b/lib/markdownlint.mjs index 8ae980196..44d2afdd1 100644 --- a/lib/markdownlint.mjs +++ b/lib/markdownlint.mjs @@ -446,7 +446,7 @@ function getEnabledRulesPerLineNumber( * names. * @param {string} name Identifier for the content. * @param {string} content Markdown content. - * @param {Plugin[]} markdownItPlugins Additional plugins. + * @param {MarkdownItFactory} markdownItFactory ... * @param {Configuration} config Configuration object. * @param {ConfigurationParser[] | null} configParsers Configuration parsers. * @param {RegExp | null} frontMatter Regular expression for front matter. @@ -461,7 +461,7 @@ function lintContent( aliasToRuleNames, name, content, - markdownItPlugins, + markdownItFactory, config, configParsers, frontMatter, @@ -501,7 +501,7 @@ function lintContent( // Parse content into lines and get markdown-it tokens const lines = content.split(helpers.newLineRe); const markdownitTokens = needMarkdownItTokens ? - dynamicRequire("./markdownit.cjs").getMarkdownItTokens(markdownItPlugins, preClearedContent, lines) : + dynamicRequire("./markdownit.cjs").getMarkdownItTokens(markdownItFactory, preClearedContent, lines) : []; // Create (frozen) parameters for rules /** @type {MarkdownParsers} */ @@ -753,10 +753,9 @@ function lintContent( * Lints a file containing Markdown content. * * @param {Rule[]} ruleList List of rules. - * @param {Object.} aliasToRuleNames Map of alias to rule - * names. + * @param {Object.} aliasToRuleNames Map of alias to rule names. * @param {string} file Path of file to lint. - * @param {Plugin[]} markdownItPlugins Additional plugins. + * @param {MarkdownItFactory} markdownItFactory ... * @param {Configuration} config Configuration object. * @param {ConfigurationParser[] | null} configParsers Configuration parsers. * @param {RegExp | null} frontMatter Regular expression for front matter. @@ -772,7 +771,7 @@ function lintFile( ruleList, aliasToRuleNames, file, - markdownItPlugins, + markdownItFactory, config, configParsers, frontMatter, @@ -792,7 +791,7 @@ function lintFile( aliasToRuleNames, file, content, - markdownItPlugins, + markdownItFactory, config, configParsers, frontMatter, @@ -859,7 +858,7 @@ function lintInput(options, synchronous, callback) { const resultVersion = (options.resultVersion === undefined) ? 3 : options.resultVersion; - const markdownItPlugins = options.markdownItPlugins || []; + const markdownItFactory = options.markdownItFactory || (() => { throw new Error("BAD"); }); const fs = options.fs || nodeFs; const aliasToRuleNames = mapAliasToRuleNames(ruleList); const results = newResults(ruleList); @@ -891,7 +890,7 @@ function lintInput(options, synchronous, callback) { ruleList, aliasToRuleNames, currentItem, - markdownItPlugins, + markdownItFactory, config, configParsers, frontMatter, @@ -910,7 +909,7 @@ function lintInput(options, synchronous, callback) { aliasToRuleNames, currentItem, strings[currentItem] || "", - markdownItPlugins, + markdownItFactory, config, configParsers, frontMatter, @@ -1475,6 +1474,13 @@ export function getVersion() { * @property {RuleFunction} function Rule implementation. */ +/** + * ... + * + * @callback MarkdownItFactory + * @returns {import("markdown-it").markdownit} + */ + /** * Configuration options. * @@ -1486,7 +1492,7 @@ export function getVersion() { * @property {RegExp | null} [frontMatter] Front matter pattern. * @property {Object} [fs] File system implementation. * @property {boolean} [handleRuleFailures] True to catch exceptions. - * @property {Plugin[]} [markdownItPlugins] Additional plugins. + * @property {MarkdownItFactory} [markdownItFactory] ... * @property {boolean} [noInlineConfig] True to ignore HTML directives. * @property {number} [resultVersion] Results object version. * @property {Object.} [strings] Strings to lint. diff --git a/package.json b/package.json index 92a6171ee..262e5f76e 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,6 @@ "node": ">=18" }, "dependencies": { - "markdown-it": "14.1.0", "micromark": "4.0.1", "micromark-extension-directive": "3.0.2", "micromark-extension-gfm-autolink-literal": "2.1.0", @@ -97,6 +96,7 @@ "js-yaml": "4.1.0", "json-schema-to-typescript": "15.0.3", "jsonc-parser": "3.3.1", + "markdown-it": "14.1.0", "markdown-it-for-inline": "2.0.1", "markdown-it-sub": "2.0.0", "markdown-it-sup": "2.0.0", diff --git a/test/markdownlint-test-custom-rules.mjs b/test/markdownlint-test-custom-rules.mjs index 9e640f1f0..11ecac2e5 100644 --- a/test/markdownlint-test-custom-rules.mjs +++ b/test/markdownlint-test-custom-rules.mjs @@ -4,6 +4,7 @@ import fs from "node:fs/promises"; import { createRequire } from "node:module"; const require = createRequire(import.meta.url); import test from "ava"; +import markdownIt from "markdown-it"; import { lint as lintAsync } from "markdownlint/async"; import { lint as lintPromise } from "markdownlint/promise"; import { lint as lintSync } from "markdownlint/sync"; @@ -376,6 +377,7 @@ test("customRulesNpmPackage", (t) => new Promise((resolve) => { require("./rules/npm"), require("markdownlint-rule-extended-ascii") ], + "markdownItFactory": () => markdownIt({ "html": true }), "strings": { "string": "# Text\n\n---\n\nText ✅\n" }, @@ -557,7 +559,9 @@ test("customRulesParserUndefined", (t) => { ], "strings": { "string": "# Heading\n" - } + }, + "markdownItFactory": () => markdownIt({ "html": true }) + }; return lintPromise(options).then(() => null); }); @@ -608,7 +612,8 @@ test("customRulesParserMarkdownIt", (t) => { ], "strings": { "string": "# Heading\n" - } + }, + "markdownItFactory": () => markdownIt({ "html": true }) }; return lintPromise(options).then(() => null); }); @@ -657,6 +662,7 @@ test("customRulesMarkdownItParamsTokensSameObject", (t) => { } } ], + "markdownItFactory": () => markdownIt({ "html": true }), "strings": { "string": "# Heading\n" } diff --git a/test/markdownlint-test.mjs b/test/markdownlint-test.mjs index 06fdd9927..9625c421b 100644 --- a/test/markdownlint-test.mjs +++ b/test/markdownlint-test.mjs @@ -680,7 +680,7 @@ test("readmeHeadings", (t) => new Promise((resolve) => { "##### options.frontMatter", "##### options.fs", "##### options.handleRuleFailures", - "##### options.markdownItPlugins", + "##### options.markdownItFactory", "##### options.noInlineConfig", "##### options.resultVersion", "##### options.strings", @@ -1104,6 +1104,16 @@ test("someCustomRulesHaveValidUrl", (t) => { } }); +function getMarkdownItFactory(markdownItPlugins) { + return () => { + const md = markdownIt({ "html": true }); + for (const markdownItPlugin of markdownItPlugins) { + // @ts-ignore + md.use(...markdownItPlugin); + } + return md; + } +} test("markdownItPluginsSingle", (t) => new Promise((resolve) => { t.plan(4); lintAsync({ @@ -1112,9 +1122,9 @@ test("markdownItPluginsSingle", (t) => new Promise((resolve) => { }, // Use a markdown-it custom rule so the markdown-it plugin will be run "customRules": customRules.anyBlockquote, - "markdownItPlugins": [ + "markdownItFactory": getMarkdownItFactory([ [ pluginInline, "check_text_plugin", "text", () => t.true(true) ] - ] + ]) }, function callback(err, actual) { t.falsy(err); const expected = { "string": [] }; @@ -1131,12 +1141,12 @@ test("markdownItPluginsMultiple", (t) => new Promise((resolve) => { }, // Use a markdown-it custom rule so the markdown-it plugin will be run "customRules": customRules.anyBlockquote, - "markdownItPlugins": [ + "markdownItFactory": getMarkdownItFactory([ [ pluginSub ], [ pluginSup ], [ pluginInline, "check_sub_plugin", "sub_open", () => t.true(true) ], [ pluginInline, "check_sup_plugin", "sup_open", () => t.true(true) ] - ] + ]) }, function callback(err, actual) { t.falsy(err); const expected = { "string": [] }; @@ -1151,9 +1161,9 @@ test("markdownItPluginsNoMarkdownIt", (t) => new Promise((resolve) => { "strings": { "string": "# Heading\n\nText\n" }, - "markdownItPlugins": [ + "markdownItFactory": getMarkdownItFactory([ [ pluginInline, "check_text_plugin", "text", () => t.fail() ] - ] + ]) }, function callback(err, actual) { t.falsy(err); const expected = { "string": [] }; @@ -1173,9 +1183,9 @@ test("markdownItPluginsUnusedUncalled", (t) => new Promise((resolve) => { }, // Use a markdown-it custom rule so the markdown-it plugin will be run "customRules": customRules.anyBlockquote, - "markdownItPlugins": [ + "markdownItFactory": getMarkdownItFactory([ [ pluginInline, "check_text_plugin", "text", () => t.fail() ] - ] + ]) }, function callback(err, actual) { t.falsy(err); const expected = { "string": [] }; @@ -1242,7 +1252,8 @@ test("token-map-spans", (t) => { } } ], - "files": [ "./test/token-map-spans.md" ] + "files": [ "./test/token-map-spans.md" ], + "markdownItFactory": () => markdownIt({ "html": true }) }; lintSync(options); });