markdownlint-cli2 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020-2021 David Anson
3
+ Copyright (c) 2020-2022 David Anson
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -20,8 +20,11 @@ As a development dependency of the current package:
20
20
  npm install markdownlint-cli2 --save-dev
21
21
  ```
22
22
 
23
- Or [use the container image](#container-image) available on
24
- [Docker Hub as davidanson/markdownlint-cli2][docker-hub-markdownlint-cli2].
23
+ Or as a Docker container image:
24
+
25
+ ```shell
26
+ docker pull davidanson/markdownlint-cli2
27
+ ```
25
28
 
26
29
  ## Overview
27
30
 
@@ -51,13 +54,16 @@ markdownlint-cli2 vX.Y.Z (markdownlint vX.Y.Z)
51
54
  https://github.com/DavidAnson/markdownlint-cli2
52
55
 
53
56
  Syntax: markdownlint-cli2 glob0 [glob1] [...] [globN]
57
+ markdownlint-cli2-fix glob0 [glob1] [...] [globN]
58
+ markdownlint-cli2-config config-file glob0 [glob1] [...] [globN]
54
59
 
55
60
  Glob expressions (from the globby library):
56
61
  - * matches any number of characters, but not /
57
62
  - ? matches a single character, but not /
58
- - ** matches any number of characters, including / (when it's the only thing in a path part)
63
+ - ** matches any number of characters, including /
59
64
  - {} allows for a comma-separated list of "or" expressions
60
65
  - ! or # at the beginning of a pattern negate the match
66
+ - : at the beginning identifies a literal file path
61
67
 
62
68
  Dot-only glob:
63
69
  - The command "markdownlint-cli2 ." would lint every file in the current directory tree which is probably not intended
@@ -67,10 +73,10 @@ Dot-only glob:
67
73
  Configuration via:
68
74
  - .markdownlint-cli2.jsonc
69
75
  - .markdownlint-cli2.yaml
70
- - .markdownlint-cli2.js
76
+ - .markdownlint-cli2.cjs or .markdownlint-cli2.mjs
71
77
  - .markdownlint.jsonc or .markdownlint.json
72
78
  - .markdownlint.yaml or .markdownlint.yml
73
- - .markdownlint.js
79
+ - .markdownlint.cjs or .markdownlint.mjs
74
80
 
75
81
  Cross-platform compatibility:
76
82
  - UNIX and Windows shells expand globs according to different rules; quoting arguments is recommended
@@ -79,31 +85,45 @@ Cross-platform compatibility:
79
85
  - Some UNIX shells parse exclamation (!) in double-quotes; hashtag (#) is recommended in these cases
80
86
  - The path separator is forward slash (/) on all platforms; backslash (\) is automatically converted
81
87
 
82
- Therefore, the most compatible glob syntax for cross-platform support:
88
+ The most compatible syntax for cross-platform support:
83
89
  $ markdownlint-cli2 "**/*.md" "#node_modules"
84
90
  ```
85
91
 
86
92
  For scenarios where it is preferable to specify glob expressions in a
87
- configuration file, the `globs` property of `.markdownlint-cli2.jsonc` or
88
- `.markdownlint-cli2.yaml` or `.markdownlint-cli2.js` may be used instead of (or
89
- in addition to) passing `glob0 ... globN` on the command-line.
93
+ configuration file, the `globs` property of `.markdownlint-cli2.jsonc`, `.yaml`,
94
+ `.cjs`, or `.mjs` may be used instead of (or in addition to) passing
95
+ `glob0 ... globN` on the command-line.
90
96
 
91
- As shown above, the default command-line for `markdownlint-cli2` looks something
97
+ As shown above, a typical command-line for `markdownlint-cli2` looks something
92
98
  like:
93
99
 
94
100
  ```bash
95
101
  markdownlint-cli2 "**/*.md" "#node_modules"
96
102
  ```
97
103
 
98
- However, because sharing configuration between "normal" and "fix" modes is so
104
+ Because sharing the same configuration between "normal" and "fix" modes is
99
105
  common, the following command defaults the `fix` property (see below) to `true`:
100
106
 
101
107
  ```bash
102
108
  markdownlint-cli2-fix "**/*.md" "#node_modules"
103
109
  ```
104
110
 
105
- Other than the default behavior of the `fix` property (which can be overridden
106
- in both cases), these two commands behave identically.
111
+ Other than the default behavior of the `fix` property (which can be overridden),
112
+ these two commands behave identically.
113
+
114
+ In cases where it is not convenient to store a configuration file in the root
115
+ of a project, the `markdownlint-cli2-config` command can be used. This command
116
+ accepts a path to any supported configuration file as its first argument:
117
+
118
+ ```bash
119
+ markdownlint-cli2-config "config/.markdownlint-cli2.jsonc" "**/*.md" "#node_modules"
120
+ ```
121
+
122
+ The configuration file name must be (or end with) one of the supported types
123
+ above. For example, `.markdownlint.json` or `example.markdownlint-cli2.jsonc`.
124
+ The specified configuration file will be loaded, parsed, and applied as a base
125
+ configuration for the current directory - which will then be handled normally.
126
+ Otherwise, this command behaves identically to `markdownlint-cli2`.
107
127
 
108
128
  ### Container Image
109
129
 
@@ -111,7 +131,7 @@ A container image [`davidanson/markdownlint-cli2`][docker-hub-markdownlint-cli2]
111
131
  can also be used (e.g., as part of a CI pipeline):
112
132
 
113
133
  ```bash
114
- docker run -v $PWD:/workdir davidanson/markdownlint-cli2:0.3.1 "**/*.md" "#node_modules"
134
+ docker run -v $PWD:/workdir davidanson/markdownlint-cli2:0.5.0 "**/*.md" "#node_modules"
115
135
  ```
116
136
 
117
137
  Notes:
@@ -123,19 +143,19 @@ Notes:
123
143
  runs with restricted permissions. If it is necessary to run as `root`, pass
124
144
  the `-u root` option when invoking `docker`.
125
145
  - By default, `markdownlint-cli2` will execute within the `/workdir` directory
126
- _inside the container_. So, as shown above, [bind mount][docker-bind-mounts]
146
+ *inside the container*. So, as shown above, [bind mount][docker-bind-mounts]
127
147
  the project's directory there.
128
148
  - A custom working directory can be specified with Docker's `-w` flag:
129
149
 
130
150
  ```bash
131
- docker run -w /myfolder -v $PWD:/myfolder davidanson/markdownlint-cli2:0.3.1 "**/*.md" "#node_modules"
151
+ docker run -w /myfolder -v $PWD:/myfolder davidanson/markdownlint-cli2:0.5.0 "**/*.md" "#node_modules"
132
152
  ```
133
153
 
134
- To invoke the `markdownlint-cli2-fix` command instead, specify it via Docker's
135
- `--entrypoint` flag:
154
+ To invoke the `markdownlint-cli2-config` or `markdownlint-cli2-fix` commands
155
+ instead, use Docker's `--entrypoint` flag:
136
156
 
137
157
  ```bash
138
- docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/markdownlint-cli2:0.3.1 "**/*.md" "#node_modules"
158
+ docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/markdownlint-cli2:0.5.0 "**/*.md" "#node_modules"
139
159
  ```
