Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: igorshubovych/markdownlint-cli
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c17b68f607587a9c3c006100533f7d208c1f3154
Choose a base ref
...
head repository: igorshubovych/markdownlint-cli
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 8f46fb3ab7e84bc7e5170eb371a4d779474a632d
Choose a head ref

Commits on Feb 4, 2020

  1. Copy the full SHA
    2721817 View commit details

Commits on Feb 5, 2020

  1. Copy the full SHA
    2fb5506 View commit details

Commits on Feb 6, 2020

  1. Copy the full SHA
    a22c5ce View commit details
  2. Copy the full SHA
    6c13911 View commit details

Commits on Feb 8, 2020

  1. Copy the full SHA
    a1713b7 View commit details
  2. Copy the full SHA
    b0dfa3d View commit details

Commits on Feb 9, 2020

  1. Bump version 0.22.0

    DavidAnson committed Feb 9, 2020
    Copy the full SHA
    8a3a64e View commit details

Commits on Mar 5, 2020

  1. Fix error when an absolute path is given

    When an absolute path is given (e.g. via `lint-staged`), `node-ignore`
    was throwing a RangeError `path should be a path.relative()'d string`.
    see https://github.com/kaelzhang/node-ignore#1-pathname-should-be-a-pathrelatived-pathname
    
    This is only a "quick win" to fix this issue.
    As `prepareFileList` was already returning an `absolute` property based
    on the cwd, this commit only introduce a new `relative` property, which
    is also based on the cwd.
    No new specific test was written for this use case.
    nlm-pro committed Mar 5, 2020
    Copy the full SHA
    6e9849c View commit details

Commits on Mar 12, 2020

  1. Copy the full SHA
    9478e8e View commit details
  2. Copy the full SHA
    9a654d2 View commit details

