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 +28 -0
- package/config-schema.json +24 -1
- package/package.json +4 -3
- package/src/bootstrap.js +78 -4
- package/src/cache.js +3 -0
- package/src/cli.js +12 -12
- package/src/glob.js +80 -3
- package/src/logger.js +6 -0
- package/src/output-formatters.js +18 -1
- package/src/plugins.js +47 -22
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.
|
package/config-schema.json
CHANGED
|
@@ -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
|
-
"
|
|
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.
|
|
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": "^
|
|
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": "^
|
|
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
|
-
"
|
|
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
|
-
|
|
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", "
|
|
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
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.
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
7
|
+
class NotFound extends Error {}
|
|
8
|
+
|
|
9
|
+
async function getMatches(pattern) {
|
|
5
10
|
try {
|
|
6
|
-
return await glob(pattern, { dot: 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
|
-
|
|
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);
|
package/src/output-formatters.js
CHANGED
|
@@ -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(
|
|
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
|
|
41
|
-
*
|
|
42
|
-
*
|
|
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 `
|
|
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
|
|
81
|
-
*
|
|
82
|
-
*
|
|
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
|
|
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
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
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,
|