140
160
 
141
161
  ### Exit Codes
@@ -159,11 +179,11 @@ docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/mark
159
179
  - See the [Configuration][markdownlint-configuration] section of the
160
180
  `markdownlint` documentation for information about the inline comment syntax
161
181
  for enabling and disabling rules with HTML comments.
162
- - In general, glob expressions match files under the current directory and
163
- configuration for that (top-level) directory applies to the entire tree
182
+ - In general, glob expressions match files under the current directory and the
183
+ configuration for that directory applies to the entire tree.
164
184
  - When glob expressions match files *not* under the current directory,
165
- configuration for the current (top-level) directory is applied to the
166
- closest common parent directory
185
+ configuration for the current directory is applied to the closest common
186
+ parent directory.
167
187
 
168
188
  ### `.markdownlint-cli2.jsonc`
169
189
 
@@ -177,8 +197,6 @@ docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/mark
177
197
  - `customRules`: `Array` of `String`s (or `Array`s of `String`s) of module
178
198
  names/paths of [custom rules][markdownlint-custom-rules] to load and use
179
199
  when linting
180
- - Each `String` is passed as the `id` parameter to Node's
181
- [require function][nodejs-require]
182
200
  - Relative paths are resolved based on the location of the `JSONC` file
183
201
  - Search [`markdownlint-rule` on npm][markdownlint-rule]
184
202
  - `fix`: `Boolean` value to enable fixing of linting errors reported by rules
@@ -229,9 +247,16 @@ docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/mark
229
247
  - This top-level setting is valid **only** in the directory from which
230
248
  `markdownlint-cli2` is run
231
249
  - Search [`markdownlint-cli2-formatter` on npm][markdownlint-cli2-formatter]
250
+ - When referencing a module via the `customRules`, `markdownItPlugins`, or
251
+ `outputFormatters` properties, each `String` identifier is passed to Node's
252
+ [`require` function][nodejs-require] then (if that failed) its
253
+ [`import` expression][nodejs-import-expression]
254
+ - Importing a locally-installed module using a bare specifier (ex:
255
+ `package-name`) or using a directory name (ex: `./package-dir`) will not
256
+ work until [`import.meta.resolve`][nodejs-import-meta-resolve] is available
232
257
  - Settings in this file apply to the directory it is in and all subdirectories.
233
258
  - Settings **merge with** those applied by any versions of this file in a parent
234
- directory.
259
+ directory (up to the current directory).
235
260
  - For example: [`.markdownlint-cli2.jsonc`][markdownlint-cli2-jsonc] with all
236
261
  properties set
237
262
 
@@ -245,17 +270,20 @@ docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/mark
245
270
  - For example: [`.markdownlint-cli2.yaml`][markdownlint-cli2-yaml] with all
246
271
  properties set
247
272
 
248
- ### `.markdownlint-cli2.js`
273
+ ### `.markdownlint-cli2.cjs` or `.markdownlint-cli2.mjs`
249
274
 
250
- - The format of this file is a [CommonJS module][commonjs-module] that exports
251
- the object described above for `.markdownlint-cli2.jsonc`.
275
+ - The format of this file is a [CommonJS module][commonjs-module] (`.cjs`) or
276
+ [ECMAScript module][ecmascript-module] (`.mjs`) that exports the object
277
+ described above for `.markdownlint-cli2.jsonc`.
252
278
  - Instead of passing a `String` to identify the module name/path to load for
253
279
  `customRules`, `markdownItPlugins`, and `outputFormatters`, the corresponding
254
280
  `Object` or `Function` can be provided directly.
255
281
  - Other details are the same as for `.markdownlint-cli2.jsonc` described above.
256
282
  - If a `.markdownlint-cli2.jsonc` or `.markdownlint-cli2.yaml` file is present
257
- in the same directory, it takes precedence.
258
- - For example: [`.markdownlint-cli2.js`][markdownlint-cli2-js]
283
+ in the same directory, it takes precedence; `.markdownlint-cli2.cjs` takes
284
+ precedence over `.markdownlint-cli2.mjs`.
285
+ - For example: [`.markdownlint-cli2.cjs`][markdownlint-cli2-cjs] or
286
+ [`.markdownlint-cli2.mjs`][markdownlint-cli2-mjs]
259
287
 
260
288
  ### `.markdownlint.jsonc` or `.markdownlint.json`
261
289
 
@@ -263,7 +291,7 @@ docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/mark
263
291
  the [`markdownlint` `config` object][markdownlint-config].
264
292
  - Settings in this file apply to the directory it is in and all subdirectories
265
293
  - Settings **override** those applied by any versions of this file in a parent
266
- directory.
294
+ directory (up to the current directory).
267
295
  - If `jsonc` and `json` files are present in the same directory, the `jsonc`
268
296
  version takes precedence.
269
297
  - To merge the settings of these files or share configuration, use the `extends`
@@ -282,14 +310,17 @@ docker run -v $PWD:/workdir --entrypoint="markdownlint-cli2-fix" davidanson/mark
282
310
  precedence.
283
311
  - For example: [`.markdownlint.yaml`][markdownlint-yaml]
284
312
 
285
- ### `.markdownlint.js`
313
+ ### `.markdownlint.cjs` or `.markdownlint.mjs`
286
314
 
287
- - The format of this file is a [CommonJS module][commonjs-module] that exports
288
- the [`markdownlint` `config` object][markdownlint-config].
315
+ - The format of this file is a [CommonJS module][commonjs-module] (`.cjs`) or
316
+ [ECMAScript module][ecmascript-module] (`.mjs`) that exports the
317
+ [`markdownlint` `config` object][markdownlint-config].
289
318
  - Other details are the same as for `jsonc`/`json` files described above.
290
- - If a `jsonc`, `json`, `yaml`, or `yml` file is present in the same directory,
291
- it takes precedence.
292
- - For example: [`.markdownlint.js`][markdownlint-js]
319
+ - If a `.markdownlint.jsonc`, `.json`, `.yaml`, or `.yml` file is present in the
320
+ same directory, it takes precedence; `.markdownlint.cjs` takes precedence over
321
+ `.markdownlint.mjs`.
322
+ - For example: [`.markdownlint.cjs`][markdownlint-cjs] or
323
+ [`.markdownlint.mjs`][markdownlint-mjs]
293
324
 
294
325
  ## Compatibility
295
326
 
@@ -313,7 +344,7 @@ reference to the `repos` list in that project's `.pre-commit-config.yaml` like:
313
344
 
314
345
  ```yaml
315
346
  - repo: https://github.com/DavidAnson/markdownlint-cli2
316
- rev: v0.3.1
347
+ rev: v0.5.0
317
348
  hooks:
318
349
  - id: markdownlint-cli2
319
350
  ```
@@ -344,11 +375,15 @@ reference to the `repos` list in that project's `.pre-commit-config.yaml` like:
344
375
  - 0.2.0 - Improve handling of Windows paths using backslash
345
376
  - 0.3.0 - Add Docker container, update dependencies
346
377
  - 0.3.1 - Extensibility tweaks
378
+ - 0.3.2 - Extensibility/Windows/consistency improvements
379
+ - 0.4.0 - New rules, async custom rules, explicit config, CJS (breaking)
380
+ - 0.5.0 - New rules, support modules (MJS) everywhere, include dotfiles
347
381
 
