v8r 4.2.1 → 4.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## 📦 [4.4.0](https://www.npmjs.com/package/v8r/v/4.4.0) - 2025-04-26
4
+
5
+ Version 4.4.0 is a deprecation release. This release adds deprecation warnings for
6
+ upcoming breaking changes that will be made in version 5.0
7
+
8
+ * This release adds the `--output-format` CLI argument and `outputFormat` config file key.
9
+ In v8r 4.4.0 `--format` and `format` can still be used as aliases.
10
+ In version 5 `--format` and `format` will be removed.
11
+ It is recommended to switch to using `--output-format` and `outputFormat` now.
12
+ * Starting from v8r version 5, v8r will ignore patterns in `.gitignore` by default.
13
+ * In v8r version 5 the `fileLocation` argument of `getSingleResultLogMessage` will be removed.
14
+ The signature will become `getSingleResultLogMessage(result, format)`.
15
+ Plugins implementing the `getSingleResultLogMessage` plugin hook will need to to update
16
+ the signature to be compatible with version 5.
17
+ If you are using `fileLocation` in the `getSingleResultLogMessage` function body,
18
+ switch to using `result.fileLocation`.
19
+ * Starting from v8r version 5 file paths will no longer be passed to plugins in dot-relative notation.
20
+ Plugins implementing the `getSingleResultLogMessage`, `getAllResultsLogMessage` and `parseInputFile`
21
+ plugin hooks may need to be updated.
22
+
23
+ ## 📦 [4.3.0](https://www.npmjs.com/package/v8r/v/4.3.0) - 2025-04-21
24
+
25
+ * Add ignore patern files. v8r now looks for ignore patterns in `.v8rignore` by default.
26
+ More info: https://chris48s.github.io/v8r/ignoring-files/
27
+ * Include the prop name in `additionalProperty` log message.
28
+ * Allow config file to contain `$schema` key.
29
+ * Fix: Clear the cache on init if TTL is 0.
30
+
3
31
  ## 📦 [4.2.1](https://www.npmjs.com/package/v8r/v/4.2.1) - 2024-12-14
4
32
 
5
33
  * Upgrade to flat-cache 6.
@@ -3,6 +3,13 @@
3
3
  "$schema": "https://json-schema.org/draft/2019-09/schema",
4
4
  "type": "object",
5
5
  "additionalProperties": false,
6
+ "allOf": [
7
+ {
8
+ "not": {
9
+ "required": ["format", "outputFormat"]
10
+ }
11
+ }
12
+ ],
6
13
  "properties": {
7
14
  "cacheTtl": {
8
15
  "description": "Remove cached HTTP responses older than cacheTtl seconds old. Specifying 0 clears and disables cache completely",
@@ -57,14 +64,27 @@
57
64
  }
58
65
  }
59
66
  },
60
- "format": {
67
+ "outputFormat": {
61
68
  "description": "Output format for validation results. 'text' and 'json' are always valid. Plugins may define additional values which are valid here.",
62
69
  "type": "string"
63
70
  },
71
+ "format": {
72
+ "description": "Output format for validation results. 'text' and 'json' are always valid. Plugins may define additional values which are valid here.",
73
+ "type": "string",
74
+ "deprecated": true
75
+ },
64
76
  "ignoreErrors": {
65
77
  "description": "Exit with code 0 even if an error was encountered. True means a non-zero exit code is only issued if validation could be completed successfully and one or more files were invalid",
66
78
  "type": "boolean"
67
79
  },
80
+ "ignorePatternFiles": {
81
+ "description": "A list of files containing glob patterns to ignore",
82
+ "uniqueItems": true,
83
+ "type": "array",
84
+ "items": {
85
+ "type": "string"
86
+ }
87
+ },
68
88
  "patterns": {
69
89
  "type": "array",
70
90
  "description": "One or more filenames or glob patterns describing local file or files to validate",
@@ -87,6 +107,9 @@
87
107
  "type": "string",
88
108
  "pattern": "^(package:|file:)"
89
109
  }
110
+ },
111
+ "$schema": {
112
+ "type": "string"
90
113
  }
