From 5a0223a99f5eebf9db30465c247aef2bf14ea334 Mon Sep 17 00:00:00 2001 From: David Anson Date: Sat, 30 Sep 2023 23:04:59 -0700 Subject: [PATCH] wip --- markdownlint-cli2.js | 676 ++++++++++-------- resolve-and-require.js | 6 +- schema/markdownlint-cli2-config-schema.json | 10 + test/markdownlint-cli2-test-cases.js | 7 + test/markdownlint-cli2-test.js | 2 +- test/modulePaths/.markdownlint-cli2.jsonc | 18 + test/modulePaths/dir/about.md | 6 + test/modulePaths/dir/hr.md | 3 + test/modulePaths/dir/link.md | 3 + test/modulePaths/dir/subdir/info.md | 3 + test/modulePaths/viewme.md | 14 + test/resolve-and-require-test.js | 38 +- .../markdownlint-cli2-test-exec.js.md | 32 + .../markdownlint-cli2-test-exec.js.snap | Bin 14109 -> 14225 bytes .../markdownlint-cli2-test-main.js.md | 32 + .../markdownlint-cli2-test-main.js.snap | Bin 12331 -> 12450 bytes 16 files changed, 527 insertions(+), 323 deletions(-) create mode 100644 test/modulePaths/.markdownlint-cli2.jsonc create mode 100644 test/modulePaths/dir/about.md create mode 100644 test/modulePaths/dir/hr.md create mode 100644 test/modulePaths/dir/link.md create mode 100644 test/modulePaths/dir/subdir/info.md create mode 100644 test/modulePaths/viewme.md diff --git a/markdownlint-cli2.js b/markdownlint-cli2.js index eaa10927..e762f844 100755 --- a/markdownlint-cli2.js +++ b/markdownlint-cli2.js @@ -69,19 +69,19 @@ const readConfig = (fs, dir, name, otherwise) => { }; // Import or resolve/require a module ID with a custom directory in the path -const importOrRequireResolve = async (dir, id) => { +const importOrRequireResolve = async (dirs, id) => { if (typeof id === "string") { const expandId = markdownlintRuleHelpers.expandTildePath(id, require("node:os")); const errors = []; try { - return resolveAndRequire(dynamicRequire, expandId, dir); + return resolveAndRequire(dynamicRequire, expandId, dirs); } catch (error) { errors.push(error); } try { const fileUrlString = - pathToFileURL(pathDefault.resolve(dir, expandId)).toString(); + pathToFileURL(pathDefault.resolve(dirs[0], expandId)).toString(); // eslint-disable-next-line no-inline-comments const module = await import(/* webpackIgnore: true */ fileUrlString); return module.default; @@ -98,31 +98,38 @@ const importOrRequireResolve = async (dir, id) => { }; // Import or require an array of modules by ID -const importOrRequireIds = (dir, ids, noRequire) => ( - Promise.all(noRequire ? [] : ids.map((id) => importOrRequireResolve(dir, id))) +const importOrRequireIds = (dirs, ids, noRequire) => ( + Promise.all( + noRequire ? [] : ids.map((id) => importOrRequireResolve(dirs, id)) + ) ); // Import or require an array of modules by ID (preserving parameters) -const importOrRequireIdsAndParams = async (dir, idsAndParams, noRequire) => { +const importOrRequireIdsAndParams = async (dirs, idsAndParams, noRequire) => { if (noRequire) { return []; } const ids = idsAndParams.map((entry) => entry[0]); - const modules = await importOrRequireIds(dir, ids); + const modules = await importOrRequireIds(dirs, ids, noRequire); const modulesAndParams = idsAndParams. map((entry, i) => [ modules[i], ...entry.slice(1) ]); return modulesAndParams; }; // Import or require a JavaScript file and return the exported object -const importOrRequireConfig = (fs, dir, name, noRequire, otherwise) => { - const id = pathPosix.join(dir, name); - return () => fs.promises.access(id). - then( - () => (noRequire ? {} : importOrRequireResolve(dir, id)), - otherwise - ); -}; +const importOrRequireConfig = + (fs, dir, name, modulePaths, noRequire, otherwise) => { + const id = pathPosix.join(dir, name); + return () => fs.promises.access(id). + then( + () => ( + noRequire + ? {} + : importOrRequireResolve([ dir, ...modulePaths ], id) + ), + otherwise + ); + }; // Extend a config object if it has 'extends' property const getExtendedConfig = async (config, configPath, fs) => { @@ -155,7 +162,7 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => { basename.endsWith(".markdownlint-cli2.mjs") ) { options = await ( - importOrRequireConfig(fs, dirname, basename, noRequire, noop)() + importOrRequireConfig(fs, dirname, basename, [], noRequire, noop)() ); } else if ( basename.endsWith(".markdownlint.jsonc") || @@ -171,7 +178,7 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => { basename.endsWith(".markdownlint.mjs") ) { config = await ( - importOrRequireConfig(fs, dirname, basename, noRequire, noop)() + importOrRequireConfig(fs, dirname, basename, [], noRequire, noop)() ); } else { throw new Error( @@ -272,129 +279,140 @@ $ markdownlint-cli2 "**/*.md" "#node_modules"` }; // Get (creating if necessary) and process a directory's info object -const getAndProcessDirInfo = - (fs, tasks, dirToDirInfo, dir, relativeDir, noRequire, allowPackageJson) => { - let dirInfo = dirToDirInfo[dir]; - if (!dirInfo) { - dirInfo = { - dir, - relativeDir, - "parent": null, - "files": [], - "markdownlintConfig": null, - "markdownlintOptions": null - }; - dirToDirInfo[dir] = dirInfo; - - // Load markdownlint-cli2 object(s) - const markdownlintCli2Jsonc = - pathPosix.join(dir, ".markdownlint-cli2.jsonc"); - const markdownlintCli2Yaml = - pathPosix.join(dir, ".markdownlint-cli2.yaml"); - const packageJson = pathPosix.join(dir, "package.json"); - tasks.push( - fs.promises.access(markdownlintCli2Jsonc). - then( - () => fs.promises. - readFile(markdownlintCli2Jsonc, utf8). - then( - (content) => getJsoncParse(). - then((jsoncParse) => jsoncParse(content)) - ), - () => fs.promises.access(markdownlintCli2Yaml). - then( - () => fs.promises. - readFile(markdownlintCli2Yaml, utf8). - then(yamlParse), +const getAndProcessDirInfo = ( + fs, + tasks, + dirToDirInfo, + dir, + relativeDir, + noRequire, + allowPackageJson +) => { + let dirInfo = dirToDirInfo[dir]; + if (!dirInfo) { + dirInfo = { + dir, + relativeDir, + "parent": null, + "files": [], + "markdownlintConfig": null, + "markdownlintOptions": null + }; + dirToDirInfo[dir] = dirInfo; + + // Load markdownlint-cli2 object(s) + const markdownlintCli2Jsonc = + pathPosix.join(dir, ".markdownlint-cli2.jsonc"); + const markdownlintCli2Yaml = + pathPosix.join(dir, ".markdownlint-cli2.yaml"); + const packageJson = pathPosix.join(dir, "package.json"); + tasks.push( + fs.promises.access(markdownlintCli2Jsonc). + then( + () => fs.promises. + readFile(markdownlintCli2Jsonc, utf8). + then( + (content) => getJsoncParse(). + then((jsoncParse) => jsoncParse(content)) + ), + () => fs.promises.access(markdownlintCli2Yaml). + then( + () => fs.promises. + readFile(markdownlintCli2Yaml, utf8). + then(yamlParse), + importOrRequireConfig( + fs, + dir, + ".markdownlint-cli2.cjs", + [], + noRequire, importOrRequireConfig( fs, dir, - ".markdownlint-cli2.cjs", + ".markdownlint-cli2.mjs", + [], noRequire, - importOrRequireConfig( - fs, - dir, - ".markdownlint-cli2.mjs", - noRequire, - () => (allowPackageJson - ? fs.promises.access(packageJson) - // eslint-disable-next-line prefer-promise-reject-errors - : Promise.reject() - ). - then( - () => fs.promises. - readFile(packageJson, utf8). - then( - (content) => getJsoncParse(). - then((jsoncParse) => jsoncParse(content)). - then((obj) => obj[packageName]) - ), - noop - ) - ) + () => (allowPackageJson + ? fs.promises.access(packageJson) + // eslint-disable-next-line prefer-promise-reject-errors + : Promise.reject() + ). + then( + () => fs.promises. + readFile(packageJson, utf8). + then( + (content) => getJsoncParse(). + then((jsoncParse) => jsoncParse(content)). + then((obj) => obj[packageName]) + ), + noop + ) ) ) - ). - then((options) => { - dirInfo.markdownlintOptions = options; - return options && - options.config && - getExtendedConfig( - options.config, - // Just needs to identify a file in the right directory - markdownlintCli2Jsonc, - fs - ). - then((config) => { - options.config = config; - }); - }) - ); + ) + ). + then((options) => { + dirInfo.markdownlintOptions = options; + return options && + options.config && + getExtendedConfig( + options.config, + // Just needs to identify a file in the right directory + markdownlintCli2Jsonc, + fs + ). + then((config) => { + options.config = config; + }); + }) + ); - // Load markdownlint object(s) - const readConfigs = + // Load markdownlint object(s) + const readConfigs = + readConfig( + fs, + dir, + ".markdownlint.jsonc", readConfig( fs, dir, - ".markdownlint.jsonc", + ".markdownlint.json", readConfig( fs, dir, - ".markdownlint.json", + ".markdownlint.yaml", readConfig( fs, dir, - ".markdownlint.yaml", - readConfig( + ".markdownlint.yml", + importOrRequireConfig( fs, dir, - ".markdownlint.yml", + ".markdownlint.cjs", + [], + noRequire, importOrRequireConfig( fs, dir, - ".markdownlint.cjs", + ".markdownlint.mjs", + [], noRequire, - importOrRequireConfig( - fs, - dir, - ".markdownlint.mjs", - noRequire, - noop - ) + noop ) ) ) ) - ); - tasks.push( - readConfigs(). - then((config) => { - dirInfo.markdownlintConfig = config; - }) + ) ); - } - return dirInfo; - }; + tasks.push( + readConfigs(). + then((config) => { + dirInfo.markdownlintConfig = config; + }) + ); + } + return dirInfo; +}; // Get base markdownlint-cli2 options object const getBaseOptions = async ( @@ -448,86 +466,97 @@ const getBaseOptions = async ( }; // Enumerate files from globs and build directory infos -const enumerateFiles = - // eslint-disable-next-line max-len - async (fs, baseDirSystem, baseDir, globPatterns, dirToDirInfo, noErrors, noRequire) => { - const tasks = []; - const globbyOptions = { - "absolute": true, - "cwd": baseDir, - "dot": true, - "expandDirectories": false, - fs - }; - if (noErrors) { - globbyOptions.suppressErrors = true; - } - // Special-case literal files - const literalFiles = []; - const filteredGlobPatterns = globPatterns.filter( - (globPattern) => { - if (globPattern.startsWith(":")) { - literalFiles.push( - posixPath(pathDefault.resolve(baseDirSystem, globPattern.slice(1))) - ); - return false; - } - return true; +const enumerateFiles = async ( + fs, + baseDirSystem, + baseDir, + globPatterns, + dirToDirInfo, + noErrors, + noRequire +) => { + const tasks = []; + const globbyOptions = { + "absolute": true, + "cwd": baseDir, + "dot": true, + "expandDirectories": false, + fs + }; + if (noErrors) { + globbyOptions.suppressErrors = true; + } + // Special-case literal files + const literalFiles = []; + const filteredGlobPatterns = globPatterns.filter( + (globPattern) => { + if (globPattern.startsWith(":")) { + literalFiles.push( + posixPath(pathDefault.resolve(baseDirSystem, globPattern.slice(1))) + ); + return false; } - ).map((globPattern) => globPattern.replace(/^\\:/u, ":")); - const baseMarkdownlintOptions = dirToDirInfo[baseDir].markdownlintOptions; - const globsForIgnore = - (baseMarkdownlintOptions.globs || []). - filter((glob) => glob.startsWith("!")); - const filteredLiteralFiles = - ((literalFiles.length > 0) && (globsForIgnore.length > 0)) - ? removeIgnoredFiles(baseDir, literalFiles, globsForIgnore) - : literalFiles; - // Manually expand directories to avoid globby call to dir-glob.sync - const expandedDirectories = await Promise.all( - filteredGlobPatterns.map((globPattern) => { - const barePattern = - globPattern.startsWith("!") - ? globPattern.slice(1) - : globPattern; - const globPath = ( - pathPosix.isAbsolute(barePattern) || - pathDefault.isAbsolute(barePattern) - ) - ? barePattern - : pathPosix.join(baseDir, barePattern); - return fs.promises.stat(globPath). - then((stats) => (stats.isDirectory() - ? pathPosix.join(globPattern, "**") - : globPattern)). - catch(() => globPattern); - }) - ); - // Process glob patterns - // eslint-disable-next-line no-inline-comments - const { globby } = await import(/* webpackMode: "eager" */ "globby"); - const files = [ - ...await globby(expandedDirectories, globbyOptions), - ...filteredLiteralFiles - ]; - for (const file of files) { - const dir = pathPosix.dirname(file); - const dirInfo = getAndProcessDirInfo( - fs, - tasks, - dirToDirInfo, - dir, - null, - noRequire, - false - ); - dirInfo.files.push(file); + return true; } - await Promise.all(tasks); - }; + ).map((globPattern) => globPattern.replace(/^\\:/u, ":")); + const baseMarkdownlintOptions = dirToDirInfo[baseDir].markdownlintOptions; + const globsForIgnore = + (baseMarkdownlintOptions.globs || []). + filter((glob) => glob.startsWith("!")); + const filteredLiteralFiles = + ((literalFiles.length > 0) && (globsForIgnore.length > 0)) + ? removeIgnoredFiles(baseDir, literalFiles, globsForIgnore) + : literalFiles; + // Manually expand directories to avoid globby call to dir-glob.sync + const expandedDirectories = await Promise.all( + filteredGlobPatterns.map((globPattern) => { + const barePattern = + globPattern.startsWith("!") + ? globPattern.slice(1) + : globPattern; + const globPath = ( + pathPosix.isAbsolute(barePattern) || + pathDefault.isAbsolute(barePattern) + ) + ? barePattern + : pathPosix.join(baseDir, barePattern); + return fs.promises.stat(globPath). + then((stats) => (stats.isDirectory() + ? pathPosix.join(globPattern, "**") + : globPattern)). + catch(() => globPattern); + }) + ); + // Process glob patterns + // eslint-disable-next-line no-inline-comments + const { globby } = await import(/* webpackMode: "eager" */ "globby"); + const files = [ + ...await globby(expandedDirectories, globbyOptions), + ...filteredLiteralFiles + ]; + for (const file of files) { + const dir = pathPosix.dirname(file); + const dirInfo = getAndProcessDirInfo( + fs, + tasks, + dirToDirInfo, + dir, + null, + noRequire, + false + ); + dirInfo.files.push(file); + } + await Promise.all(tasks); +}; // Enumerate (possibly missing) parent directories and update directory infos -const enumerateParents = async (fs, baseDir, dirToDirInfo, noRequire) => { +const enumerateParents = async ( + fs, + baseDir, + dirToDirInfo, + noRequire +) => { const tasks = []; // Create a lookup of baseDir and parents @@ -571,136 +600,144 @@ const enumerateParents = async (fs, baseDir, dirToDirInfo, noRequire) => { }; // Create directory info objects by enumerating file globs -const createDirInfos = - // eslint-disable-next-line max-len - async (fs, baseDirSystem, baseDir, globPatterns, dirToDirInfo, optionsOverride, noErrors, noRequire) => { - await enumerateFiles( - fs, - baseDirSystem, - baseDir, - globPatterns, - dirToDirInfo, - noErrors, - noRequire - ); - await enumerateParents( - fs, - baseDir, - dirToDirInfo, - noRequire - ); +const createDirInfos = async ( + fs, + baseDirSystem, + baseDir, + globPatterns, + modulePaths, + dirToDirInfo, + optionsOverride, + noErrors, + noRequire +) => { + await enumerateFiles( + fs, + baseDirSystem, + baseDir, + globPatterns, + dirToDirInfo, + noErrors, + noRequire + ); + await enumerateParents( + fs, + baseDir, + dirToDirInfo, + noRequire + ); - // Merge file lists with identical configuration - const dirs = Object.keys(dirToDirInfo); - dirs.sort((a, b) => b.length - a.length); - const dirInfos = []; - const noConfigDirInfo = - // eslint-disable-next-line unicorn/consistent-function-scoping - (dirInfo) => ( - dirInfo.parent && - !dirInfo.markdownlintConfig && - !dirInfo.markdownlintOptions - ); - const tasks = []; - for (const dir of dirs) { - const dirInfo = dirToDirInfo[dir]; - if (noConfigDirInfo(dirInfo)) { - if (dirInfo.parent) { - appendToArray(dirInfo.parent.files, dirInfo.files); - } - dirToDirInfo[dir] = null; - } else { - const { markdownlintOptions, relativeDir } = dirInfo; - if (markdownlintOptions && markdownlintOptions.customRules) { - tasks.push( - importOrRequireIds( - relativeDir || dir, - markdownlintOptions.customRules, - noRequire - ).then((customRules) => { - // Expand nested arrays (for packages that export multiple rules) - markdownlintOptions.customRules = customRules.flat(); - }) - ); - } - if (markdownlintOptions && markdownlintOptions.markdownItPlugins) { - tasks.push( - importOrRequireIdsAndParams( - relativeDir || dir, - markdownlintOptions.markdownItPlugins, - noRequire - ).then((markdownItPlugins) => { - markdownlintOptions.markdownItPlugins = markdownItPlugins; - }) - ); - } - dirInfos.push(dirInfo); + // Merge file lists with identical configuration + const dirs = Object.keys(dirToDirInfo); + dirs.sort((a, b) => b.length - a.length); + const dirInfos = []; + const noConfigDirInfo = + // eslint-disable-next-line unicorn/consistent-function-scoping + (dirInfo) => ( + dirInfo.parent && + !dirInfo.markdownlintConfig && + !dirInfo.markdownlintOptions + ); + const tasks = []; + for (const dir of dirs) { + const dirInfo = dirToDirInfo[dir]; + if (noConfigDirInfo(dirInfo)) { + if (dirInfo.parent) { + appendToArray(dirInfo.parent.files, dirInfo.files); } - } - await Promise.all(tasks); - for (const dirInfo of dirInfos) { - while (dirInfo.parent && !dirToDirInfo[dirInfo.parent.dir]) { - dirInfo.parent = dirInfo.parent.parent; + dirToDirInfo[dir] = null; + } else { + const { markdownlintOptions, relativeDir } = dirInfo; + if (markdownlintOptions && markdownlintOptions.customRules) { + tasks.push( + importOrRequireIds( + [ relativeDir || dir, ...modulePaths ], + markdownlintOptions.customRules, + noRequire + ).then((customRules) => { + // Expand nested arrays (for packages that export multiple rules) + markdownlintOptions.customRules = customRules.flat(); + }) + ); } + if (markdownlintOptions && markdownlintOptions.markdownItPlugins) { + tasks.push( + importOrRequireIdsAndParams( + [ relativeDir || dir, ...modulePaths ], + markdownlintOptions.markdownItPlugins, + noRequire + ).then((markdownItPlugins) => { + markdownlintOptions.markdownItPlugins = markdownItPlugins; + }) + ); + } + dirInfos.push(dirInfo); + } + } + await Promise.all(tasks); + for (const dirInfo of dirInfos) { + while (dirInfo.parent && !dirToDirInfo[dirInfo.parent.dir]) { + dirInfo.parent = dirInfo.parent.parent; } + } - // Verify dirInfos is simplified - // if ( - // dirInfos.filter( - // (di) => di.parent && !dirInfos.includes(di.parent) - // ).length > 0 - // ) { - // throw new Error("Extra parent"); - // } - // if ( - // dirInfos.filter( - // (di) => !di.parent && (di.dir !== baseDir) - // ).length > 0 - // ) { - // throw new Error("Missing parent"); - // } - // if ( - // dirInfos.filter( - // (di) => di.parent && - // !((di.markdownlintConfig ? 1 : 0) ^ (di.markdownlintOptions ? 1 : 0)) - // ).length > 0 - // ) { - // throw new Error("Missing object"); - // } - // if (dirInfos.filter((di) => di.dir === "/").length > 0) { - // throw new Error("Includes root"); - // } - - // Merge configuration by inheritance - for (const dirInfo of dirInfos) { - let markdownlintOptions = dirInfo.markdownlintOptions || {}; - let { markdownlintConfig } = dirInfo; - let parent = dirInfo; - // eslint-disable-next-line prefer-destructuring - while ((parent = parent.parent)) { - if (parent.markdownlintOptions) { - markdownlintOptions = mergeOptions( - parent.markdownlintOptions, - markdownlintOptions - ); - } - if ( - !markdownlintConfig && - parent.markdownlintConfig && - !markdownlintOptions.config - ) { - // eslint-disable-next-line prefer-destructuring - markdownlintConfig = parent.markdownlintConfig; - } + // Verify dirInfos is simplified + // if ( + // dirInfos.filter( + // (di) => di.parent && !dirInfos.includes(di.parent) + // ).length > 0 + // ) { + // throw new Error("Extra parent"); + // } + // if ( + // dirInfos.filter( + // (di) => !di.parent && (di.dir !== baseDir) + // ).length > 0 + // ) { + // throw new Error("Missing parent"); + // } + // if ( + // dirInfos.filter( + // (di) => di.parent && + // !((di.markdownlintConfig ? 1 : 0) ^ (di.markdownlintOptions ? 1 : 0)) + // ).length > 0 + // ) { + // throw new Error("Missing object"); + // } + // if (dirInfos.filter((di) => di.dir === "/").length > 0) { + // throw new Error("Includes root"); + // } + + // Merge configuration by inheritance + for (const dirInfo of dirInfos) { + let markdownlintOptions = dirInfo.markdownlintOptions || {}; + let { markdownlintConfig } = dirInfo; + let parent = dirInfo; + // eslint-disable-next-line prefer-destructuring + while ((parent = parent.parent)) { + if (parent.markdownlintOptions) { + markdownlintOptions = mergeOptions( + parent.markdownlintOptions, + markdownlintOptions + ); + } + if ( + !markdownlintConfig && + parent.markdownlintConfig && + !markdownlintOptions.config + ) { + // eslint-disable-next-line prefer-destructuring + markdownlintConfig = parent.markdownlintConfig; } - dirInfo.markdownlintOptions = mergeOptions( - markdownlintOptions, - optionsOverride - ); - dirInfo.markdownlintConfig = markdownlintConfig; } - return dirInfos; - }; + dirInfo.markdownlintOptions = mergeOptions( + markdownlintOptions, + optionsOverride + ); + dirInfo.markdownlintConfig = markdownlintConfig; + } + return dirInfos; +}; // Lint files in groups by shared configuration const lintFiles = async (fs, dirInfos, fileContents) => { @@ -820,6 +857,7 @@ const outputSummary = async ( relativeDir, summary, outputFormatters, + modulePaths, logMessage, logError ) => { @@ -832,8 +870,9 @@ const outputSummary = async ( logError }; const dir = relativeDir || baseDir; + const dirs = [ dir, ...modulePaths ]; const formattersAndParams = outputFormatters - ? await importOrRequireIdsAndParams(dir, outputFormatters) + ? await importOrRequireIdsAndParams(dirs, outputFormatters) : [ [ require("markdownlint-cli2-formatter-default") ] ]; await Promise.all(formattersAndParams.map((formatterAndParams) => { const [ formatter, ...formatterParams ] = formatterAndParams; @@ -937,12 +976,15 @@ const main = async (params) => { logMessage(`Finding: ${globPatterns.join(" ")}`); } // Create linting tasks + const modulePaths = (baseMarkdownlintOptions.modulePaths || []). + map((path) => pathDefault.resolve(baseDir, path)); const dirInfos = await createDirInfos( fs, baseDirSystem, baseDir, globPatterns, + modulePaths, dirToDirInfo, optionsOverride, noErrors, @@ -973,7 +1015,13 @@ const main = async (params) => { (optionsOverride && optionsOverride.outputFormatters) || baseMarkdownlintOptions.outputFormatters; const errorsPresent = await outputSummary( - baseDir, relativeDir, summary, outputFormatters, logMessage, logError + baseDir, + relativeDir, + summary, + outputFormatters, + modulePaths, + logMessage, + logError ); // Return result return errorsPresent ? 1 : 0; diff --git a/resolve-and-require.js b/resolve-and-require.js index 668157ac..3f169919 100644 --- a/resolve-and-require.js +++ b/resolve-and-require.js @@ -6,12 +6,12 @@ * Wrapper for calling Node's require.resolve/require with an additional path. * @param {Object} req Node's require implementation (or equivalent). * @param {String} id Package identifier to require. - * @param {String} dir Directory to include when resolving paths. + * @param {String[]} dirs Directories to include when resolving paths. * @returns {Object} Exported module content. */ -const resolveAndRequire = (req, id, dir) => { +const resolveAndRequire = (req, id, dirs) => { const resolvePaths = req.resolve.paths ? req.resolve.paths("") : []; - const paths = [ dir, ...resolvePaths ]; + const paths = [ ...dirs, ...resolvePaths ]; const resolved = req.resolve(id, { paths }); return req(resolved); }; diff --git a/schema/markdownlint-cli2-config-schema.json b/schema/markdownlint-cli2-config-schema.json index 48e7afb4..bd9e4109 100644 --- a/schema/markdownlint-cli2-config-schema.json +++ b/schema/markdownlint-cli2-config-schema.json @@ -74,6 +74,16 @@ "minItems": 1 } }, + "modulePaths": { + "description": "Additional paths to resolve module locations from (only valid at the root)", + "type": "array", + "default": [], + "items": { + "description": "Path to resolve module locations from", + "type": "string", + "minLength": 1 + } + }, "noInlineConfig": { "description": "Whether to disable support of HTML comments within Markdown content", "type": "boolean", diff --git a/test/markdownlint-cli2-test-cases.js b/test/markdownlint-cli2-test-cases.js index 5f1a093b..5bce13a5 100644 --- a/test/markdownlint-cli2-test-cases.js +++ b/test/markdownlint-cli2-test-cases.js @@ -1176,6 +1176,13 @@ const testCases = ({ "post": deleteDirectory }); + testCase({ + "name": "modulePaths", + "args": [ "**/*.md" ], + "exitCode": 1, + "usesRequire": true + }); + }; module.exports = testCases; diff --git a/test/markdownlint-cli2-test.js b/test/markdownlint-cli2-test.js index cfde663e..ec77b24e 100644 --- a/test/markdownlint-cli2-test.js +++ b/test/markdownlint-cli2-test.js @@ -111,7 +111,7 @@ test("validateMarkdownlintConfigSchema", async (t) => { }); test("validateMarkdownlintCli2ConfigSchema", async (t) => { - t.plan(84); + t.plan(85); // Validate schema const { addSchema, validate } = diff --git a/test/modulePaths/.markdownlint-cli2.jsonc b/test/modulePaths/.markdownlint-cli2.jsonc new file mode 100644 index 00000000..e93eb42f --- /dev/null +++ b/test/modulePaths/.markdownlint-cli2.jsonc @@ -0,0 +1,18 @@ +{ + "customRules": [ + "markdownlint-rule-sample-commonjs" + ], + "markdownItPlugins": [ + [ "custom-markdown-it-plugin" ] + ], + "outputFormatters": [ + [ "output-formatter-sample-commonjs" ] + ], + "modulePaths": [ + "../invalid", + "../customRules", + "../markdownItPlugins/module", + "../no-config", + "../outputFormatters-module" + ] +} diff --git a/test/modulePaths/dir/about.md b/test/modulePaths/dir/about.md new file mode 100644 index 00000000..56d31c78 --- /dev/null +++ b/test/modulePaths/dir/about.md @@ -0,0 +1,6 @@ +# About # + +Text text text +1. List +3. List +3. List diff --git a/test/modulePaths/dir/hr.md b/test/modulePaths/dir/hr.md new file mode 100644 index 00000000..7a141621 --- /dev/null +++ b/test/modulePaths/dir/hr.md @@ -0,0 +1,3 @@ +# hr + +--- diff --git a/test/modulePaths/dir/link.md b/test/modulePaths/dir/link.md new file mode 100644 index 00000000..88b7f3b6 --- /dev/null +++ b/test/modulePaths/dir/link.md @@ -0,0 +1,3 @@ +# Heading + +Text [ link ](https://example.com) diff --git a/test/modulePaths/dir/subdir/info.md b/test/modulePaths/dir/subdir/info.md new file mode 100644 index 00000000..fc9dba79 --- /dev/null +++ b/test/modulePaths/dir/subdir/info.md @@ -0,0 +1,3 @@ +## Information +Text ` code1` text `code2 ` text + diff --git a/test/modulePaths/viewme.md b/test/modulePaths/viewme.md new file mode 100644 index 00000000..d60ebf5a --- /dev/null +++ b/test/modulePaths/viewme.md @@ -0,0 +1,14 @@ +# Title + +> Tagline + + +# Description + +Text text text +Text text text +Text text text + +## Summary + +Text text text \ No newline at end of file diff --git a/test/resolve-and-require-test.js b/test/resolve-and-require-test.js index 96edff00..49038b38 100644 --- a/test/resolve-and-require-test.js +++ b/test/resolve-and-require-test.js @@ -12,7 +12,7 @@ test("built-in module", (t) => { t.plan(1); t.deepEqual( require("node:fs"), - resolveAndRequire(require, "fs", __dirname) + resolveAndRequire(require, "fs", [ __dirname ]) ); }); @@ -20,7 +20,7 @@ test("locally-installed module", (t) => { t.plan(1); t.deepEqual( require("markdownlint"), - resolveAndRequire(require, "markdownlint", __dirname) + resolveAndRequire(require, "markdownlint", [ __dirname ]) ); }); @@ -31,7 +31,7 @@ test("relative (to __dirname) path to module", (t) => { resolveAndRequire( require, "./customRules/node_modules/markdownlint-rule-sample-commonjs", - __dirname + [ __dirname ] ) ); }); @@ -48,7 +48,7 @@ test("module in alternate node_modules", (t) => { resolveAndRequire( require, "markdownlint-rule-sample-commonjs", - path.join(__dirname, "customRules") + [ path.join(__dirname, "customRules") ] ) ); }); @@ -67,7 +67,35 @@ test("module in alternate node_modules and no require.resolve.paths", (t) => { resolveAndRequire( require, "markdownlint-rule-sample-commonjs", - path.join(__dirname, "customRules") + [ path.join(__dirname, "customRules") ] + ) + ); +}); + +test("module local, relative, and in alternate node_modules", (t) => { + t.plan(3); + const dirs = [ + __dirname, + path.join(__dirname, "customRules") + ]; + t.deepEqual( + require("markdownlint"), + resolveAndRequire(require, "markdownlint", dirs) + ); + t.deepEqual( + require("./customRules/node_modules/markdownlint-rule-sample-commonjs"), + resolveAndRequire( + require, + "./customRules/node_modules/markdownlint-rule-sample-commonjs", + dirs + ) + ); + t.deepEqual( + require("./customRules/node_modules/markdownlint-rule-sample-commonjs"), + resolveAndRequire( + require, + "markdownlint-rule-sample-commonjs", + dirs ) ); }); diff --git a/test/snapshots/markdownlint-cli2-test-exec.js.md b/test/snapshots/markdownlint-cli2-test-exec.js.md index bc75c062..2e700e90 100644 --- a/test/snapshots/markdownlint-cli2-test-exec.js.md +++ b/test/snapshots/markdownlint-cli2-test-exec.js.md @@ -7190,3 +7190,35 @@ Generated by [AVA](https://avajs.dev). Summary: 2 error(s)␊ `, } + +## modulePaths (exec) + +> Snapshot 1 + + { + exitCode: 1, + formatterCodeQuality: '', + formatterJson: '', + formatterJunit: '', + formatterSarif: '', + stderr: `cjs: dir/about.md 1 MD021/no-multiple-space-closed-atx␊ + cjs: dir/about.md 4 MD032/blanks-around-lists␊ + cjs: dir/about.md 5 MD029/ol-prefix␊ + cjs: dir/hr.md 3 sample-rule-commonjs␊ + cjs: dir/subdir/info.md 1 MD022/blanks-around-headings/blanks-around-headers␊ + cjs: dir/subdir/info.md 1 MD041/first-line-heading/first-line-h1␊ + cjs: dir/subdir/info.md 2 MD038/no-space-in-code␊ + cjs: dir/subdir/info.md 2 MD038/no-space-in-code␊ + cjs: dir/subdir/info.md 4 MD012/no-multiple-blanks␊ + cjs: viewme.md 3 MD009/no-trailing-spaces␊ + cjs: viewme.md 5 MD012/no-multiple-blanks␊ + cjs: viewme.md 6 MD025/single-title/single-h1␊ + cjs: viewme.md 12 MD019/no-multiple-space-atx␊ + cjs: viewme.md 14 MD047/single-trailing-newline␊ + `, + stdout: `markdownlint-cli2 vX.Y.Z (markdownlint vX.Y.Z)␊ + Finding: **/*.md␊ + Linting: 5 file(s)␊ + Summary: 14 error(s)␊ + `, + } diff --git a/test/snapshots/markdownlint-cli2-test-exec.js.snap b/test/snapshots/markdownlint-cli2-test-exec.js.snap index ca23f4865928e881e9d21d7ab6916bee531e899d..dae4f4948083870da393ef661dd760a7d6efeffd 100644 GIT binary patch delta 13410 zcmaKy18^o?7w2Q!wmB2?i8Zlp+fL?*or!JRw(W_H2`9Fb&HH}eZtd1?ZT+flpT7Tl zZ{O~&>c01!lVulY&myEIM51hG=-_N&??U3n3I`6-as{0hlSmr5$7P;}SVs^r=K1|5 z-!%yoRBU^|D5S?Gj)fUmW3lXzx!AuKo9S)tZpKB$?fs~Q3I;BvjBoK74&K3-zGR7t zVSz$niPGzjfbl#6ikB)I=W^gk;_4axiLbq*!5J1nyw3kQw|QIh zcxe^U)Y0*DBmuaI%i2UfKhs-T^+i}a^YwY0oW5v_xd7zz<7jcZs5UTXZf?SFd_DSA+wHn{+%j9vQ0i8B%83V*VO^n8ny(l(jb|o*D$829x972H`04}ap*3SpLH{B|_7n5&% z6oI>v4L!&q$QLSf`T?(^-rv~S<_Z3y8^rqO#e};SuFjn1#Lm^)j}DI|=@tE`7H&y) zDohj2&Ws(i*;q!%M-FqMz^?~`x6~tlPZOhR7qgoEfS-66B#=XrSgSAVIZ(}(K&BqI zDDHPY%jpE(GQ~-dzpqdTN*seM*)gcg+QJFcvSC8mY2CrTw%1)|k-go~g7vgYnoML{ z=<^cDQQg(eYEda?P*R!X6Ml_>^)S2R-gPA(LN~hT@U8I(#-{@JW>nAQC9~ki0pw=+ z--NmFu1pt1`@Wg;JUNwm;^MtN0QZtPIggJ4`Z=)MEOU~WGjoNXo^`;FD4d5)aL=XH z8aG}1S4_w_DsH-o)v<3I^7~v;u!IG=s&d|X3UzVO7#%~tSVr{03C7as?}A~6`<<*h zuc@w&i|$*OwcbK&IXzdqRdUuek%ETBdD;E7HzIG}YQ0VB2tH7_*fdyHoarOgvQ9vg zUpuh&De=QN+6h7T0`d>Kb2+WE@EjA3>+v1!_UtRG0p8QQPnd)5BGkZG=8Ny@!dunZ zQ2+?)i#Z1*L~zOUGT2YJsN$FlYlmG>1u!a`bkpmhCUnk%g znZzu-g8MIP)fXCrk%^RQk8C%DsTz@6&23vDrc3?sW0Mb^Q<$A0P9N2lgOyC#l6FJx z*ur)cNWS|82)4uJk@g?dE~eb%9g+5vg3T&U5YBGqM$GA~h$t8aHs}5<@gvS|9d8Sm z;|W`!19XIAhjEm|@e|mABJL(>5zxTVu#?;jMlB>H@S;!-LE?#!ACstj5jxenq_en? zXesTp(!^nx8^DPRiEK?C!W1w>`O-|kjoy8F%(W2>Xa{%A0Hr>)$Yv^qH=jS1x-~be zFj;NDd#m%meHxq8uh?DmZTgOtCB`-PCXsye0WUaO3b+~yK(yxEGgAqAxGEq`WBzP} z@R#*va7;#cUA4|cIn@SPO1%>{7P8b9e)tAa9#rz-DOa-#IGhNW!&>_9 z>Hn(#*EoqPla~-JK8Bo3?3bZ!ES!xOmWE^euR%8!zeXJR)E@yQqHH0X2V@Uk!qEE1 z`k#j5P!So&^Z(K3zn%AL3-7}9vfw!;oYvmoX(mpHe7d1?9(Qg5K184uCnw6%6G>I`!t?+mltv%UgS@Y6&DIx5qG-Rs@C{5}pjJi0lCf1_=YTO8-8t?7xXn9G#4D zYbRy?GIITJOJ|bW*eAWl7~~`sdhRE+-`HcG$>Fi9>u59C^1dYC(qeBzZiKAA3(6MM z^UY&|W7qg|ZDWF1VsM>i;BO5SG$rJ#D7WD!so`-C^9}X#qT=do7K7OqRGwZ(TU%^LQ%IwJeJ^Xrpj@*Dq5v9vq@32 z@(FFFY>)Fsg-#&uh2MUfgYt^K&=Tb6@q&hQwhj4e$uobxbf@TQWw8Ks_ullocl-mf z(;5@*{O19i*slVn*eoV&dCWNiGUy!jgr`l)e1%+Cwo>XAX;L%igFO?$#as1UE@0j< zc`&LKL5|LItZj;O@n9wBVCC=f*zKC6K=&W*hsd3~8BIu=bl(R*d|}&fY%&!;IvgZU zJ1AnYPdL3177kO6>~5TZy)3Qj^M7GF-xMjJ9T3FMnrzID6@F#1?m2+%A)mR;kN7-L zT6`nLvjAP6xpBE~_SN^uSPa*gDa7{DrijwBsm2n`v6!;#!Jz47S5`J#y6PSlGPssI z_)y>_vc9`klCPy!y!=hXAOe+w!(bpGYR|o`8+XrFD@SOLEDgT}EaA8%Y?Rfh({$c2 z#oyj!?$wWE6833?VNPRtswuBt>24y{*PE3Y!jU69)N-Q>b4J2$NBxE!Y<~lyuo%L* zE)p%q?#C|S{`_jw3vAYC!cK6NVw?MAi|RG^Ktp&9DxXe(+-IscD7k4CxnfSKCq-XD ze^Ml#+>fUjD@^wc^vH{^7#Ik*tDQPw?HQ^_%K4;hX{p%q)Iz#*RZ zwpR{k`rXP&0shue%lf9eT-#>o22^k827bLt!!A3Exkscm{xmVHu39MTNg}R`3Kk&X zOu2Unau5AChU|XC#WZv^cxD{-SO=uI6gUx!#etdX5XHrw3`EF8OQNTMcX8zi~CU{7Xd5JmbCFZF2 z5v7i$tCUJNrk`MwOGcYSL@ODMQ6v_jOqNk1GF~VgF-HPP8!LP#0hkL3%p5?3whrec zs3hNUh;<&!W7x7VoVNJrK89+~FS-$hcM^@f5s7pLig3=rGbW&GMImd&!fT~MYoS5k z8S>$`2(=f;lo#MkDaP*6$k^LtuF=FT^&-%2><$xNFu1iYGE5uy=ZbW!F@E2u-gqw zB|aIZ2zBhc11O6t-q;bdwS;^dpnMx1rFlzy(f=yzoo3$asdN`Guxh`nmk% z&s2m?>#SJu-s5>Zl3AuX3IoN z(2|hph*bc{JvU>$Wp~!3Igx2E_fa`lh#^>p#;5rsp00B>b}dZp-px_JDD4pUe(K<= z;{Hv4BgiUHE>HJZrV`GIjpZrp&+ipA(#eduDNnkzY0-*Ptjg6I)2+%q3Ro*-Sldqk zlG^6i>Jxm@)!;o}Z>d$p>Ce0mUDE^>`80S7LXmHsZAnHTuV8L}2l!osvhDw{A2sv= zX_8uHXgBWR2v?-Cn9k#Ffdtd2S8 zNXumrG7T6v3GT|44`zinDwx6{=>_QnVvQQkz4D{N@(qmn?${^MhIerC$9wF0KD|;t z5%e8(Ts@Qe`~=H!?g&@%00_Z7a>&B*yHpkx8nf}!kdg5(NyW@SkXuOpx`{Q8+Sg#_ z1`xjJ3NKB!Z67r40a`ZW{Q0903``9EBnZqYuRzF?_8VV@@CTRRYUnQ#jT*ND(-|hW zy#ujtADh>mKjnEJeyC%vF6hj*)Xc)G8bew8OuYX%t7$pE+=tRRj+3rdh>v8#(IW?s z06O&T9!OiqPy0+FQ%UD5wu%>ADI1m#ia*R!iDsbK3OB;eQEhN#T$$(R5y=X(!7iJi z)pMaS_OojR$%llar9?(Rk>UjbEl~1>!k~7Caf47LiBo7IBxC+dkE7)0s$>Syd!_4( zCMsJWMw$}k-71@um&jV%h(}evea@y;#ql{S2zQfDWAf+N5G|@VIBN+vWRC7e<<8O9 z(ygi)OPgfFlj$XG7}uFhXJ>9`u8JmuJRix8y1zr^Zp^?#V-aXOPPKP@VK1?J%>U$r z47lt&@_ph1<9czax0CLjBT=$NiK3$E-Ypl^yRtq@3koX`%5fa?A-3}eH|BvbSN6K> zxv`pTY4#S7x*C*==_t^;1hHjE)yRS=5vKm!`AuaUzHppueu*cnMhiBNG$P- zz3+|i0JXQldj(huL*zVYT{?`$o_sbCKG$VJBp>ycDkb4)Hm=o?51W7Q{z}J!q3udX zl-cu2r!+&yqdE$o1z%65zAg{4Yj3zKJ3=oC^`QRy&>4LUcG@?d39)|T^N%iIx~p!9 z7^q=uXC>7}^XIl>@wp==S+k`m>0a>kVXb|Kz?F4sksUCzmLB%<<^^%>x#|Hwo7>%C zXB#;;Zc$5$P55Tz#?RiY2H!OKdYa6excG2Rkxvl%h>SLt28r2&RFQW?u|TS^1%Gm0sV zC2}I&WPr!tk*iwISU7_*7RBl!XsjTD>2gOwF={+2m1(jMFw`cBGi2{lPUf5NACy;k z#19s~f0}l1iF%n%; zgUD-FipDLEtW;Yt-v563=LN|At&o$q$>*;>&nC+UX z3HZU#+%;R3I%G`Ge>^DQT|enK8Ir%yqI8gZF(VBY@XU}G{<%wt8As&jlV{S5{E2+8 z%LNSGC%7q~%XD+}tkfOz=l7YJj^y>p!k8fFu7*EA1kQNYitE|L@k&>Q6D|$FwKWKJ zc3d;7yK#DD$0g|b6`aEZp0stYOD8puMWV{f_8UYioVFJvSR>!@x^I>B67FJJym@zX zve?;wz}c!yta3!3Qe(4)pVkVxgyV}lS^&{`Blu=V)(!1K;2IJ(aIJKU;hhmq4U68Q z(>xd-9@UqPTDCndmZaUu_WeDmTNhNwmY4c<+xx~gRGujHs_s6Uy_I(bUN|weATO9| zj#LIhY$3i$=)^BhIIrt6cy03^MKn*>TM~z z5_4N`g5J&#Tpm{bmJ$NeaR|0X#smyhy%#$|OPiCm0Re@tM)w5)5BF;nvtD(vS7wza zw#KcZ{-b!lp`JKb#~R4a@D~r2rqj_&232wqibaq?W1Q8EtKi1GljP3Jv_k0kL}J2p zHp!3%-$W(3KYemFYI6PO?x{@|`y;WYxYgmGZ6sZ#eD z<-w%75-5Tynj}pgH2|(>HAQ?$tuc5V)qg&0*)AcMz0*UQY@Pvd%iC#oJ->dQ@i}D& z@DxRUrpIpi^ZxS4lJpRU&CfB={~;>5VjAenReMu%{E`0}dBNP?^B=>$EDdo}-v!9I z{yufV#Zq$ms#$!BZw}J^&SGzw{*p#UkP9%5PvOjLAj_LWO1p zzgR(vf`3J!+*e)1w*GMF0@@iLRqJsjxZO zqi7O?f3RUS@L_`eCbxeL6Nm+a3WI9uA65^=L#woN2xA2GMMxknB4Dz&3pEJgAK-A= z0i(UGkol1LVxF`4F8*>ooB@9RU(`+c%}E_Zkm&t`4Xd7xY(M+~(<_j%7^hY+NHA#S zm!FtX6uKW+ix>*aKM;(@wvvFmi=lk~2O1*+2U4hE29z(x!Lf=W9ba@%2jRbnv>lFg z!xMn`=Wce5GIq%vrA?x=$-+>=P!5E`>g~d#9Hk*h!M_Lu{zt(H`(i_z%ya+07*s{ z`nTndPbjk*noyx&Aq}w-24P8VEb|FPe( z0v6DP#*z=cMF55AAL6A!xE%aDh{Le{0k-5fVPh{2Bp4)^n5SR9&9%(Nt6=3nNOFZ< zJo2N^{X;V@-Ci@G)`}+|JYOVjHZMq0iz{1SgESWiIEaXP zP_eYH4#GS}kVcR|BgmO0Xe3jICa(<0UJp@#7JHtmFS{1?b=i0A+gMKxE>h zu&X-MZ^wA;r^ASZeA Y!WSBRDvs(u4bZcvtR;?SbiBgy*)Vz?pJl&#Vc^IO>~G> zY+Yj3nG!Xcnh?!5sM4>E_n(!Z2V#_?!6&_gIboDXqtG!|DU>czszjRz=Xt9lrTr}= z!;DY~7lX?xk4B}vEBQg9#Q0F0$!TP5@z*AQ{0ii7cw^?qZt~s3zJl}cr>}e40Qr3o z&*pxa`w3CJ>8Oat(?B_Q66O>|OXlU^^TjGlIr=8ap3}CZ#(ei&y|U05EbzfiW4VRU z47rO=8##HCry&ly)^1;tBPI+$+8At^)oW|c(5=Uews(g8?+&+dUflb;raL0_+V@Qh za%QDrs^0R3xF3z-KYoQSp~j7M@;heGO4O4Mtl`0G8$QZqgdVLUq4%#FMqjyUFc(D) z+vfst4=@hy@=}yt4;W8@m=J8$qn(7i!uR1u;YMv#_p3X}aw?fLdFxDh!F1Dw%dYub zqGh6GItg1cUOAZMvKR^&v#zC6GaeKTJQ+CY`NVP|+Y1g8##!1#OsWXB6!R{?r)!T& zc_Y2w4etGs(U;W9hlaz$#1{-f8IoUaVwG2N-L}W*G&@VoOGYf9nHOFivaJUmtPz2k zCqC7V444fcSCO7R62!&!tx))=>%yM0P%+`@Uz@?7&*1e8^=Dq158VTGD*Ay?KD|qx z0ue@w-dE2=K3jn^uUDQBD|aW#4OX$Z9AV1niAHYwL~>AGXl~obfv(t zHIwBSq#h7d2-mBCJl%EW2`*NwM`aeQ(9f99O^I(W9=)UC%dSAbkm(b|EQGMG^k5O( z$84Wc)e|#{gwdm)DcI2fce{!?i_1zLHY+D$N$U}?qg@HbFc5gt!i-cdZcd^rMTeFKgAbyMv7?$Nz4Ma_OBV&qrl?3%#iC8@oQAU*&W5b+N5mk1a zmk|?5OG^g)bQY)X3wVqX3-Qk1>3XQE3VFN*3LJ#G=lU6Hsb+v4qk&&eV0~{av-7)B z5-Q#&nN4EEbhE}Er!!SSM+4R5kSkC4OJHkkX2MBP(^mTir|b6>a)920$`yEbhAkU4gCAbN z^$$6_-e97(eI|%DNj;WoA?(_>t4s1fN`r!waA%2}CxOYUlAPy}w+HF~5iS*H_y z#JJ(euJvL*F`4>*Ja&GgjTs4XE>+h-Z=hIBi2dth^Rsh4lErMXp~sl<$H$iR>k7F;B(p+OfCoU&qdm3|$5My8SrP`3EZ%bN*gZ0t5t^Mq8CPVp40v%oS>8ft*YG{zkIN9$ zlKvf-%YNP1{r+~dt->Npw!-gs3fuwjF&|@mc3``ch_269+ww2nm>@OTp95BLyDUwa z5a|Nhm=et4_OL>pezCw`yOJTW~cE)ZQKw=q{^pd0=%MxQ9P||JWuEs%KFV)I8#WW=o}%L&sfYM zZ})CJ6bAxl?Pr?v!!Mh8iXW#P0WlE-#~ma zK|aM0g-0P%4g-?inZ=}RBTqN!C@7jN1X2cn0zyV)&6gtWE^YX--q_*gfmhdAStHfw zXFe6+D{vI`RKa^ETfpF$?P`=8Bp;a77P;az0FBg6Os_s2 z)25l{zPr@Yseo~Git4SPs!>d6Nffr4vtwKkWzepKRFd(r4oI#WmjyeWf>JGOdK&hy zJN4nXHm|R6cFh(l?Tc85#{@fF(;TAm9|Y}RrPPQ7g^N{gte+)#pU;E zgeIc498l464T}~(gqf_jCymbMi{J)hsW$_?(x?avu_*Iap=--qRGacjr`i|n$9?mV zKq%*#K`eE}Vw3#UQnZcCb76GF{z}@$dR8r3gzM@#y#UeE-~w5G*}!p-a{t`WCdDRa z3>R!*Gb}5T>zO}m#%GM>P_^OUaV=xy)K33xx2T%8jja_wi=8fuN>a0F(XzhjX01?@ zk}ZnbM*O{;YNItJl{Vh3kJmMC+b;>SYLl1^!}J@DOUAoU$^6;!y0CPdws1w2)l~Ar zaX>5^WD}6|o)=MpPpe`zX`cXF$A<=+fUTL1JqXHAQ8~%P;LNRq6miX}XBD!G35;_& z$*Y6Xg}0mxq{Ur(%1!wggo6F$Uaepsp6EooK83H|)818|jgfspr~vW7re)jCb^V7l z(Wq}#!RV%$bkg8YY{4S(GDZcAK)VvXl*VQ#4u4=JOu)jqG`Qw?9G>QiMqHjU2Rm8D z?J;(qFZH%Ji*`?kW+wv>jB#5d- zyE|YX?FrnbkhT?KYi5*-=;+|MY4EP`ndXwbSDDCTSr($fyIcu9y-`KYVz5`w{AG(u zU5#6!TtQLesqgIcrdPujn7jg^-!^nB#33?GVl~_GLPLoVJ-m>Cjnv>M*dOd&3yFRv zaV@a0XsFZ=#Jt7x#*A|>bxZf&6y?9=<%qy^LCiAxi3MyGUMF?5_H>Fs>XrD6K)Hr3 z1syA(Th}2o z;!Zy*!FUoFSFy8)Q$iujN9_plt`>q(l)MOfcaQ!CZ~d2!YztD?6g_7c^xcPb5f9*$ z+*rx~+Syn~eka!^&~~=JBs1OJ;~s-!&XE-(t=&qHNFqM)$$2Ctsi= z0M%V2+r;HFvIe$3H43Ho5?(zVF}TYhJl4jT#fO?0yxpGxW}x`}3Dga9quv9nB^$_@ zjdvWL%1{qC4B0zO+5;^tfxAF(9$4lU3pzyf7_&cP@kGt7QEbv3c{4UYF_SqB4%i0T zn{Z})xUe6|gE=u-+0asj&{8H*FsEhq{1@gXCM}u5D`q$NGw{UhXJ*JOPD>WG6A;1{ zWa3vY;w2|W&Cx71NN3&(YbFW@0rH8i0_kc(bSy(kMq!k`f?%FxU6sJZ+VMn+<(0?| zdE+7}2w8N{shCk4$MqP*c9v3nK8qwh5?>WG+|+UU65MaX0|oj6adXGs!erO#F}Y^b zV9|?>XAv&b;$A}({43YEVjr#gXto~YzN@F*({za%1e{SKi^&}XSt`SF8HQ(#I8K%^ z6s&Wibl#3kQZI|j}l@RmsxslUx4 zmJ5F>sRfHVL3SAmBo#u?Ep`CtAOokQRNx>|oXLM=O;jQdHK;e4VWZI&#*ZFc2G||a zmZk1z=2j*NEon(@NjXbV+Gp~(Bn@EbvS(*?G5^+K-1w6vujU4{;PVWkm=HY{m!4xe zo03g(`F?SB5P_I-z16o@3zE{XEff4pNp6RRLX#}oP2(b)N_Ta7{FL zSwnG`-BagXp^=|w#`AK5*yj))d6?WupH)VqV&)Pa`rcQ=rdHc9NucL5Nt;=X_f_Ac z`6j9;Etwm?Uw;ND3_Vff*8X|)IKbY3MX2@_s(TD)FGSqEez4D@x6DB>z%>s!^l88z zZ(L7(44E%0{tXncmY=wo`ze*l5-;n#Rlz)X92Gk;Cj0+7a$8jL?l9&|u zHUqd_ILBw^b3bi+j)V%bHj(Ri?jh56_BdKKx0;5@99sfztQP0+kqf05PrnJB_Y&Z5 zr7`LOQ%@>=#%c(h4aNPzMeM!C29nS_UK+uOvdx`n8>G1pbLcGa%vM)oi(_KIFC)hd zT|IuYajTgGOByB8U~^rU>hS#vr-HHpn@7VYo4t{9cy_A}HMBC?knH5GLJFtER9&B#?Imw#H z6Qy{yO~@o0RD(0%QeC>)_UX9Lbl<*qD{%SLJ*T($uJ`STmTgAea%_g< zeTETOkH{|o{=mY}6H0WJ!qogHs`>%|nf+{MK?3ejIq~h+S6+v#tg_pK8f7sjhd<@_ zF%fK|6FLREQElW33k zWt6jA5cYt~G3FkLHx@!l^)OBo=@~w#SX@>75=E~pkD_Hy*_Nub6l%`o5o~_j`MK}k z4`Ue@<+gR*{#G^cLB@4laZ&%4)bXcZH%r_WpO3Da#vOk0b-Dc@1Ra<1VL1ix zdKY-YEkt5d=iy(qIA1j;fayNJESFsH^Q!O#3wqUKV{vww8Ly*CLfh)yW+C0ZQC0P& zvP7jcwWYc~uHr_Xe`Dm|zEBBJ$2ML~K&NH{rEHL-)&m8lODLfUN1_fmxG7IKN!4Vz z#LnJN0MN36zBM?}@__<>9+cFY5G79z#CvM6AWI+G>Wy_$+euH=VbARdyCZyBM|)og z4Ar4HC-FIUXcG%KmN>tAL6ulAQJf-mRKVYMtE-Ve;w(SUG(FTNfhs#NGr3E z?&mrEl0%>WK=Duyyt$Nc(vQOjHj4LbmRmiLk{8<%NiD@2OFX0>o8cg7C8XjysDX>Sio zbnC=w*;R8zf7(Z+zO%XJyZ5C8uUOJ&v;Ld~@-|Yw)S$KIdP+J)rAQ_|{ExZItA+Iz z7ipqR%>|54m(t=Vj6(sAG}ED)52A|7ry>bAx9+F7tgHcG&erPl)nZnbwR$8Y{Rc8r zArt+tq4(NWVywr3y`LHz$dRvxjy>!ckFNqM=JOK6onJn|WZ@^q`K?NJal~NWNPVXT z`OyY1;k7l`N^B{KPVqGtr+w-A}?p(>J6 z%_pC1C@JJ74DHn-yQJPTMmhgX1k?sRaj6*!q~=39+cS#vI9lzY<+r&;r-YO}E6i_Y zP4Td?=1E|3PD8ZG;&ekSGX~L>5`wft+EF{7*}D3`?*YXvSVGRhN)S&sCc`>c2V-AH zA5SL^X8Dhym~}d2{cN4ViG=G-3rY`8yl#naWgSYL5-9qHp*6L)Zy^632h>-p4lvz3 zQh8S(ke&Y+UWoDXO->1!@lBpZtMt6E1HD^ z?qaMRvIH*4x^)dox>b$)qA!jA{&tGlp!fa%Tg)@?nROX0(f{v7h3leT6%bcUF(zTq}!x8d=48p*S9HgS39#^@K~;flfkSzPU}K-h*I z`ySGWR5zM!l?a<bV47ocSD0PEYsnIBUH!wW>HC zZeDEJRx|t!bBppR&)U2{P=?*|Dd({VsQ$gM(lk-!TN$d1m^W8-uro}Y&8iG_EG8OG zqZZ*ZUKTX&o7{Tm;=K7!Mk}1cZIAoJk$C;R=+c_WAoapFJ=JS;MIV?H1+8vOi(`n#ckzAX%xu8E-tj>(5Zj#Dw#g7heyeMSzkbg3 z_z>CG`H)Y=K!l--vEI@3+M0@oNsD3u{E1vg(u0QCEBZ;vXe0&NXZ_}0ZI~=W9MB>(Lpy-3}vqy;hntHKFWn~drkZ~y?^Eq2? zm}%Cx=tkt80c&94`(kMGs^t2sJF(7!lRGYRF`mmbd%jp7V=7^cpkq;fR{nzvIZF2l zI?E@n{G5ewc7e3u=#WCz>q^sDE1+>dSu92z>2hB`4|~J52~gQLS*zXe^Gdzd=r?N; z4E{QD^6&S5LwHy3_+IYC6&wf0Oj14YU&kaz9~tl}yYb(2v~c8Xp|5nbKF!y<%2>^I zQMoegF8J&C49ngbkhoR6`_p|-+d|@P*hGa#yRK^B5FQhkg9Rh--KEu#8Ek4a)>eP6 z!N`frmApLjjdoc$s&j1|hl_{FA!rKXGGgP7l(I{@&sn%JPTX0g-{87=Q__RCBQ5eC zD4WJ(OCFz8oL#4fqB9zHM@8mJMlQvGgdln;ijAeY|5j*GHUjb!KPJC{fU@uXLG;5UrDs$+bEmYv@pLR;3H zeP2a<6(-CZn5)|tgH5j?HQ{)>1ik} z_}MN$sBIwaon?QTQNd&NQJ!E&jjT>CPE8uo;-U7&hR=^Xw6a_SSy+W4hX4pL3N%af8Ez`EJxkFF=SW(Bnit8&@|PS&1zGg(j=`r;Kk&{8<82+42=f-Xk5#eU3AHcyqEFPPmQ$n zvj2u&rB*Y!f0^UfwYxEpE`CeEcXR!87YZGUf zCJ7!m^p$ViUc2~1jE8yoh21Y_tsn4Lia&2!2255xx(4kh&+FhgEf@QS?3-iC!l8H6 z0nuC=$%9dX!F>9pj5T-pr8x5mm)wgl-kR)B1SJsX%INP)Lm1qwqJYU*R`SYV*rh%Y zZghbyCN9lQAQOrF7(^=eM}Dn1fkWgYFZuK%|A_`%yDT_*WzCquC}6{VYM&kJW8|oL zP2;Jc;xU_vBOmdpZHOWlS$0O9QNi(0jcF`^k+ovJQV8k=U zjNB75Lflg*q}j*Y*3YX^s$EY9+tVqaiD1ANif4~4z`o)o%(?8h zF9cOBu4NjTjo43pYc(z4E-KWiA7+&uIGbi;2a+MNi>cIm^yB?fsp3hhB5z0eQwM09 z04z5?B6}()3Dj;<25vnrw#HLa_j0#8UE;HD{-)c~o^&6!_j;yxHC2=2$ykoy$~ZdM z3#wNi9T**;+~55^DaNr^Wt>VOE{*)ZsKpvU*qng8FhIx zJD`kc4|B|?s%M=2*zM!|S+Ld2H+|`mXYuy1A8eE@oSNWzfzi0%6VJd~vMQ-o&?zN7yq(L^Hhrz(95Wo|erT)R)DE(riWk0ZNd>`*%$7UW4R~ zfV7!)Be=S|VH3aQrW!d=8`9_skA#sV#}p;^Oiz}8RB)H3N$P<5yN2Ih;1}qmfDsiR zEI2|jm@jCGVECUXl1Ong>8Zm|sKvZLgVZB_dUX^X121e6FskD6hEPQCZz+6!b6N<~ zCdAD=c;Y_K%-FD#e{N+vEuWz>bE0Q|)1Z87NbOY>JvCD>NVfeI<#|H~!gQXF^;dhp zhgtjCG_VrQJOYRln){uYgoTW5_1%w9A?-7W9-eEKg$nblASLFSst+$ TOuufL{16a2RPmr2f&lp+t)(Mp delta 13294 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 diff --git a/test/snapshots/markdownlint-cli2-test-main.js.md b/test/snapshots/markdownlint-cli2-test-main.js.md index 0b103a89..b5acede4 100644 --- a/test/snapshots/markdownlint-cli2-test-main.js.md +++ b/test/snapshots/markdownlint-cli2-test-main.js.md @@ -5265,3 +5265,35 @@ Generated by [AVA](https://avajs.dev). Summary: 2 error(s)␊ `, } + +## modulePaths (main) + +> Snapshot 1 + + { + exitCode: 1, + formatterCodeQuality: '', + formatterJson: '', + formatterJunit: '', + formatterSarif: '', + stderr: `cjs: dir/about.md 1 MD021/no-multiple-space-closed-atx␊ + cjs: dir/about.md 4 MD032/blanks-around-lists␊ + cjs: dir/about.md 5 MD029/ol-prefix␊ + cjs: dir/hr.md 3 sample-rule-commonjs␊ + cjs: dir/subdir/info.md 1 MD022/blanks-around-headings/blanks-around-headers␊ + cjs: dir/subdir/info.md 1 MD041/first-line-heading/first-line-h1␊ + cjs: dir/subdir/info.md 2 MD038/no-space-in-code␊ + cjs: dir/subdir/info.md 2 MD038/no-space-in-code␊ + cjs: dir/subdir/info.md 4 MD012/no-multiple-blanks␊ + cjs: viewme.md 3 MD009/no-trailing-spaces␊ + cjs: viewme.md 5 MD012/no-multiple-blanks␊ + cjs: viewme.md 6 MD025/single-title/single-h1␊ + cjs: viewme.md 12 MD019/no-multiple-space-atx␊ + cjs: viewme.md 14 MD047/single-trailing-newline␊ + `, + stdout: `markdownlint-cli2 vX.Y.Z (markdownlint vX.Y.Z)␊ + Finding: **/*.md␊ + Linting: 5 file(s)␊ + Summary: 14 error(s)␊ + `, + } diff --git a/test/snapshots/markdownlint-cli2-test-main.js.snap b/test/snapshots/markdownlint-cli2-test-main.js.snap index 60adceb8892b4797df486cc6e178da2811bcb20c..1aead3bc6483ccc28b2675ef9805929bebf557e0 100644 GIT binary patch literal 12450 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 D2(~RV literal 12331 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