348
382
  <!-- markdownlint-disable line-length -->
349
383
 
350
384
  [commonmark]: https://commonmark.org/
351
385
  [commonjs-module]: https://nodejs.org/api/modules.html#modules_modules_commonjs_modules
386
+ [ecmascript-module]: https://nodejs.org/api/esm.html#modules-ecmascript-modules
352
387
  [docker-bind-mounts]: https://docs.docker.com/storage/bind-mounts/
353
388
  [docker-hub-markdownlint-cli2]: https://hub.docker.com/r/davidanson/markdownlint-cli2
354
389
  [front-matter]: https://jekyllrb.com/docs/frontmatter/
@@ -373,16 +408,20 @@ reference to the `repos` list in that project's `.pre-commit-config.yaml` like:
373
408
  [markdownlint-cli2]: https://github.com/DavidAnson/markdownlint-cli2
374
409
  [markdownlint-cli2-blog]: https://dlaa.me/blog/post/markdownlintcli2
375
410
  [markdownlint-cli2-formatter]: https://www.npmjs.com/search?q=keywords:markdownlint-cli2-formatter
376
- [markdownlint-cli2-js]: test/markdownlint-cli2-js/.markdownlint-cli2.js
411
+ [markdownlint-cli2-cjs]: test/markdownlint-cli2-cjs/.markdownlint-cli2.cjs
377
412
  [markdownlint-cli2-jsonc]: test/markdownlint-cli2-jsonc-example/.markdownlint-cli2.jsonc
413
+ [markdownlint-cli2-mjs]: test/markdownlint-cli2-mjs/.markdownlint-cli2.mjs
378
414
  [markdownlint-cli2-yaml]: test/markdownlint-cli2-yaml-example/.markdownlint-cli2.yaml
379
- [markdownlint-js]: test/markdownlint-js/.markdownlint.js
415
+ [markdownlint-cjs]: test/markdownlint-cjs/.markdownlint.cjs
380
416
  [markdownlint-jsonc]: https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.jsonc
417
+ [markdownlint-mjs]: test/markdownlint-mjs/.markdownlint.mjs
381
418
  [markdownlint-rule]: https://www.npmjs.com/search?q=keywords:markdownlint-rule
382
419
  [markdownlint-yaml]: https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.yaml
383
420
  [nodejs]: https://nodejs.org/
384
421
  [nodejs-docker]: https://github.com/nodejs/docker-node
385
422
  [nodejs-docker-non-root]: https://github.com/nodejs/docker-node/blob/main/docs/BestPractices.md#non-root-user
423
+ [nodejs-import-expression]: https://nodejs.org/api/esm.html#import-expressions
424
+ [nodejs-import-meta-resolve]: https://nodejs.org/api/esm.html#importmetaresolvespecifier-parent
386
425
  [nodejs-require]: https://nodejs.org/api/modules.html#modules_require_id
387
426
  [npm-image]: https://img.shields.io/npm/v/markdownlint-cli2.svg
388
427
  [npm-url]: https://www.npmjs.com/package/markdownlint-cli2
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ // @ts-check
4
+
5
+ "use strict";
6
+
7
+ const { run } = require("./markdownlint-cli2");
8
+
9
+ run({
10
+ "name": "markdownlint-cli2-config"
11
+ });
@@ -7,5 +7,6 @@
7
7
  const { run } = require("./markdownlint-cli2");
8
8
 
9
9
  run({
10
- "fixDefault": true
10
+ "fixDefault": true,
11
+ "name": "markdownlint-cli2-fix"
11
12
  });
@@ -10,19 +10,19 @@ const dynamicRequire = (typeof __non_webpack_require__ === "undefined") ? requir
10
10
  // Capture native require implementation for dynamic loading of modules
11
11
 
12
12
  // Requires
13
- const path = require("path");
14
- const globby = require("globby");
13
+ const path = require("node:path");
14
+ const { pathToFileURL } = require("node:url");
15
15
  const markdownlintLibrary = require("markdownlint");
16
16
  const { markdownlint, "readConfig": markdownlintReadConfig } =
17
17
  markdownlintLibrary.promises;
18
- const markdownlintRuleHelpers = require("markdownlint-rule-helpers");
18
+ const markdownlintRuleHelpers = require("markdownlint/helpers");
19
19
  const appendToArray = require("./append-to-array");
20
20
  const mergeOptions = require("./merge-options");
21
21
  const resolveAndRequire = require("./resolve-and-require");
22
22
 
23
23
  // Variables
24
24
  const packageName = "markdownlint-cli2";
25
- const packageVersion = "0.3.1";
25
+ const packageVersion = "0.5.0";
26
26
  const libraryName = "markdownlint";
27
27
  const libraryVersion = markdownlintLibrary.getVersion();
28
28
  const dotOnlySubstitute = "*.{md,markdown}";
@@ -31,10 +31,16 @@ const utf8 = "utf8";
31
31
  // No-op function
32
32
  const noop = () => null;
33
33
 
34
- // Parse JSONC text
35
- const jsoncParse = (text) => JSON.parse(require("strip-json-comments")(text));
34
+ // Gets a synchronous function to parse JSONC text
35
+ const getJsoncParse = async () => {
36
+ const { "default": stripJsonComments } =
37
+ // eslint-disable-next-line max-len
38
+ // eslint-disable-next-line no-inline-comments, node/no-unsupported-features/es-syntax
39
+ await import(/* webpackMode: "eager" */ "strip-json-comments");
40
+ return (text) => JSON.parse(stripJsonComments(text));
41
+ };
36
42
 
37
- // Parse YAML text
43
+ // Synchronous function to parse YAML text
38
44
  const yamlParse = (text) => require("yaml").parse(text);
39
45
 
40
46
  // Negate a glob
@@ -48,54 +54,142 @@ const readConfig = (fs, dir, name, otherwise) => {
48
54
  const file = path.posix.join(dir, name);
49
55
  return () => fs.promises.access(file).
50
56
  then(
51
- // @ts-ignore
52
- () => markdownlintReadConfig(file, [ jsoncParse, yamlParse ], fs),
57
+ () => getJsoncParse().then(
58
+ (jsoncParse) => markdownlintReadConfig(
59
+ file,
60
+ [ jsoncParse, yamlParse ],
61
+ fs
62
+ )
63
+ ),
53
64
  otherwise
54
65
  );
55
66
  };
56
67
 