91
114
  }
92
115
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "v8r",
3
- "version": "4.2.1",
3
+ "version": "4.4.0",
4
4
  "description": "A command-line JSON, YAML and TOML validator that's on your wavelength",
5
5
  "scripts": {
6
6
  "test": "V8R_CACHE_NAME=v8r-test c8 --reporter=text mocha \"src/**/*.spec.js\"",
@@ -37,6 +37,7 @@
37
37
  "glob": "^10.1.0",
38
38
  "global-agent": "^3.0.0",
39
39
  "got": "^13.0.0",
40
+ "ignore": "^7.0.0",
40
41
  "is-url": "^1.2.4",
41
42
  "js-yaml": "^4.0.0",
42
43
  "json5": "^2.2.0",
@@ -47,13 +48,13 @@
47
48
  "devDependencies": {
48
49
  "c8": "^10.1.2",
49
50
  "eslint": "^9.9.0",
50
- "eslint-config-prettier": "^9.0.0",
51
+ "eslint-config-prettier": "^10.1.2",
51
52
  "eslint-plugin-jsdoc": "^50.2.2",
52
53
  "eslint-plugin-mocha": "^10.0.3",
53
54
  "eslint-plugin-prettier": "^5.0.0",
54
55
  "mocha": "^11.0.1",
55
56
  "mock-cwd": "^1.0.0",
56
- "nock": "^13.0.4",
57
+ "nock": "^14.0.4",
57
58
  "prettier": "^3.0.0",
58
59
  "prettier-plugin-jsdoc": "^1.3.0"
59
60
  },
package/src/bootstrap.js CHANGED
@@ -53,6 +53,8 @@ function mergeConfigs(args, config) {
53
53
  if (config.filepath) {
54
54
  mergedConfig.configFileRelativePath = getRelativeFilePath(config);
55
55
  }
56
+ // https://github.com/chris48s/v8r/issues/494
57
+ delete mergedConfig.format;
56
58
  return mergedConfig;
57
59
  }
58
60
 
@@ -76,13 +78,58 @@ function parseArgs(argv, config, documentFormats, outputFormats) {
76
78
  )} (from config file ${getRelativeFilePath(config)})`;
77
79
  }
78
80
 
81
+ const ignoreFilesOpts = {
82
+ describe: "A list of files containing glob patterns to ignore",
83
+ };
84
+ let ignoreFilesDefault = [".v8rignore"];
85
+ ignoreFilesOpts.defaultDescription = `${JSON.stringify(ignoreFilesDefault)}`;
86
+ if (Object.keys(config.config).includes("ignorePatternFiles")) {
87
+ ignoreFilesDefault = config.config.ignorePatternFiles;
88
+ ignoreFilesOpts.defaultDescription = `${JSON.stringify(
89
+ ignoreFilesDefault,
90
+ )} (from config file ${getRelativeFilePath(config)})`;
91
+ }
92
+
79
93
  parser
80
94
  .command(
95
+ // command
81
96
  command,
97
+
98
+ // description
82
99
  `Validate local ${documentFormats.join("/")} files against schema(s)`,
100
+
101
+ // builder
83
102
  (yargs) => {
84
103
  yargs.positional("patterns", patternsOpts);
85
104
  },
105
+
106
+ // handler
107
+ (args) => {
108
+ /*
109
+ Yargs doesn't allow .conflicts() with an argument that has a default
110
+ value (it considers the arg "set" even if we just use the default)
111
+ so we need to apply the default values here.
112
+ */
113
+ if (args.ignorePatternFiles === undefined) {
114
+ args.ignorePatternFiles = args["ignore-pattern-files"] =
115
+ ignoreFilesDefault;
116
+ }
117
+
118
+ if (args.ignore === false) {
119
+ args.ignorePatternFiles = args["ignore-pattern-files"] = [];
120
+ }
121
+
122
+ if (args.ignore === undefined) {
123
+ args.ignore = true;
124
+ }
125
+
126
+ // https://github.com/chris48s/v8r/issues/494
127
+ if (process.argv.includes("--format")) {
128
+ logger.warning(
129
+ "In v8r version 5 the --format argument will be removed. Switch to using --output-format",
130
+ );
131
+ }
132
+ },
86
133
  )
87
134
  .version(
88
135
  // Workaround for https://github.com/yargs/yargs/issues/1934
@@ -110,7 +157,7 @@ function parseArgs(argv, config, documentFormats, outputFormats) {
110
157
  alias: "c",
111
158
  array: true,
112
159
  describe:
113
- "Local path or URL of custom catalogs to use prior to schemastore.org",
160
+ "A list of local paths or URLs of custom catalogs to use prior to schemastore.org",
114
161
  })
115
162
  .conflicts("schema", "catalogs")
116
163
  .option("ignore-errors", {
@@ -121,6 +168,17 @@ function parseArgs(argv, config, documentFormats, outputFormats) {
121
168
  "means a non-zero exit code is only issued if validation could be " +
122
169
  "completed successfully and one or more files were invalid",
123
170
  })
171
+ .option("ignore-pattern-files", {
172
+ type: "string",
173
+ array: true,
174
+ describe: "A list of files containing glob patterns to ignore",
175
+ ...ignoreFilesOpts,
176
+ })
177
+ .option("no-ignore", {
178
+ type: "boolean",
179
+ describe: "Disable all ignore files",
180
+ })
181
+ .conflicts("ignore-pattern-files", "no-ignore")
124
182
  .option("cache-ttl", {
125
183
  type: "number",
126
184
  default: 600,
@@ -128,11 +186,14 @@ function parseArgs(argv, config, documentFormats, outputFormats) {
128
186
  "Remove cached HTTP responses older than <cache-ttl> seconds old. " +
129
187
  "Passing 0 clears and disables cache completely",
130
188
  })
131
- .option("format", {
189
+ .option("output-format", {
132
190
  type: "string",
133
191
  choices: outputFormats,
134
192
  default: "text",
135
- describe: "Output format for validation results",
193
+ // https://github.com/chris48s/v8r/issues/494
194
+ describe:
195
+ "Output format for validation results. The '--format' alias is deprecated.",
196
+ alias: "format",
136
197
  })
137
198
  .example([
138
199
  ["$0 file.json", "Validate a single file"],
@@ -144,7 +205,7 @@ function parseArgs(argv, config, documentFormats, outputFormats) {
144
205
  ]);
145
206
 
146
207
  for (const [key, value] of Object.entries(config.config)) {
147
- if (["cacheTtl", "format", "ignoreErrors", "verbose"].includes(key)) {
208
+ if (["cacheTtl", "outputFormat", "ignoreErrors", "verbose"].includes(key)) {
148
209
  parser.default(
149
210
  decamelize(key, { separator: "-" }),
150
211
  value,
@@ -190,6 +251,14 @@ async function bootstrap(argv, config, cosmiconfigOptions = {}) {
190
251
  const configFile = await getCosmiConfig(cosmiconfigOptions);
191
252
  validateConfigAgainstSchema(configFile);
192
253
 
254
+ // https://github.com/chris48s/v8r/issues/494
255
+ if (configFile.config.format) {
256
+ logger.warning(
257
+ "In v8r version 5 the 'format' config file key will be removed. Switch to using 'outputFormat'",
258
+ );
259
+ configFile.config.outputFormat = configFile.config.format;
260
+ }
261
+
193
262
  // load both core and user plugins
194
263
  let plugins = resolveUserPlugins(configFile.config.plugins || []);
195
264
  const { allLoadedPlugins, loadedCorePlugins, loadedUserPlugins } =
@@ -205,6 +274,11 @@ async function bootstrap(argv, config, cosmiconfigOptions = {}) {
205
274
  // parse command line arguments
206
275
  const args = parseArgs(argv, configFile, documentFormats, outputFormats);
207
276
 
277
+ // https://github.com/chris48s/v8r/issues/599
278
+ logger.warning(
279
+ "Starting from v8r version 5, v8r will ignore patterns in .gitignore by default.",
280
+ );
281
+
208
282
  return {
209
283
  config: mergeConfigs(args, configFile),
210
284
  allLoadedPlugins,
package/src/cache.js CHANGED
@@ -8,6 +8,9 @@ class Cache {
8
8
  this.ttl = this.cache._cache.ttl || 0;
9
9
  this.callCounter = {};
10
10
  this.callLimit = 10;
11
+ if (this.ttl === 0) {
12
+ this.cache.clear();
13
+ }
11
14
  }
12
15
 
13
16
  limitDepth(url) {
package/src/cli.js CHANGED
@@ -7,7 +7,7 @@ import { validate } from "./ajv.js";
7
7
  import { bootstrap } from "./bootstrap.js";
8
8
  import { Cache } from "./cache.js";
9
9
  import { getCatalogs, getMatchForFilename } from "./catalogs.js";
10
- import { getFiles } from "./glob.js";
10
+ import { getFiles, NotFound } from "./glob.js";
11
11
  import { getFromUrlOrFile } from "./io.js";
12
12
  import logger from "./logger.js";
13
13
  import { getDocumentLocation } from "./output-formatters.js";
@@ -145,7 +145,7 @@ async function validateFile(filename, config, plugins, cache) {
145
145
  const message = plugin.getSingleResultLogMessage(
146
146
  result,
147
147
  filename,
148
- config.format,
148
+ config.outputFormat,
149
149
  );
150
150
  if (message != null) {
151
151
  logger.log(message);
@@ -170,19 +170,16 @@ function resultsToStatusCode(results, ignoreErrors) {
170
170
  function Validator() {
171
171
  return async function (config, plugins) {
172
172
  let filenames = [];
173
- for (const pattern of config.patterns) {
174
- const matches = await getFiles(pattern);
175
- if (matches.length === 0) {
176
- logger.error(`Pattern '${pattern}' did not match any files`);
173
+ try {
174
+ filenames = await getFiles(config.patterns, config.ignorePatternFiles);
175
+ } catch (e) {
176
+ if (e instanceof NotFound) {
177
+ logger.error(e.message);
177
178
  return EXIT.NOT_FOUND;
178
179
  }
179
- filenames = filenames.concat(matches);
180
+ throw e;
180
181
  }
181
182
 
182
- // de-dupe and sort
183
- filenames = [...new Set(filenames)];
184
- filenames.sort((a, b) => a.localeCompare(b));
185
-
186
183
  const ttl = secondsToMilliseconds(config.cacheTtl || 0);
187
184
  const cache = new Cache(getFlatCache(ttl));
188
185
 
@@ -194,7 +191,10 @@ function Validator() {
194
191
  }
195
192
 
196
193
  for (const plugin of plugins) {
197
- const message = plugin.getAllResultsLogMessage(results, config.format);
194
+ const message = plugin.getAllResultsLogMessage(
195
+ results,
196
+ config.outputFormat,
197
+ );
198
198
  if (message != null) {
199
199
  logger.log(message);
200
200
  break;
package/src/glob.js CHANGED
@@ -1,13 +1,90 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
1
3
  import { glob } from "glob";
4
+ import ignore from "ignore";
2
5
  import logger from "./logger.js";
3
6
 
4
- async function getFiles(pattern) {
7
+ class NotFound extends Error {}
8
+
9
+ async function getMatches(pattern) {
5
10
  try {
6
- return await glob(pattern, { dot: true, dotRelative: true });
11
+ return await glob(pattern, { dot: true });
7
12
  } catch (e) {
8
13
  logger.error(e.message);
9
14
  return [];
10
15
  }
11
16
  }
12
17
 
13
- export { getFiles };
18
+ async function exists(path) {
19
+ try {
20
+ await fs.promises.access(path);
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ async function filterIgnores(filenames, ignorePatterns) {
28
+ const ig = ignore();
29
+ for (const patterns of ignorePatterns) {
30
+ ig.add(patterns);
31
+ }
32
+ return ig.filter(filenames);
33
+ }
34
+
35
+ async function readIgnoreFiles(filenames) {
36
+ let content = [];
37
+ for (const filename of filenames) {
38
+ const abspath = path.join(process.cwd(), filename);
39
+ if (await exists(abspath)) {
40
+ content.push(await fs.promises.readFile(abspath, "utf8"));
41
+ }
42
+ }
43
+ return content;
44
+ }
45
+
46
+ function getSeparator() {
47
+ return process.platform == "win32" ? "\\" : "/";
48
+ }
49
+
50
+ async function getFiles(patterns, ignorePatternFiles) {
51
+ let filenames = [];
52
+
53
+ // find all the files matching input globs
54
+ for (const pattern of patterns) {
55
+ const matches = await getMatches(pattern);
56
+ if (matches.length === 0) {
57
+ throw new NotFound(`Pattern '${pattern}' did not match any files`);
58
+ }
59
+ filenames = filenames.concat(matches);
60
+ }
61
+
62
+ // de-dupe
63
+ filenames = [...new Set(filenames)];
64
+
65
+ // process ignores
66
+ const ignorePatterns = await readIgnoreFiles(ignorePatternFiles);
67
+ let filteredFilenames = await filterIgnores(filenames, ignorePatterns);
68
+
69
+ const diff = filenames.filter((x) => filteredFilenames.indexOf(x) < 0);
70
+ if (diff.length > 0) {
71
+ logger.debug(
72
+ `Ignoring file(s):\n ${diff.join("\n ")}\nbased on ignore patterns in\n ${ignorePatternFiles.join("\n ")}`,
73
+ );
74
+ }
75
+
76
+ // finally, sort
77
+ filteredFilenames.sort((a, b) => a.localeCompare(b));
78
+
79
+ if (filteredFilenames.length === 0) {
80
+ throw new NotFound(`Could not find any files to validate`);
81
+ }
82
+
83
+ const sep = getSeparator();
84
+ filteredFilenames = filteredFilenames.map((fn) =>
85
+ !fn.startsWith(".." + sep) ? "." + sep + fn : fn,
86
+ );
87
+ return filteredFilenames;
88
+ }
89
+
90
+ export { getFiles, NotFound };
package/src/logger.js CHANGED
@@ -47,6 +47,12 @@ class Logger {
47
47
  this.writeErr(formatedMessage);
48
48
  }
49
49
 
50
+ warning(message) {
51
+ const formatedMessage = chalk.yellow.bold("▲ ") + message;
52
+ this.stderr.push(formatedMessage);
53
+ this.writeErr(formatedMessage);
54
+ }
55
+
50
56
  error(message) {
51
57
  const formatedMessage = chalk.red.bold("✖ ") + message;
52
58
  this.stderr.push(formatedMessage);
@@ -9,8 +9,25 @@ function getDocumentLocation(result) {
9
9
 
10
10
  function formatErrors(location, errors) {
11
11
  const ajv = new Ajv();
12
+ let formattedErrors = [];
13
+
14
+ if (errors) {
15
+ formattedErrors = errors.map(function (error) {
16
+ if (
17
+ error.keyword === "additionalProperties" &&
18
+ typeof error.params.additionalProperty === "string"
19
+ ) {
20
+ return {
21
+ ...error,
22
+ message: `${error.message}, found additional property '${error.params.additionalProperty}'`,
23
+ };
24
+ }
25
+ return error;
26
+ });
27
+ }
28
+
12
29
  return (
13
- ajv.errorsText(errors, {
30
+ ajv.errorsText(formattedErrors, {
14
31
  separator: "\n",
15
32
  dataVar: location + "#",
16
33
  }) + "\n"
package/src/plugins.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import logger from "./logger.js";
2
3
 
3
4
  /**
4
5
  * Base class for all v8r plugins.
@@ -37,10 +38,9 @@ class BasePlugin {
37
38
  *
38
39
  * @param {string} contents - The unparsed file content.
39
40
  * @param {string} fileLocation - The file path. Filenames are resolved and
40
- * normalised by [glob](https://www.npmjs.com/package/glob) using the
41
- * `dotRelative` option. This means relative paths in the current directory
42
- * will be prefixed with `./` (or `.\` on Windows) even if this was not
43
- * present in the input filename or pattern.
41
+ * normalised using dot-relative notation. This means relative paths in the
42
+ * current directory will be prefixed with `./` (or `.\` on Windows) even if
43
+ * this was not present in the input filename or pattern.
44
44
  * @param {string | undefined} parser - If the user has specified a parser to
45
45
  * use for this file in a custom schema, this will be passed to
46
46
  * `parseInputFile` in the `parser` param.
@@ -54,8 +54,8 @@ class BasePlugin {
54
54
  /**
55
55
  * Use the `registerOutputFormats` hook to tell v8r about additional output
56
56
  * formats that can be generated. Any formats registered with this hook become
57
- * valid values for the `format` property in the config file and the
58
- * `--format` command line argument.
57
+ * valid values for the `outputFormat` property in the config file and the
58
+ * `--output-format` command line argument.
59
59
  *
60
60
  * @returns {string[]} Output formats to register
61
61
  */
@@ -77,12 +77,11 @@ class BasePlugin {
77
77
  * @param {ValidationResult} result - Result of attempting to validate this
78
78
  * document.
79
79
  * @param {string} fileLocation - The document file path. Filenames are
80
- * resolved and normalised by [glob](https://www.npmjs.com/package/glob)
81
- * using the `dotRelative` option. This means relative paths in the current
82
- * directory will be prefixed with `./` (or `.\` on Windows) even if this
83
- * was not present in the input filename or pattern.
80
+ * resolved and normalised using dot-relative notation. This means relative
81
+ * paths in the current directory will be prefixed with `./` (or `.\` on
82
+ * Windows) even if this was not present in the input filename or pattern.
84
83
  * @param {string} format - The user's requested output format as specified in
85
- * the config file or via the `--format` command line argument.
84
+ * the config file or via the `--output-format` command line argument.
86
85
  * @returns {string | undefined} Log message
87
86
  */
88
87
  // eslint-disable-next-line no-unused-vars
@@ -104,7 +103,7 @@ class BasePlugin {
104
103
  * @param {ValidationResult[]} results - Results of attempting to validate
105
104
  * these documents.
106
105
  * @param {string} format - The user's requested output format as specified in
107
- * the config file or via the `--format` command line argument.
106
+ * the config file or via the `--output-format` command line argument.
108
107
  * @returns {string | undefined} Log message
109
108
  */
110
109
  // eslint-disable-next-line no-unused-vars
@@ -125,7 +124,11 @@ class Document {
125
124
  }
126
125
  }
127
126
 
128
- function validatePlugin(plugin) {
127
+ function hasProperty(plugin, prop) {
128
+ return Object.prototype.hasOwnProperty.call(plugin.prototype, prop);
129
+ }
130
+
131
+ function validatePlugin(plugin, warnings) {
129
132
  if (
130
133
  typeof plugin.name !== "string" ||
131
134
  !plugin.name.startsWith("v8r-plugin-")
@@ -152,6 +155,29 @@ function validatePlugin(plugin) {
152
155
  );
153
156
  }
154
157
  }
158
+
159
+ if (warnings === true) {
160
+ // https://github.com/chris48s/v8r/issues/500
161
+ if (hasProperty(plugin, "getSingleResultLogMessage")) {
162
+ logger.warning(
163
+ "In v8r version 5 the fileLocation argument of getSingleResultLogMessage will be removed.\n" +
164
+ " The signature will become getSingleResultLogMessage(result, format).\n" +
165
+ ` ${plugin.name} will need to be updated`,
166
+ );
167
+ }
168
+
169
+ // https://github.com/chris48s/v8r/issues/600
170
+ if (
171
+ hasProperty(plugin, "getSingleResultLogMessage") ||
172
+ hasProperty(plugin, "getAllResultsLogMessage") ||
173
+ hasProperty(plugin, "parseInputFile")
174
+ ) {
175
+ logger.warning(
176
+ "Starting from v8r version 5 file paths will no longer be passed to plugins in dot-relative notation.\n" +
177
+ ` ${plugin.name} may need to be updated`,
178
+ );
179
+ }
180
+ }
155
181
  }
156
182
 
157
183
  function resolveUserPlugins(userPlugins) {
@@ -167,19 +193,19 @@ function resolveUserPlugins(userPlugins) {
167
193
  return plugins;
168
194
  }
169
195
 
170
- async function loadPlugins(plugins) {
196
+ async function loadPlugins(plugins, warnings) {
171
197
  let loadedPlugins = [];
172
198
  for (const plugin of plugins) {
173
199
  loadedPlugins.push(await import(plugin));
174
200
  }
175
201
  loadedPlugins = loadedPlugins.map((plugin) => plugin.default);
176
- loadedPlugins.forEach((plugin) => validatePlugin(plugin));
202
+ loadedPlugins.forEach((plugin) => validatePlugin(plugin, warnings));
177
203
  loadedPlugins = loadedPlugins.map((plugin) => new plugin());
178
204
  return loadedPlugins;
179
205
  }
180
206
 
181
207
  async function loadAllPlugins(userPlugins) {
182
- const loadedUserPlugins = await loadPlugins(userPlugins);
208
+ const loadedUserPlugins = await loadPlugins(userPlugins, true);
183
209
 
184
210
  const corePlugins = [
185
211
  "./plugins/parser-json.js",
@@ -189,7 +215,7 @@ async function loadAllPlugins(userPlugins) {
189
215
  "./plugins/output-text.js",
190
216
  "./plugins/output-json.js",
191
217
  ];
192
- const loadedCorePlugins = await loadPlugins(corePlugins);
218
+ const loadedCorePlugins = await loadPlugins(corePlugins, false);
193
219
 
194
220
  return {
195
221
  allLoadedPlugins: loadedUserPlugins.concat(loadedCorePlugins),
@@ -201,11 +227,10 @@ async function loadAllPlugins(userPlugins) {
201
227
  /**
202
228
  * @typedef {object} ValidationResult
203
229
  * @property {string} fileLocation - Path of the document that was validated.
204
- * Filenames are resolved and normalised by
205
- * [glob](https://www.npmjs.com/package/glob) using the `dotRelative` option.
206
- * This means relative paths in the current directory will be prefixed with
207
- * `./` (or `.\` on Windows) even if this was not present in the input
208
- * filename or pattern.
230
+ * Filenames are resolved and normalised using dot-relative notation. This
231
+ * means relative paths in the current directory will be prefixed with `./`
232
+ * (or `.\` on Windows) even if this was not present in the input filename or
233
+ * pattern.
209
234
  * @property {number | null} documentIndex - Some file formats allow multiple
210
235
  * documents to be embedded in one file (e.g:
211
236
  * [yaml](https://www.yaml.info/learn/document.html)). In these cases,