markdownlint-cli2
Advanced tools
Comparing version 0.0.8 to 0.0.9
@@ -71,7 +71,7 @@ #!/usr/bin/env node | ||
// Main function | ||
const main = async (argv, logMessage, logError) => { | ||
// Output help for missing arguments | ||
// Process command-line arguments and return glob patterns | ||
const processArgv = (argv, logMessage) => { | ||
const globPatterns = argv.map((glob) => glob.replace(/^#/u, "!")); | ||
if (globPatterns.length === 0) { | ||
// Output help if missing arguments | ||
const { name, version, author, homepage } = require("./package.json"); | ||
@@ -115,3 +115,3 @@ /* eslint-disable max-len */ | ||
/* eslint-enable max-len */ | ||
return 1; | ||
return null; | ||
} else if ((globPatterns.length === 1) && (globPatterns[0] === ".")) { | ||
@@ -121,75 +121,85 @@ // Substitute a more reasonable pattern | ||
} | ||
return globPatterns; | ||
}; | ||
// Read base ignore globs to pass as globby patterns (best performance) | ||
const tasks = []; | ||
const dirToDirInfo = {}; | ||
const getAndProcessDirInfo = (dir, func) => { | ||
let dirInfo = dirToDirInfo[dir]; | ||
if (!dirInfo) { | ||
dirInfo = { | ||
// Get (creating if necessary) and process a directory's info object | ||
const getAndProcessDirInfo = (tasks, dirToDirInfo, dir, func) => { | ||
let dirInfo = dirToDirInfo[dir]; | ||
if (!dirInfo) { | ||
dirInfo = { | ||
dir, | ||
"parent": null, | ||
"files": [], | ||
"markdownlintConfig": null, | ||
"markdownlintOptions": null | ||
}; | ||
dirToDirInfo[dir] = dirInfo; | ||
// Load markdownlint-cli2 object(s) | ||
const markdownlintCli2Jsonc = path.join(dir, ".markdownlint-cli2.jsonc"); | ||
const markdownlintCli2Yaml = path.join(dir, ".markdownlint-cli2.yaml"); | ||
tasks.push( | ||
fs.access(markdownlintCli2Jsonc). | ||
then( | ||
() => fs.readFile(markdownlintCli2Jsonc, utf8).then(jsoncParse), | ||
() => fs.access(markdownlintCli2Yaml). | ||
then( | ||
() => fs.readFile(markdownlintCli2Yaml, utf8).then(yamlParse), | ||
requireConfig( | ||
dir, | ||
".markdownlint-cli2.js", | ||
() => null | ||
) | ||
) | ||
). | ||
then((options) => { | ||
dirInfo.markdownlintOptions = options; | ||
}) | ||
); | ||
// Load markdownlint object(s) | ||
const readConfigs = | ||
readConfig( | ||
dir, | ||
"parent": null, | ||
"files": [], | ||
"markdownlintConfig": null, | ||
"markdownlintOptions": null | ||
}; | ||
dirToDirInfo[dir] = dirInfo; | ||
const markdownlintCli2Jsonc = path.join(dir, ".markdownlint-cli2.jsonc"); | ||
const markdownlintCli2Yaml = path.join(dir, ".markdownlint-cli2.yaml"); | ||
tasks.push( | ||
fs.access(markdownlintCli2Jsonc). | ||
then( | ||
() => fs.readFile(markdownlintCli2Jsonc, utf8).then(jsoncParse), | ||
() => fs.access(markdownlintCli2Yaml). | ||
then( | ||
() => fs.readFile(markdownlintCli2Yaml, utf8).then(yamlParse), | ||
requireConfig( | ||
dir, | ||
".markdownlint-cli2.js", | ||
() => null | ||
) | ||
) | ||
). | ||
then((options) => { | ||
dirInfo.markdownlintOptions = options; | ||
}) | ||
); | ||
const readConfigs = | ||
".markdownlint.jsonc", | ||
readConfig( | ||
dir, | ||
".markdownlint.jsonc", | ||
".markdownlint.json", | ||
readConfig( | ||
dir, | ||
".markdownlint.json", | ||
".markdownlint.yaml", | ||
readConfig( | ||
dir, | ||
".markdownlint.yaml", | ||
readConfig( | ||
".markdownlint.yml", | ||
requireConfig( | ||
dir, | ||
".markdownlint.yml", | ||
requireConfig( | ||
dir, | ||
".markdownlint.js", | ||
() => null | ||
) | ||
".markdownlint.js", | ||
() => null | ||
) | ||
) | ||
) | ||
); | ||
tasks.push( | ||
readConfigs(). | ||
then((config) => { | ||
dirInfo.markdownlintConfig = config; | ||
}) | ||
) | ||
); | ||
} | ||
if (func) { | ||
func(dirInfo); | ||
} | ||
return dirInfo; | ||
}; | ||
getAndProcessDirInfo("."); | ||
tasks.push( | ||
readConfigs(). | ||
then((config) => { | ||
dirInfo.markdownlintConfig = config; | ||
}) | ||
); | ||
} | ||
if (func) { | ||
func(dirInfo); | ||
} | ||
return dirInfo; | ||
}; | ||
// Get base markdownlint-cli2 options object | ||
const getBaseOptions = async (globPatterns) => { | ||
const tasks = []; | ||
const dirToDirInfo = {}; | ||
getAndProcessDirInfo(tasks, dirToDirInfo, "."); | ||
await Promise.all(tasks); | ||
tasks.length = 0; | ||
const baseMarkdownlintOptions = dirToDirInfo["."].markdownlintOptions || {}; | ||
// Pass base ignore globs as globby patterns (best performance) | ||
const ignorePatterns = (baseMarkdownlintOptions.ignores || []). | ||
@@ -199,12 +209,15 @@ map(negateGlob); | ||
delete baseMarkdownlintOptions.ignores; | ||
const showProgress = !baseMarkdownlintOptions.noProgress; | ||
return { | ||
baseMarkdownlintOptions, | ||
dirToDirInfo | ||
}; | ||
}; | ||
// Enumerate files from globs and build directory info list | ||
if (showProgress) { | ||
logMessage(`Finding: ${globPatterns.join(" ")}`); | ||
} | ||
// Enumerate files from globs and build directory infos | ||
const enumerateFiles = async (globPatterns, dirToDirInfo) => { | ||
const tasks = []; | ||
for await (const file of globby.stream(globPatterns)) { | ||
// @ts-ignore | ||
const dir = path.dirname(file); | ||
getAndProcessDirInfo(dir, (dirInfo) => { | ||
getAndProcessDirInfo(tasks, dirToDirInfo, dir, (dirInfo) => { | ||
dirInfo.files.push(file); | ||
@@ -214,5 +227,7 @@ }); | ||
await Promise.all(tasks); | ||
tasks.length = 0; | ||
}; | ||
// Fill out directory info list with parent directories | ||
// Enumerate (possibly missing) parent directories and update directory infos | ||
const enumerateParents = async (dirToDirInfo) => { | ||
const tasks = []; | ||
for (let lastDirInfo of Object.values(dirToDirInfo)) { | ||
@@ -223,11 +238,17 @@ let { dir } = lastDirInfo; | ||
lastDir = dir; | ||
// eslint-disable-next-line no-loop-func | ||
lastDirInfo = getAndProcessDirInfo(dir, (dirInfo) => { | ||
lastDirInfo.parent = dirInfo; | ||
}); | ||
lastDirInfo = | ||
// eslint-disable-next-line no-loop-func | ||
getAndProcessDirInfo(tasks, dirToDirInfo, dir, (dirInfo) => { | ||
lastDirInfo.parent = dirInfo; | ||
}); | ||
} | ||
} | ||
await Promise.all(tasks); | ||
tasks.length = 0; | ||
}; | ||
// Create directory info objects by enumerating file globs | ||
const createDirInfos = async (globPatterns, dirToDirInfo) => { | ||
await enumerateFiles(globPatterns, dirToDirInfo); | ||
await enumerateParents(dirToDirInfo); | ||
// Merge file lists with identical configuration | ||
@@ -245,3 +266,3 @@ const dirs = Object.keys(dirToDirInfo); | ||
const dirInfo = dirToDirInfo[dir]; | ||
if ((dirInfo.files.length === 0) || noConfigDirInfo(dirInfo)) { | ||
if (noConfigDirInfo(dirInfo)) { | ||
if (dirInfo.parent) { | ||
@@ -252,2 +273,9 @@ appendToArray(dirInfo.parent.files, dirInfo.files); | ||
} else { | ||
const { markdownlintOptions } = dirInfo; | ||
if (markdownlintOptions) { | ||
markdownlintOptions.customRules = | ||
requireIds(dir, markdownlintOptions.customRules || []); | ||
markdownlintOptions.markdownItPlugins = | ||
requireIdsAndParams(dir, markdownlintOptions.markdownItPlugins || []); | ||
} | ||
dirInfos.push(dirInfo); | ||
@@ -263,5 +291,2 @@ } | ||
// Verify dirInfos is simplified | ||
// if (dirInfos.filter((di) => !di.files.length).length > 0) { | ||
// throw new Error("Empty files"); | ||
// } | ||
// if (dirInfos.filter( | ||
@@ -284,2 +309,3 @@ // (di) => di.parent && !dirInfos.includes(di.parent)).length > 0 | ||
let markdownlintOptions = dirInfo.markdownlintOptions || {}; | ||
let { markdownlintConfig } = dirInfo; | ||
let parent = dirInfo; | ||
@@ -299,11 +325,20 @@ // eslint-disable-next-line prefer-destructuring | ||
} | ||
if ( | ||
!markdownlintConfig && | ||
parent.markdownlintConfig && | ||
!markdownlintOptions.config | ||
) { | ||
// eslint-disable-next-line prefer-destructuring | ||
markdownlintConfig = parent.markdownlintConfig; | ||
} | ||
} | ||
dirInfo.markdownlintOptions = markdownlintOptions; | ||
dirInfo.markdownlintConfig = markdownlintConfig; | ||
} | ||
return dirInfos; | ||
}; | ||
// Lint each list of files | ||
if (showProgress) { | ||
const fileCount = dirInfos.reduce((p, c) => p + c.files.length, 0); | ||
logMessage(`Linting: ${fileCount} file(s)`); | ||
} | ||
// Lint files in groups by shared configuration | ||
const lintFiles = async (dirInfos) => { | ||
const tasks = []; | ||
for (const dirInfo of dirInfos) { | ||
@@ -322,6 +357,4 @@ const { dir, files, markdownlintConfig, markdownlintOptions } = dirInfo; | ||
"files": filteredFiles, | ||
"config": | ||
markdownlintConfig || markdownlintOptions.config, | ||
"customRules": | ||
requireIds(dir, markdownlintOptions.customRules || []), | ||
"config": markdownlintConfig || markdownlintOptions.config, | ||
"customRules": markdownlintOptions.customRules, | ||
"frontMatter": markdownlintOptions.frontMatter | ||
@@ -331,6 +364,4 @@ ? new RegExp(markdownlintOptions.frontMatter, "u") | ||
"handleRuleFailures": true, | ||
"markdownItPlugins": | ||
requireIdsAndParams(dir, markdownlintOptions.markdownItPlugins || []), | ||
"noInlineConfig": | ||
Boolean(markdownlintOptions.noInlineConfig), | ||
"markdownItPlugins": markdownlintOptions.markdownItPlugins, | ||
"noInlineConfig": Boolean(markdownlintOptions.noInlineConfig), | ||
"resultVersion": 3 | ||
@@ -362,5 +393,7 @@ }; | ||
const taskResults = await Promise.all(tasks); | ||
tasks.length = 0; | ||
return taskResults; | ||
}; | ||
// Create summary of results | ||
// Create summary of results | ||
const createSummary = (taskResults) => { | ||
const summary = []; | ||
@@ -394,26 +427,52 @@ let counter = 0; | ||
summary.forEach((result) => delete result.counter); | ||
return summary; | ||
}; | ||
// Output summary via formatters | ||
// Output summary via formatters | ||
const outputSummary = | ||
async (summary, outputFormatters, logMessage, logError) => { | ||
const errorsPresent = (summary.length > 0); | ||
if (errorsPresent || outputFormatters) { | ||
const formatterOptions = { | ||
"results": summary, | ||
logMessage, | ||
logError | ||
}; | ||
const formattersAndParams = requireIdsAndParams( | ||
".", | ||
outputFormatters || [ [ "markdownlint-cli2-formatter-default" ] ] | ||
); | ||
await Promise.all(formattersAndParams.map((formatterAndParams) => { | ||
const [ formatter, ...formatterParams ] = formatterAndParams; | ||
return formatter(formatterOptions, ...formatterParams); | ||
})); | ||
} | ||
return errorsPresent; | ||
}; | ||
// Main function | ||
const main = async (argv, logMessage, logError) => { | ||
const globPatterns = processArgv(argv, logMessage); | ||
if (!globPatterns) { | ||
return 1; | ||
} | ||
const { baseMarkdownlintOptions, dirToDirInfo } = | ||
await getBaseOptions(globPatterns); | ||
const showProgress = !baseMarkdownlintOptions.noProgress; | ||
if (showProgress) { | ||
logMessage(`Finding: ${globPatterns.join(" ")}`); | ||
} | ||
const dirInfos = await createDirInfos(globPatterns, dirToDirInfo); | ||
if (showProgress) { | ||
const fileCount = dirInfos.reduce((p, c) => p + c.files.length, 0); | ||
logMessage(`Linting: ${fileCount} file(s)`); | ||
} | ||
const lintResults = await lintFiles(dirInfos); | ||
const summary = createSummary(lintResults); | ||
if (showProgress) { | ||
logMessage(`Summary: ${summary.length} error(s)`); | ||
} | ||
const { outputFormatters } = baseMarkdownlintOptions; | ||
const errorsPresent = (summary.length > 0); | ||
if (errorsPresent || outputFormatters) { | ||
const formatterOptions = { | ||
"results": summary, | ||
logMessage, | ||
logError | ||
}; | ||
const formattersAndParams = requireIdsAndParams( | ||
".", | ||
outputFormatters || [ [ "markdownlint-cli2-formatter-default" ] ] | ||
); | ||
await Promise.all(formattersAndParams.map((formatterAndParams) => { | ||
const [ formatter, ...formatterParams ] = formatterAndParams; | ||
return formatter(formatterOptions, ...formatterParams); | ||
})); | ||
} | ||
// Done | ||
const errorsPresent = | ||
await outputSummary(summary, outputFormatters, logMessage, logError); | ||
return errorsPresent ? 1 : 0; | ||
@@ -420,0 +479,0 @@ }; |
{ | ||
"name": "markdownlint-cli2", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "A fast, flexible, configuration-based command-line interface for linting Markdown/CommonMark files with the `markdownlint` library", | ||
@@ -24,4 +24,4 @@ "author": { | ||
"lint-watch": "git ls-files | entr npm run lint", | ||
"test": "tape test/*.js", | ||
"test-cover": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 tape test/*.js", | ||
"test": "ava test/*.js", | ||
"test-cover": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 ava test/*.js", | ||
"test-watch": "git ls-files | entr npm run test" | ||
@@ -46,2 +46,3 @@ }, | ||
"devDependencies": { | ||
"ava": "~3.12.1", | ||
"c8": "~7.3.0", | ||
@@ -59,5 +60,3 @@ "cpy": "~8.1.0", | ||
"@iktakahiro/markdown-it-katex": "~4.0.0", | ||
"markdownlint-rule-titlecase": "~0.0.5", | ||
"tape": "~5.0.1", | ||
"tape-player": "~0.1.1" | ||
"markdownlint-rule-titlecase": "~0.0.5" | ||
}, | ||
@@ -64,0 +63,0 @@ "keywords": [ |
@@ -240,2 +240,3 @@ # markdownlint-cli2 | ||
- 0.0.8 - Support `.markdownlint-cli2.yaml`, add progress | ||
- 0.0.9 - Improve configuration file handling | ||
@@ -242,0 +243,0 @@ <!-- markdownlint-disable line-length --> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32737
14
466
283