57
- // Require a module ID with the specified directory in the path
58
- const requireResolve = (dir, id) => {
68
+ // Import or resolve/require a module ID with a custom directory in the path
69
+ const importOrRequireResolve = async (dir, id) => {
59
70
  if (typeof id === "string") {
60
- return resolveAndRequire(dynamicRequire, id, dir);
71
+ const expandId =
72
+ markdownlintRuleHelpers.expandTildePath(id, require("node:os"));
73
+ const errors = [];
74
+ try {
75
+ return resolveAndRequire(dynamicRequire, expandId, dir);
76
+ } catch (error) {
77
+ errors.push(error);
78
+ }
79
+ try {
80
+ const fileUrlString =
81
+ pathToFileURL(path.resolve(dir, expandId)).toString();
82
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
83
+ const module = await import(fileUrlString);
84
+ return module.default;
85
+ } catch (error) {
86
+ errors.push(error);
87
+ }
88
+ // Use AggregateError once available in Node 15+
89
+ throw new Error(errors.map((error) => error.message).join(" / "));
61
90
  }
62
91
  return id;
63
92
  };
64
93
 
65
- // Require an array of modules by ID
66
- const requireIds = (dir, ids, noRequire) => (
67
- noRequire ? [] : ids.map((id) => requireResolve(dir, id))
94
+ // Import or require an array of modules by ID
95
+ const importOrRequireIds = (dir, ids, noRequire) => (
96
+ Promise.all(noRequire ? [] : ids.map((id) => importOrRequireResolve(dir, id)))
68
97
  );
69
98
 
70
- // Require an array of modules by ID (preserving parameters)
71
- const requireIdsAndParams = (dir, idsAndParams, noRequire) => {
99
+ // Import or require an array of modules by ID (preserving parameters)
100
+ const importOrRequireIdsAndParams = async (dir, idsAndParams, noRequire) => {
72
101
  if (noRequire) {
73
102
  return [];
74
103
  }
75
104
  const ids = idsAndParams.map((entry) => entry[0]);
76
- const modules = requireIds(dir, ids);
105
+ const modules = await importOrRequireIds(dir, ids);
77
106
  const modulesAndParams = idsAndParams.
78
107
  map((entry, i) => [ modules[i], ...entry.slice(1) ]);
79
108
  return modulesAndParams;
80
109
  };
81
110
 
82
- // Require a JS file and return the exported object
83
- const requireConfig = (fs, dir, name, noRequire) => (
111
+ // Import or require a JavaScript file and return the exported object
112
+ const importOrRequireConfig = (fs, dir, name, noRequire, otherwise) => (
84
113
  () => (noRequire
85
114
  // eslint-disable-next-line prefer-promise-reject-errors
86
115
  ? Promise.reject()
87
116
  : fs.promises.access(path.posix.join(dir, name))
88
117
  ).
89
118
  then(
90
- () => requireResolve(dir, `./${name}`),
91
- noop
119
+ () => importOrRequireResolve(dir, `./${name}`),
120
+ otherwise
92
121
  )
93
122
  );
94
123
 
95
- // Process command-line arguments and return glob patterns
124
+ // Read an options or config file in any format and return the object
125
+ const readOptionsOrConfig = async (configPath, fs, noRequire) => {
126
+ const basename = path.basename(configPath);
127
+ const dirname = path.dirname(configPath);
128
+ let options = null;
129
+ let config = null;
130
+ if (basename.endsWith(".markdownlint-cli2.jsonc")) {
131
+ const jsoncParse = await getJsoncParse();
132
+ options = jsoncParse(await fs.promises.readFile(configPath, utf8));
133
+ } else if (basename.endsWith(".markdownlint-cli2.yaml")) {
134
+ options = yamlParse(await fs.promises.readFile(configPath, utf8));
135
+ } else if (
136
+ basename.endsWith(".markdownlint-cli2.cjs") ||
137
+ basename.endsWith(".markdownlint-cli2.mjs")
138
+ ) {
139
+ options = await (
140
+ importOrRequireConfig(fs, dirname, basename, noRequire, noop)()
141
+ );
142
+ } else if (
143
+ basename.endsWith(".markdownlint.jsonc") ||
144
+ basename.endsWith(".markdownlint.json") ||
145
+ basename.endsWith(".markdownlint.yaml") ||
146
+ basename.endsWith(".markdownlint.yml")
147
+ ) {
148
+ const jsoncParse = await getJsoncParse();
149
+ config =
150
+ await markdownlintReadConfig(configPath, [ jsoncParse, yamlParse ], fs);
151
+ } else if (
152
+ basename.endsWith(".markdownlint.cjs") ||
153
+ basename.endsWith(".markdownlint.mjs")
154
+ ) {
155
+ config = await (
156
+ importOrRequireConfig(fs, dirname, basename, noRequire, noop)()
157
+ );
158
+ } else {
159
+ throw new Error(
160
+ `Configuration file "${configPath}" is unrecognized; ` +
161
+ "its name should be (or end with) one of the supported types " +
162
+ "(e.g., \".markdownlint.json\" or \"example.markdownlint-cli2.jsonc\")."
163
+ );
164
+ }
165
+ return options || { config };
166
+ };
167
+
168
+ // Filter a list of files to ignore by glob
169
+ const removeIgnoredFiles = (dir, files, ignores) => {
170
+ const micromatch = require("micromatch");
171
+ return micromatch(
172
+ files.map((file) => path.posix.relative(dir, file)),
173
+ ignores
174
+ ).map((file) => path.posix.join(dir, file));
175
+ };
176
+
177
+ // Process/normalize command-line arguments and return glob patterns
96
178
  const processArgv = (argv) => {
97
179
  const globPatterns = (argv || []).map(
98
- (glob) => glob.replace(/^#/u, "!").replace(/\\(?![$()*+?[\]^])/gu, "/")
180
+ (glob) => {
181
+ if (glob.startsWith(":")) {
182
+ return glob;
183
+ }
184
+ // Escape RegExp special characters recognized by fast-glob
185
+ // https://github.com/mrmlnc/fast-glob#advanced-syntax
186
+ const specialCharacters = /\\(?![$()*+?[\]^])/gu;
187
+ if (glob.startsWith("\\:")) {
188
+ return `\\:${glob.slice(2).replace(specialCharacters, "/")}`;
189
+ }
190
+ return (glob.startsWith("#") ? `!${glob.slice(1)}` : glob).
191
+ replace(specialCharacters, "/");
192
+ }
99
193
  );
100
194
  if ((globPatterns.length === 1) && (globPatterns[0] === ".")) {
101
195
  // Substitute a more reasonable pattern
@@ -106,31 +200,33 @@ const processArgv = (argv) => {
106
200
 
107
201
  // Show help if missing arguments
108
202
  const showHelp = (logMessage) => {
109
- const { name, homepage } = require("./package.json");
110
203
  /* eslint-disable max-len */
111
- logMessage(`${homepage}
204
+ logMessage(`https://github.com/DavidAnson/markdownlint-cli2
112
205
 
113
- Syntax: ${name} glob0 [glob1] [...] [globN]
206
+ Syntax: markdownlint-cli2 glob0 [glob1] [...] [globN]
207
+ markdownlint-cli2-fix glob0 [glob1] [...] [globN]
208
+ markdownlint-cli2-config config-file glob0 [glob1] [...] [globN]
114
209
 
115
210
  Glob expressions (from the globby library):
116
211
  - * matches any number of characters, but not /
117
212
  - ? matches a single character, but not /
118
- - ** matches any number of characters, including / (when it's the only thing in a path part)
213
+ - ** matches any number of characters, including /
119
214
  - {} allows for a comma-separated list of "or" expressions
120
215
  - ! or # at the beginning of a pattern negate the match
216
+ - : at the beginning identifies a literal file path
121
217
 
122
218
  Dot-only glob:
123
- - The command "${name} ." would lint every file in the current directory tree which is probably not intended
124
- - Instead, it is mapped to "${name} ${dotOnlySubstitute}" which lints all Markdown files in the current directory
125
- - To lint every file in the current directory tree, the command "${name} **" can be used instead
219
+ - The command "markdownlint-cli2 ." would lint every file in the current directory tree which is probably not intended
220
+ - Instead, it is mapped to "markdownlint-cli2 ${dotOnlySubstitute}" which lints all Markdown files in the current directory
221
+ - To lint every file in the current directory tree, the command "markdownlint-cli2 **" can be used instead
126
222
 
127
223
  Configuration via:
128
224
  - .markdownlint-cli2.jsonc
129
225
  - .markdownlint-cli2.yaml
130
- - .markdownlint-cli2.js
226
+ - .markdownlint-cli2.cjs or .markdownlint-cli2.mjs
131
227
  - .markdownlint.jsonc or .markdownlint.json
132
228
  - .markdownlint.yaml or .markdownlint.yml
133
- - .markdownlint.js
229
+ - .markdownlint.cjs or .markdownlint.mjs
134
230
 
135
231
  Cross-platform compatibility:
136
232
  - UNIX and Windows shells expand globs according to different rules; quoting arguments is recommended
@@ -139,19 +235,20 @@ Cross-platform compatibility:
139
235
  - Some UNIX shells parse exclamation (!) in double-quotes; hashtag (#) is recommended in these cases
140
236
  - The path separator is forward slash (/) on all platforms; backslash (\\) is automatically converted
141
237
 
142
- Therefore, the most compatible glob syntax for cross-platform support:
143
- $ ${name} "**/*.md" "#node_modules"`
238
+ The most compatible syntax for cross-platform support:
239
+ $ markdownlint-cli2 "**/*.md" "#node_modules"`
144
240
  );
145
241
  /* eslint-enable max-len */
146
242
  };
147
243
 
148
244
  // Get (creating if necessary) and process a directory's info object
149
245
  const getAndProcessDirInfo =
150
- (fs, tasks, dirToDirInfo, dir, noRequire, func) => {
246
+ (fs, tasks, dirToDirInfo, dir, relativeDir, noRequire, func) => {
151
247
  let dirInfo = dirToDirInfo[dir];
152
248
  if (!dirInfo) {
153
249
  dirInfo = {
154
250
  dir,
251
+ relativeDir,
155
252
  "parent": null,
156
253
  "files": [],
157
254
  "markdownlintConfig": null,
@@ -169,17 +266,27 @@ const getAndProcessDirInfo =
169
266
  then(
170
267
  () => fs.promises.
171
268
  readFile(markdownlintCli2Jsonc, utf8).
172
- then(jsoncParse),
269
+ then(
270
+ (content) => getJsoncParse().
271
+ then((jsoncParse) => jsoncParse(content))
272
+ ),
173
273
  () => fs.promises.access(markdownlintCli2Yaml).
174
274
  then(
175
275
  () => fs.promises.
176
276
  readFile(markdownlintCli2Yaml, utf8).
177
277
  then(yamlParse),
178
- requireConfig(
278
+ importOrRequireConfig(
179
279
  fs,
180
280
  dir,
181
- ".markdownlint-cli2.js",
182
- noRequire
281
+ ".markdownlint-cli2.cjs",
282
+ noRequire,
283
+ importOrRequireConfig(
284
+ fs,
285
+ dir,
286
+ ".markdownlint-cli2.mjs",
287
+ noRequire,
288
+ noop
289
+ )
183
290
  )
184
291
  )
185
292
  ).
@@ -206,11 +313,18 @@ const getAndProcessDirInfo =
206
313
  fs,
207
314
  dir,
208
315
  ".markdownlint.yml",
209
- requireConfig(
316
+ importOrRequireConfig(
210
317
  fs,
211
318
  dir,
212
- ".markdownlint.js",
213
- noRequire
319
+ ".markdownlint.cjs",
320
+ noRequire,
321
+ importOrRequireConfig(
322
+ fs,
323
+ dir,
324
+ ".markdownlint.mjs",
325
+ noRequire,
326
+ noop
327
+ )
214
328
  )
215
329
  )
216
330
  )
@@ -233,20 +347,28 @@ const getAndProcessDirInfo =
233
347
  const getBaseOptions = async (
234
348
  fs,
235
349
  baseDir,
350
+ relativeDir,
236
351
  globPatterns,
237
- optionsDefault,
352
+ options,
238
353
  fixDefault,
239
354
  noGlobs,
240
355
  noRequire
241
356
  ) => {
242
357
  const tasks = [];
243
358
  const dirToDirInfo = {};
244
- getAndProcessDirInfo(fs, tasks, dirToDirInfo, baseDir, noRequire);
359
+ getAndProcessDirInfo(
360
+ fs,
361
+ tasks,
362
+ dirToDirInfo,
363
+ baseDir,
364
+ relativeDir,
365
+ noRequire
366
+ );
245
367
  await Promise.all(tasks);
246
368
  // eslint-disable-next-line no-multi-assign
247
369
  const baseMarkdownlintOptions = dirToDirInfo[baseDir].markdownlintOptions =
248
370
  mergeOptions(
249
- mergeOptions(optionsDefault, { "fix": fixDefault }),
371
+ mergeOptions(options, { "fix": fixDefault }),
250
372
  dirToDirInfo[baseDir].markdownlintOptions
251
373
  );
252
374
 
@@ -261,7 +383,6 @@ const getBaseOptions = async (
261
383
  // eslint-disable-next-line unicorn/no-array-callback-reference
262
384
  (baseMarkdownlintOptions.ignores || []).map(negateGlob);
263
385
  appendToArray(globPatterns, ignorePatterns);
264
- delete baseMarkdownlintOptions.ignores;
265
386
 
266
387
  return {
267
388
  baseMarkdownlintOptions,
@@ -271,21 +392,51 @@ const getBaseOptions = async (
271
392
 
272
393
  // Enumerate files from globs and build directory infos
273
394
  const enumerateFiles =
274
- async (fs, baseDir, globPatterns, dirToDirInfo, noRequire) => {
395
+ // eslint-disable-next-line max-len
396
+ async (fs, baseDirSystem, baseDir, globPatterns, dirToDirInfo, noErrors, noRequire) => {
275
397
  const tasks = [];
276
398
  const globbyOptions = {
277
399
  "absolute": true,
278
400
  "cwd": baseDir,
401
+ "dot": true,
279
402
  "expandDirectories": false,
280
403
  fs
281
404
  };
405
+ if (noErrors) {
406
+ globbyOptions.suppressErrors = true;
407
+ }
408
+ // Special-case literal files
409
+ const literalFiles = [];
410
+ const filteredGlobPatterns = globPatterns.filter(
411
+ (globPattern) => {
412
+ if (globPattern.startsWith(":")) {
413
+ literalFiles.push(
414
+ posixPath(path.resolve(baseDirSystem, globPattern.slice(1)))
415
+ );
416
+ return false;
417
+ }
418
+ return true;
419
+ }
420
+ ).map((globPattern) => globPattern.replace(/^\\:/u, ":"));
421
+ const baseMarkdownlintOptions = dirToDirInfo[baseDir].markdownlintOptions;
422
+ const globsForIgnore =
423
+ (baseMarkdownlintOptions.globs || []).
424
+ filter((glob) => glob.startsWith("!"));
425
+ const filteredLiteralFiles =
426
+ ((literalFiles.length > 0) && (globsForIgnore.length > 0))
427
+ ? removeIgnoredFiles(baseDir, literalFiles, globsForIgnore)
428
+ : literalFiles;
282
429
  // Manually expand directories to avoid globby call to dir-glob.sync
283
430
  const expandedDirectories = await Promise.all(
284
- globPatterns.map((globPattern) => {
285
- const globPath = path.resolve(
286
- baseDir,
287
- globPattern[0] === "!" ? globPattern.slice(1) : globPattern
288
- );
431
+ filteredGlobPatterns.map((globPattern) => {
432
+ const barePattern =
433
+ globPattern.startsWith("!")
434
+ ? globPattern.slice(1)
435
+ : globPattern;
436
+ const globPath =
437
+ (path.posix.isAbsolute(barePattern) || path.isAbsolute(barePattern))
438
+ ? barePattern
439
+ : path.posix.join(baseDir, barePattern);
289
440
  return fs.promises.stat(globPath).
290
441
  then((stats) => (stats.isDirectory()
291
442
  ? path.posix.join(globPattern, "**")
@@ -294,7 +445,13 @@ async (fs, baseDir, globPatterns, dirToDirInfo, noRequire) => {
294
445
  })
295
446
  );
296
447
  // Process glob patterns
297
- const files = await globby(expandedDirectories, globbyOptions);
448
+ // eslint-disable-next-line max-len
449
+ // eslint-disable-next-line no-inline-comments, node/no-unsupported-features/es-syntax
450
+ const { globby } = await import(/* webpackMode: "eager" */ "globby");
451
+ const files = [
452
+ ...await globby(expandedDirectories, globbyOptions),
453
+ ...filteredLiteralFiles
454
+ ];
298
455
  for (const file of files) {
299
456
  const dir = path.posix.dirname(file);
300
457
  getAndProcessDirInfo(
@@ -302,6 +459,7 @@ async (fs, baseDir, globPatterns, dirToDirInfo, noRequire) => {
302
459
  tasks,
303
460
  dirToDirInfo,
304
461
  dir,
462
+ null,
305
463
  noRequire,
306
464
  (dirInfo) => {
307
465
  dirInfo.files.push(file);
@@ -339,6 +497,7 @@ const enumerateParents = async (fs, baseDir, dirToDirInfo, noRequire) => {
339
497
  tasks,
340
498
  dirToDirInfo,
341
499
  dir,
500
+ null,
342
501
  noRequire,
343
502
  // eslint-disable-next-line no-loop-func
344
503
  (dirInfo) => {
@@ -357,20 +516,36 @@ const enumerateParents = async (fs, baseDir, dirToDirInfo, noRequire) => {
357
516
 
358
517
  // Create directory info objects by enumerating file globs
359
518
  const createDirInfos =
360
- async (fs, baseDir, globPatterns, dirToDirInfo, optionsOverride, noRequire) => {
361
- await enumerateFiles(fs, baseDir, globPatterns, dirToDirInfo, noRequire);
362
- await enumerateParents(fs, baseDir, dirToDirInfo, noRequire);
519
+ // eslint-disable-next-line max-len
520
+ async (fs, baseDirSystem, baseDir, globPatterns, dirToDirInfo, optionsOverride, noErrors, noRequire) => {
521
+ await enumerateFiles(
522
+ fs,
523
+ baseDirSystem,
524
+ baseDir,
525
+ globPatterns,
526
+ dirToDirInfo,
527
+ noErrors,
528
+ noRequire
529
+ );
530
+ await enumerateParents(
531
+ fs,
532
+ baseDir,
533
+ dirToDirInfo,
534
+ noRequire
535
+ );
363
536
 
364
537
  // Merge file lists with identical configuration
365
538
  const dirs = Object.keys(dirToDirInfo);
366
539
  dirs.sort((a, b) => b.length - a.length);
367
540
  const dirInfos = [];
368
541
  const noConfigDirInfo =
542
+ // eslint-disable-next-line unicorn/consistent-function-scoping
369
543
  (dirInfo) => (
370
544
  dirInfo.parent &&
371
545
  !dirInfo.markdownlintConfig &&
372
546
  !dirInfo.markdownlintOptions
373
547
  );
548
+ const tasks = [];
374
549
  for (const dir of dirs) {
375
550
  const dirInfo = dirToDirInfo[dir];
376
551
  if (noConfigDirInfo(dirInfo)) {
@@ -379,28 +554,34 @@ async (fs, baseDir, globPatterns, dirToDirInfo, optionsOverride, noRequire) => {
379
554
  }
380
555
  dirToDirInfo[dir] = null;
381
556
  } else {
382
- const { markdownlintOptions } = dirInfo;
557
+ const { markdownlintOptions, relativeDir } = dirInfo;
383
558
  if (markdownlintOptions && markdownlintOptions.customRules) {
384
- const customRules =
385
- requireIds(
386
- dir,
559
+ tasks.push(
560
+ importOrRequireIds(
561
+ relativeDir || dir,
387
562
  markdownlintOptions.customRules,
388
563
  noRequire
389
- );
390
- // Expand nested arrays (for packages that export multiple rules)
391
- markdownlintOptions.customRules = customRules.flat();
564
+ ).then((customRules) => {
565
+ // Expand nested arrays (for packages that export multiple rules)
566
+ markdownlintOptions.customRules = customRules.flat();
567
+ })
568
+ );
392
569
  }
393
570
  if (markdownlintOptions && markdownlintOptions.markdownItPlugins) {
394
- markdownlintOptions.markdownItPlugins =
395
- requireIdsAndParams(
396
- dir,
571
+ tasks.push(
572
+ importOrRequireIdsAndParams(
573
+ relativeDir || dir,
397
574
  markdownlintOptions.markdownItPlugins,
398
575
  noRequire
399
- );
576
+ ).then((markdownItPlugins) => {
577
+ markdownlintOptions.markdownItPlugins = markdownItPlugins;
578
+ })
579
+ );
400
580
  }
401
581
  dirInfos.push(dirInfo);
402
582
  }
403
583
  }
584
+ await Promise.all(tasks);
404
585
  for (const dirInfo of dirInfos) {
405
586
  while (dirInfo.parent && !dirToDirInfo[dirInfo.parent.dir]) {
406
587
  dirInfo.parent = dirInfo.parent.parent;
@@ -466,21 +647,21 @@ async (fs, baseDir, globPatterns, dirToDirInfo, optionsOverride, noRequire) => {
466
647
  };
467
648
 
468
649
  // Lint files in groups by shared configuration
469
- const lintFiles = (fs, dirInfos, fileContents) => {
650
+ const lintFiles = async (fs, dirInfos, fileContents) => {
651
+ const jsoncParse = await getJsoncParse();
470
652
  const tasks = [];
471
653
  // For each dirInfo
472
654
  for (const dirInfo of dirInfos) {
473
655
  const { dir, files, markdownlintConfig, markdownlintOptions } = dirInfo;
474
656
  // Filter file/string inputs to only those in the dirInfo
475
657
  let filesAfterIgnores = files;
476
- if (markdownlintOptions.ignores) {
658
+ if (
659
+ markdownlintOptions.ignores &&
660
+ (markdownlintOptions.ignores.length > 0)
661
+ ) {
477
662
  // eslint-disable-next-line unicorn/no-array-callback-reference
478
663
  const ignores = markdownlintOptions.ignores.map(negateGlob);
479
- const micromatch = require("micromatch");
480
- filesAfterIgnores = micromatch(
481
- files.map((file) => path.posix.relative(dir, file)),
482
- ignores
483
- ).map((file) => path.posix.join(dir, file));
664
+ filesAfterIgnores = removeIgnoredFiles(dir, files, ignores);
484
665
  }
485
666
  const filteredFiles = filesAfterIgnores.filter(
486
667
  (file) => fileContents[file] === undefined
@@ -496,6 +677,7 @@ const lintFiles = (fs, dirInfos, fileContents) => {
496
677
  "files": filteredFiles,
497
678
  "strings": filteredStrings,
498
679
  "config": markdownlintConfig || markdownlintOptions.config,
680
+ "configParsers": [ jsoncParse ],
499
681
  "customRules": markdownlintOptions.customRules,
500
682
  "frontMatter": markdownlintOptions.frontMatter
501
683
  ? new RegExp(markdownlintOptions.frontMatter, "u")
@@ -577,26 +759,33 @@ const createSummary = (baseDir, taskResults) => {
577
759
  };
578
760
 
579
761
  // Output summary via formatters
580
- const outputSummary =
581
- async (baseDir, summary, outputFormatters, logMessage, logError) => {
582
- const errorsPresent = (summary.length > 0);
583
- if (errorsPresent || outputFormatters) {
584
- const formatterOptions = {
585
- "directory": baseDir,
586
- "results": summary,
587
- logMessage,
588
- logError
589
- };
590
- const formattersAndParams = outputFormatters
591
- ? requireIdsAndParams(baseDir, outputFormatters)
592
- : [ [ require("markdownlint-cli2-formatter-default") ] ];
593
- await Promise.all(formattersAndParams.map((formatterAndParams) => {
594
- const [ formatter, ...formatterParams ] = formatterAndParams;
595
- return formatter(formatterOptions, ...formatterParams);
596
- }));
597
- }
598
- return errorsPresent;
599
- };
762
+ const outputSummary = async (
763
+ baseDir,
764
+ relativeDir,
765
+ summary,
766
+ outputFormatters,
767
+ logMessage,
768
+ logError
769
+ ) => {
770
+ const errorsPresent = (summary.length > 0);
771
+ if (errorsPresent || outputFormatters) {
772
+ const formatterOptions = {
773
+ "directory": baseDir,
774
+ "results": summary,
775
+ logMessage,
776
+ logError
777
+ };
778
+ const dir = relativeDir || baseDir;
779
+ const formattersAndParams = outputFormatters
780
+ ? await importOrRequireIdsAndParams(dir, outputFormatters)
781
+ : [ [ require("markdownlint-cli2-formatter-default") ] ];
782
+ await Promise.all(formattersAndParams.map((formatterAndParams) => {
783
+ const [ formatter, ...formatterParams ] = formatterAndParams;
784
+ return formatter(formatterOptions, ...formatterParams);
785
+ }));
786
+ }
787
+ return errorsPresent;
788
+ };
600
789
 
601
790
  // Main function
602
791
  const main = async (params) => {
@@ -609,28 +798,41 @@ const main = async (params) => {
609
798
  fixDefault,
610
799
  fileContents,
611
800
  nonFileContents,
801
+ noErrors,
612
802
  noGlobs,
613
- noRequire
803
+ noRequire,
804
+ name
614
805
  } = params;
615
806
  const logMessage = params.logMessage || noop;
616
807
  const logError = params.logError || noop;
617
- const fs = params.fs || require("fs");
808
+ const fs = params.fs || require("node:fs");
618
809
  const baseDirSystem =
619
810
  (directory && path.resolve(directory)) ||
620
811
  process.cwd();
621
812
  const baseDir = posixPath(baseDirSystem);
622
813
  // Output banner
623
814
  logMessage(
624
- `${packageName} v${packageVersion} (${libraryName} v${libraryVersion})`
815
+ // eslint-disable-next-line max-len
816
+ `${name || packageName} v${packageVersion} (${libraryName} v${libraryVersion})`
625
817
  );
818
+ // Read argv configuration file (if relevant and present)
819
+ let optionsArgv = null;
820
+ let relativeDir = null;
821
+ const [ configPath ] = (argv || []);
822
+ if ((name === "markdownlint-cli2-config") && configPath) {
823
+ optionsArgv =
824
+ await readOptionsOrConfig(configPath, fs, noRequire);
825
+ relativeDir = path.dirname(configPath);
826
+ }
626
827
  // Process arguments and get base options
627
- const globPatterns = processArgv(argv);
828
+ const globPatterns = processArgv(optionsArgv ? argv.slice(1) : argv);
628
829
  const { baseMarkdownlintOptions, dirToDirInfo } =
629
830
  await getBaseOptions(
630
831
  fs,
631
832
  baseDir,
833
+ relativeDir,
632
834
  globPatterns,
633
- optionsDefault,
835
+ optionsArgv || optionsDefault,
634
836
  fixDefault,
635
837
  noGlobs,
636
838
  noRequire
@@ -662,10 +864,12 @@ const main = async (params) => {
662
864
  const dirInfos =
663
865
  await createDirInfos(
664
866
  fs,
867
+ baseDirSystem,
665
868
  baseDir,
666
869
  globPatterns,
667
870
  dirToDirInfo,
668
871
  optionsOverride,
872
+ noErrors,
669
873
  noRequire
670
874
  );
671
875
  // Output linting status
@@ -687,7 +891,7 @@ const main = async (params) => {
687
891
  (optionsOverride && optionsOverride.outputFormatters) ||
688
892
  baseMarkdownlintOptions.outputFormatters;
689
893
  const errorsPresent = await outputSummary(
690
- baseDir, summary, outputFormatters, logMessage, logError
894
+ baseDir, relativeDir, summary, outputFormatters, logMessage, logError
691
895
  );
692
896
  // Return result
693
897
  return errorsPresent ? 1 : 0;
package/package.json CHANGED
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "name": "markdownlint-cli2",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "A fast, flexible, configuration-based command-line interface for linting Markdown/CommonMark files with the `markdownlint` library",
5
5
  "author": {
6
6
  "name": "David Anson",
7
7
  "url": "https://dlaa.me/"
8
8
  },
9
9
  "license": "MIT",
10
- "main": "markdownlint-cli2.js",
10
+ "type": "commonjs",
11
+ "exports": "./markdownlint-cli2.js",
11
12
  "bin": {
12
13
  "markdownlint-cli2": "markdownlint-cli2.js",
14
+ "markdownlint-cli2-config": "markdownlint-cli2-config.js",
13
15
  "markdownlint-cli2-fix": "markdownlint-cli2-fix.js"
14
16
  },
15
17
  "homepage": "https://github.com/DavidAnson/markdownlint-cli2",
@@ -19,55 +21,61 @@
19
21
  },
20
22
  "bugs": "https://github.com/DavidAnson/markdownlint-cli2/issues",
21
23
  "scripts": {
22
- "build-docker-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker build -t davidanson/markdownlint-cli2:$VERSION -f docker/Dockerfile .",
24
+ "build-docker-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker build -t davidanson/markdownlint-cli2:v$VERSION -f docker/Dockerfile --label org.opencontainers.image.version=v$VERSION .",
23
25
  "ci": "npm-run-all --continue-on-error --parallel test-cover lint",
26
+ "docker-npm-install": "docker run --rm --tty --name npm-install --volume $PWD:/home/workdir --workdir /home/workdir --user node node:16 npm install",
27
+ "docker-npm-run-upgrade": "docker run --rm --tty --name npm-run-upgrade --volume $PWD:/home/workdir --workdir /home/workdir --user node node:16 npm run upgrade",
24
28
  "lint": "eslint --max-warnings 0 .",
25
29
  "lint-dockerfile": "docker run --rm -i hadolint/hadolint:latest-alpine < docker/Dockerfile",
26
30
  "lint-watch": "git ls-files | entr npm run lint",
27
- "publish-docker-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker buildx build --platform linux/arm64,linux/amd64 -t davidanson/markdownlint-cli2:$VERSION -t davidanson/markdownlint-cli2:latest -f docker/Dockerfile --push .",
28
- "test": "ava test/append-to-array-test.js test/fs-mock-test.js test/markdownlint-cli2-test.js test/markdownlint-cli2-test-exec.js test/markdownlint-cli2-test-fs.js test/markdownlint-cli2-test-main.js test/merge-options-test.js test/resolve-and-require-test.js",
29
- "test-docker-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:$VERSION \"*.md\"",
30
- "test-docker-hub-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker image rm davidanson/markdownlint-cli2:$VERSION davidanson/markdownlint-cli2:latest || true && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:$VERSION \"*.md\" && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:latest \"*.md\"",
31
+ "publish-docker-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker buildx build --platform linux/arm64,linux/amd64 -t davidanson/markdownlint-cli2:v$VERSION -t davidanson/markdownlint-cli2:latest -f docker/Dockerfile --push .",
32
+ "test": "ava --timeout=1m test/append-to-array-test.js test/fs-mock-test.js test/markdownlint-cli2-test.js test/markdownlint-cli2-test-exec.js test/markdownlint-cli2-test-fs.js test/markdownlint-cli2-test-main.js test/merge-options-test.js test/resolve-and-require-test.js",
33
+ "test-docker-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:v$VERSION \"*.md\"",
34
+ "test-docker-hub-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker image rm davidanson/markdownlint-cli2:v$VERSION davidanson/markdownlint-cli2:latest || true && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:v$VERSION \"*.md\" && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:latest \"*.md\"",
31
35
  "test-cover": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 npm test",
32
- "test-watch": "git ls-files | entr npm run test"
36
+ "test-watch": "git ls-files | entr npm run test",
37
+ "update-snapshots": "ava --update-snapshots test/markdownlint-cli2-test-exec.js test/markdownlint-cli2-test-fs.js test/markdownlint-cli2-test-main.js",
38
+ "upgrade": "npx --yes npm-check-updates --upgrade",
39
+ "webworker": "cd webworker && webpack --mode none",
40
+ "webworker-install": "npm run docker-npm-install -- --no-save path-browserify process setimmediate stream-browserify url util webpack-cli"
33
41
  },
34
42
  "engines": {
35
- "node": ">=12"
43
+ "node": ">=14"
36
44
  },
37
45
  "files": [
38
46
  "append-to-array.js",
39
47
  "markdownlint-cli2.js",
48
+ "markdownlint-cli2-config.js",
40
49
  "markdownlint-cli2-fix.js",
41
50
  "merge-options.js",
42
51
  "resolve-and-require.js"
43
52
  ],
44
53
  "dependencies": {
45
- "globby": "~11.0.4",
46
- "markdownlint": "~0.24.0",
47
- "markdownlint-cli2-formatter-default": "^0.0.2",
48
- "markdownlint-rule-helpers": "~0.15.0",
49
- "micromatch": "~4.0.4",
50
- "strip-json-comments": "~3.1.1",
51
- "yaml": "~1.10.2"
54
+ "globby": "13.1.2",
55
+ "markdownlint": "0.26.1",
56
+ "markdownlint-cli2-formatter-default": "0.0.3",
57
+ "micromatch": "4.0.5",
58
+ "strip-json-comments": "5.0.0",
59
+ "yaml": "2.1.1"
52
60
  },
53
61
  "devDependencies": {
54
- "@iktakahiro/markdown-it-katex": "~4.0.1",
55
- "ava": "~3.15.0",
56
- "c8": "~7.8.0",
57
- "cpy": "~8.1.2",
58
- "del": "~6.0.0",
59
- "eslint": "~7.32.0",
60
- "eslint-plugin-node": "~11.1.0",
61
- "eslint-plugin-unicorn": "~35.0.0",
62
- "execa": "~5.1.1",
63
- "markdown-it-emoji": "~2.0.0",
64
- "markdown-it-for-inline": "~0.1.1",
65
- "markdownlint-cli2-formatter-json": "^0.0.4",
66
- "markdownlint-cli2-formatter-junit": "^0.0.3",
67
- "markdownlint-cli2-formatter-pretty": "^0.0.2",
68
- "markdownlint-cli2-formatter-summarize": "^0.0.3",
69
- "markdownlint-rule-titlecase": "~0.1.0",
70
- "npm-run-all": "~4.1.5"
62
+ "@iktakahiro/markdown-it-katex": "4.0.1",
63
+ "ava": "4.3.1",
64
+ "c8": "7.12.0",
65
+ "cpy": "9.0.1",
66
+ "del": "7.0.0",
67
+ "eslint": "8.20.0",
68
+ "eslint-plugin-node": "11.1.0",
69
+ "eslint-plugin-unicorn": "43.0.2",
70
+ "execa": "6.1.0",
71
+ "markdown-it-emoji": "2.0.2",
72
+ "markdown-it-for-inline": "0.1.1",
73
+ "markdownlint-cli2-formatter-json": "0.0.6",
74
+ "markdownlint-cli2-formatter-junit": "0.0.5",
75
+ "markdownlint-cli2-formatter-pretty": "0.0.3",
76
+ "markdownlint-cli2-formatter-summarize": "0.0.5",
77
+ "markdownlint-rule-titlecase": "0.1.0",
78
+ "npm-run-all": "4.1.5"
71
79
  },
72
80
  "keywords": [
73
81
  "markdown",
@@ -4,10 +4,9 @@
4
4
 
5
5
  /**
6
6
  * Wrapper for calling Node's require.resolve/require with an additional path.
7
- *
8
- * @param {Object} req Node's require function (or equivalent).
9
- * @param {*} id Package identifier to require.
10
- * @param {*} dir Directory to include when resolving paths.
7
+ * @param {Object} req Node's require implementation (or equivalent).
8
+ * @param {String} id Package identifier to require.
9
+ * @param {String} dir Directory to include when resolving paths.
11
10
  * @returns {Object} Exported module content.
12
11
  */
13
12
  const resolveAndRequire = (req, id, dir) => {