Commits on Mar 14, 2020

  1. Bump acorn from 6.1.0 to 6.4.1 (#81)

    Bumps [acorn](https://github.com/acornjs/acorn) from 6.1.0 to 6.4.1.
    - [Release notes](https://github.com/acornjs/acorn/releases)
    - [Commits](acornjs/acorn@6.1.0...6.4.1)
    
    Signed-off-by: dependabot[bot] <support@github.com>
    
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Mar 14, 2020
    Copy the full SHA
    f5974af View commit details

Commits on Apr 17, 2020

  1. Bump markdownlint to current version (0.20.1) (#84)

    * Bump markdownlint to current version (0.20.1)
    
    * update package-lock
    
    * bump rule helpers to current (0.8.0)
    Alex Ball authored Apr 17, 2020
    Copy the full SHA
    6e4fa47 View commit details

Commits on Apr 29, 2020

  1. Copy the full SHA
    f18d6df View commit details

Commits on May 1, 2020

  1. Copy the full SHA
    ea431a3 View commit details
  2. Copy the full SHA
    ce3df34 View commit details
  3. Copy the full SHA
    8d3774f View commit details

Commits on May 2, 2020

  1. Copy the full SHA
    17c3b4e View commit details
  2. Copy the full SHA
    794167a View commit details

Commits on May 3, 2020

  1. 4
    Copy the full SHA
    a1f9a15 View commit details

Commits on May 5, 2020

  1. Bump version 0.23.0

    DavidAnson committed May 5, 2020
    Copy the full SHA
    1479279 View commit details
  2. Copy the full SHA
    3b7ec12 View commit details

Commits on May 17, 2020

  1. Copy the full SHA
    4a040ef View commit details
  2. Bump version 0.23.1

    DavidAnson committed May 17, 2020
    Copy the full SHA
    276b78a View commit details

Commits on Jun 19, 2020

  1. Copy the full SHA
    e857b96 View commit details

Commits on Jun 30, 2020

  1. Copy the full SHA
    3fbf4ec View commit details
  2. Bump version 0.23.2

    DavidAnson committed Jun 30, 2020
    Copy the full SHA
    8f46fb3 View commit details
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
sudo: false
language: node_js
node_js:
- '14'
- '13'
- '12'
- '11'
- '10'
- '9'
- '8'
env:
- CXX=g++-4.8
addons:
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:8-alpine
FROM node:12-alpine

WORKDIR /usr/src/app

39 changes: 27 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -24,8 +24,9 @@ markdownlint --help
-f, --fix fix basic errors (does not work with STDIN)
-s, --stdin read from STDIN (does not work with files)
-o, --output [outputFile] write issues to file (no console)
-c, --config [configFile] configuration file (JSON, JSONC, or YAML)
-i, --ignore [file|directory|glob] files to ignore/exclude
-c, --config [configFile] configuration file (JSON, JSONC, JS, or YAML)
-i, --ignore [file|directory|glob] file(s) to ignore/exclude
-p, --ignore-path [file] path to file with ignore pattern(s)
-r, --rules [file|directory|glob|package] custom rule files
```

@@ -46,13 +47,14 @@ Linux Bash: `markdownlint '**/*.md' --ignore node_modules`

### Ignoring files

If present in the current folder, a `.markdownlintignore` file will be used to ignore files and /or directories according to the rules for [gitignore][gitignore].
If present in the current folder, a `.markdownlintignore` file will be used to ignore files and/or directories according to the rules for [gitignore][gitignore].
If the `-p`/`--ignore-path` option is present, the specified file will be used instead of `.markdownlintignore`.

The order of operations is:

- Enumerate files/directories/globs on the command line
- Apply exclusions from `.markdownlintignore`
- Apply exclusions from `-i`/`--ignore` option(s)
- Enumerate files/directories/globs passed on the command line
- Apply exclusions from `-p`/`--ignore-path` (if specified) or `.markdownlintignore` (if present)
- Apply exclusions from any `-i`/`--ignore` option(s) that are specified

### Fixing errors

@@ -81,10 +83,23 @@ The example of configuration file:

See [test configuration file][test-config] or [style folder][style-folder] for more examples.

CLI argument `--config` is not mandatory.
If it is not provided, `markdownlint-cli` looks for file `.markdownlint.json`/`.markdownlint.yaml`/`.markdownlint.yml` in current folder, or for file `.markdownlintrc` in current or all upper folders.
The algorithm is described in details on [rc package page][rc-standards].
If `--config` argument is provided, the file must be valid JSON, JSONC, or YAML.
The CLI argument `--config` is not required.
If it is not provided, `markdownlint-cli` looks for the file `.markdownlint.json`/`.markdownlint.yaml`/`.markdownlint.yml` in current folder, or for the file `.markdownlintrc` in the current or all parent folders.
The algorithm is described in detail on the [`rc` package page][rc-standards].
If the `--config` argument is provided, the file must be valid JSON, JSONC, JS, or YAML.
JS configuration files contain JavaScript code, must have the `.js` extension, and must export (via `module.exports = ...`) a configuration object of the form shown above.
A JS configuration file may internally `require` one or more npm packages as a way of reusing configuration across projects.

> JS configuration files must be provided via the `--config` argument; they are not automatically loaded because running untrusted code is a security concern.
## Exit codes

`markdownlint-cli` returns one of the following exit codes:

- `0`: Program ran successfully
- `1`: Linting errors / bad parameter
- `2`: Unable to write `-o`/`--output` output file
- `3`: Unable to load `-r`/`--rules` custom rule

## Related

@@ -103,9 +118,9 @@ MIT © Igor Shubovych
[appveyor-url]: https://ci.appveyor.com/project/igorshubovych/markdownlint-cli

[markdownlint]: https://github.com/DavidAnson/markdownlint
[rules]: https://github.com/DavidAnson/markdownlint/blob/master/doc/Rules.md
[rules]: https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
[config]: https://github.com/DavidAnson/markdownlint#optionsconfig
[style-folder]: https://github.com/DavidAnson/markdownlint/tree/master/style
[style-folder]: https://github.com/DavidAnson/markdownlint/tree/main/style
[test-config]: https://github.com/igorshubovych/markdownlint-cli/blob/master/test/test-config.json
[rc-standards]: https://www.npmjs.com/package/rc#standards
[glob]: https://github.com/isaacs/node-glob
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
@@ -7,9 +7,9 @@ version: "{build}"
# what combinations to test
environment:
matrix:
- nodejs_version: "8"
- nodejs_version: "10"
- nodejs_version: "12"
- nodejs_version: "14"

# Get the stable version of node
install:
92 changes: 59 additions & 33 deletions markdownlint.js
Original file line number Diff line number Diff line change
@@ -6,56 +6,68 @@ const fs = require('fs');
const path = require('path');
const Module = require('module');
const program = require('commander');
const getStdin = require('get-stdin');
const jsYaml = require('js-yaml');
const jsoncParser = require('jsonc-parser');
const differenceWith = require('lodash.differencewith');
const flatten = require('lodash.flatten');
const extend = require('deep-extend');
const ignore = require('ignore');
const markdownlint = require('markdownlint');
const markdownlintRuleHelpers = require('markdownlint-rule-helpers');
const rc = require('rc');
const glob = require('glob');
const minimatch = require('minimatch');
const minimist = require('minimist');
const pkg = require('./package');

function jsoncParse(text) {
return JSON.parse(jsoncParser.stripComments(text));
return JSON.parse(require('jsonc-parser').stripComments(text));
}

function jsYamlSafeLoad(text) {
return require('js-yaml').safeLoad(text);
}

const projectConfigFiles = [
'.markdownlint.json',
'.markdownlint.yaml',
'.markdownlint.yml'
];
const configFileParsers = [jsoncParse, jsYaml.safeLoad];
const configFileParsers = [jsoncParse, jsYamlSafeLoad];
const fsOptions = {encoding: 'utf8'};
const processCwd = process.cwd();

function readConfiguration(args) {
let config = rc('markdownlint', {});
const userConfigFile = args.config;
const jsConfigFile = /\.js$/i.test(userConfigFile);
const rcArgv = minimist(process.argv.slice(2));
if (jsConfigFile) {
// Prevent rc package from parsing .js config file as INI
delete rcArgv.config;
}

// Load from well-known config files
let config = rc('markdownlint', {}, rcArgv);
for (const projectConfigFile of projectConfigFiles) {
try {
fs.accessSync(projectConfigFile, fs.R_OK);
const projectConfig = markdownlint.readConfigSync(projectConfigFile, configFileParsers);
config = extend(config, projectConfig);
config = require('deep-extend')(config, projectConfig);
break;
} catch (error) {
} catch (_) {
// Ignore failure
}
}

// Normally parsing this file is not needed,
// because it is already parsed by rc package.
// However I have to do it to overwrite configuration
// from .markdownlint.{json,yaml,yml}.

if (userConfigFile) {
try {
const userConfig = markdownlint.readConfigSync(userConfigFile, configFileParsers);
config = extend(config, userConfig);
const userConfig = jsConfigFile ?
// Evaluate .js configuration file as code
require(path.resolve(processCwd, userConfigFile)) :
// Load JSON/YAML configuration as data
markdownlint.readConfigSync(userConfigFile, configFileParsers);
config = require('deep-extend')(config, userConfig);
} catch (error) {
console.warn('Cannot read or parse config file ' + args.config + ': ' + error.message);
console.warn('Cannot read or parse config file ' + userConfigFile + ': ' + error.message);
}
}

@@ -80,7 +92,7 @@ function prepareFileList(files, fileExtensions, previousResults) {
// Directory (file falls through to below)
if (previousResults) {
const matcher = new minimatch.Minimatch(
path.resolve(process.cwd(), path.join(file, '**', extensionGlobPart)), globOptions);
path.resolve(processCwd, path.join(file, '**', extensionGlobPart)), globOptions);
return previousResults.filter(function (fileInfo) {
return matcher.match(fileInfo.absolute);
}).map(function (fileInfo) {
@@ -90,10 +102,10 @@ function prepareFileList(files, fileExtensions, previousResults) {

return glob.sync(path.join(file, '**', extensionGlobPart), globOptions);
}
} catch (error) {
} catch (_) {
// Not a directory, not a file, may be a glob
if (previousResults) {
const matcher = new minimatch.Minimatch(path.resolve(process.cwd(), file), globOptions);
const matcher = new minimatch.Minimatch(path.resolve(processCwd, file), globOptions);
return previousResults.filter(function (fileInfo) {
return matcher.match(fileInfo.absolute);
}).map(function (fileInfo) {
@@ -110,17 +122,19 @@ function prepareFileList(files, fileExtensions, previousResults) {
return flatten(files).map(function (file) {
return {
original: file,
relative: path.relative(processCwd, file),
absolute: path.resolve(file)
};
});
}

function printResult(lintResult) {
const results = flatten(Object.keys(lintResult).map(function (file) {
return lintResult[file].map(function (result) {
const results = flatten(Object.keys(lintResult).map(file => {
return lintResult[file].map(result => {
return {
file: file,
lineNumber: result.lineNumber,
column: (result.errorRange && result.errorRange[0]) || 0,
names: result.ruleNames.join('/'),
description: result.ruleDescription +
(result.errorDetail ? ' [' + result.errorDetail + ']' : '') +
@@ -130,12 +144,14 @@ function printResult(lintResult) {
}));
let lintResultString = '';
if (results.length > 0) {
results.sort(function (a, b) {
results.sort((a, b) => {
return a.file.localeCompare(b.file) || a.lineNumber - b.lineNumber ||
a.names.localeCompare(b.names) || a.description.localeCompare(b.description);
});
lintResultString = results.map(function (result) {
return result.file + ':' + result.lineNumber + ' ' + result.names + ' ' + result.description;
lintResultString = results.map(result => {
const {file, lineNumber, column, names, description} = result;
const columnText = column ? `:${column}` : '';
return `${file}:${lineNumber}${columnText} ${names} ${description}`;
}).join('\n');
// Note: process.exit(1) will end abruptly, interrupting asynchronous IO
// streams (e.g., when the output is being piped). Just set the exit code
@@ -169,8 +185,9 @@ program
.option('-f, --fix', 'fix basic errors (does not work with STDIN)')
.option('-s, --stdin', 'read from STDIN (does not work with files)')
.option('-o, --output [outputFile]', 'write issues to file (no console)')
.option('-c, --config [configFile]', 'configuration file (JSON, JSONC, or YAML)')
.option('-i, --ignore [file|directory|glob]', 'files to ignore/exclude', concatArray, [])
.option('-c, --config [configFile]', 'configuration file (JSON, JSONC, JS, or YAML)')
.option('-i, --ignore [file|directory|glob]', 'file(s) to ignore/exclude', concatArray, [])
.option('-p, --ignore-path [file]', 'path to file with ignore pattern(s)')
.option('-r, --rules [file|directory|glob|package]', 'custom rule files', concatArray, []);

program.parse(process.argv);
@@ -180,7 +197,7 @@ function tryResolvePath(filepath) {
if (path.basename(filepath) === filepath && path.extname(filepath) === '') {
// Looks like a package name, resolve it relative to cwd
// Get list of directories, where requested module can be.
let paths = Module._nodeModulePaths(process.cwd());
let paths = Module._nodeModulePaths(processCwd);
paths = paths.concat(Module.globalPaths);
if (require.resolve.paths) {
// Node >= 8.9.0
@@ -191,8 +208,8 @@ function tryResolvePath(filepath) {
}

// Maybe it is a path to package installed locally
return require.resolve(path.join(process.cwd(), filepath));
} catch (error) {
return require.resolve(path.join(processCwd, filepath));
} catch (_) {
return filepath;
}
}
@@ -216,16 +233,23 @@ function loadCustomRules(rules) {
}));
}

const markdownlintIgnore = '.markdownlintignore';
let ignorePath = '.markdownlintignore';
let {existsSync} = fs;
if (program.ignorePath) {
ignorePath = program.ignorePath;
existsSync = () => true;
}

let ignoreFilter = () => true;
if (fs.existsSync(markdownlintIgnore)) {
const ignoreText = fs.readFileSync(markdownlintIgnore, fsOptions);
if (existsSync(ignorePath)) {
const ignoreText = fs.readFileSync(ignorePath, fsOptions);
const ignore = require('ignore');
const ignoreInstance = ignore().add(ignoreText);
ignoreFilter = fileInfo => !ignoreInstance.ignores(fileInfo.original);
ignoreFilter = fileInfo => !ignoreInstance.ignores(fileInfo.relative);
}

const files = prepareFileList(program.args, ['md', 'markdown'])
.filter(ignoreFilter);
.filter(value => ignoreFilter(value));
const ignores = prepareFileList(program.ignore, ['md', 'markdown'], files);
const customRules = loadCustomRules(program.rules);
const diff = differenceWith(files, ignores, function (a, b) {
@@ -253,6 +277,7 @@ function lintAndPrint(stdin, files) {
...lintOptions,
resultVersion: 3
};
const markdownlintRuleHelpers = require('markdownlint-rule-helpers');
files.forEach(file => {
fixOptions.files = [file];
const fixResult = markdownlint.sync(fixOptions);
@@ -274,6 +299,7 @@ function lintAndPrint(stdin, files) {
if ((files.length > 0) && !program.stdin) {
lintAndPrint(null, diff);
} else if ((files.length === 0) && program.stdin && !program.fix) {
const getStdin = require('get-stdin');
getStdin().then(lintAndPrint);
} else {
program.help();
Loading