html-validate 8.7.4 → 8.9.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.
Files changed (74) hide show
  1. package/dist/cjs/browser.js +7 -21
  2. package/dist/cjs/browser.js.map +1 -1
  3. package/dist/cjs/cli.js +483 -451
  4. package/dist/cjs/cli.js.map +1 -1
  5. package/dist/cjs/core-browser.js +9 -20
  6. package/dist/cjs/core-browser.js.map +1 -1
  7. package/dist/cjs/core-nodejs.js +203 -297
  8. package/dist/cjs/core-nodejs.js.map +1 -1
  9. package/dist/cjs/core.js +9735 -10454
  10. package/dist/cjs/core.js.map +1 -1
  11. package/dist/cjs/elements.js +2311 -2296
  12. package/dist/cjs/elements.js.map +1 -1
  13. package/dist/cjs/html-validate.js +148 -169
  14. package/dist/cjs/html-validate.js.map +1 -1
  15. package/dist/cjs/html5.js.map +1 -1
  16. package/dist/cjs/index.js +8 -21
  17. package/dist/cjs/index.js.map +1 -1
  18. package/dist/cjs/jest-diff.js +26 -36
  19. package/dist/cjs/jest-diff.js.map +1 -1
  20. package/dist/cjs/jest.js +8 -8
  21. package/dist/cjs/jest.js.map +1 -1
  22. package/dist/cjs/matcher-utils.js +12 -28
  23. package/dist/cjs/matcher-utils.js.map +1 -1
  24. package/dist/cjs/matchers-jestonly.js +38 -47
  25. package/dist/cjs/matchers-jestonly.js.map +1 -1
  26. package/dist/cjs/matchers.js +196 -196
  27. package/dist/cjs/matchers.js.map +1 -1
  28. package/dist/cjs/meta-helper.js +40 -60
  29. package/dist/cjs/meta-helper.js.map +1 -1
  30. package/dist/cjs/test-utils.js +26 -47
  31. package/dist/cjs/test-utils.js.map +1 -1
  32. package/dist/cjs/tsdoc-metadata.json +1 -1
  33. package/dist/cjs/utils/natural-join.js +10 -23
  34. package/dist/cjs/utils/natural-join.js.map +1 -1
  35. package/dist/cjs/vitest.js +6 -6
  36. package/dist/cjs/vitest.js.map +1 -1
  37. package/dist/es/browser.js +2 -2
  38. package/dist/es/cli.js +482 -454
  39. package/dist/es/cli.js.map +1 -1
  40. package/dist/es/core-browser.js +10 -21
  41. package/dist/es/core-browser.js.map +1 -1
  42. package/dist/es/core-nodejs.js +204 -299
  43. package/dist/es/core-nodejs.js.map +1 -1
  44. package/dist/es/core.js +9730 -10455
  45. package/dist/es/core.js.map +1 -1
  46. package/dist/es/elements.js +2311 -2296
  47. package/dist/es/elements.js.map +1 -1
  48. package/dist/es/html-validate.js +150 -171
  49. package/dist/es/html-validate.js.map +1 -1
  50. package/dist/es/html5.js.map +1 -1
  51. package/dist/es/index.js +3 -3
  52. package/dist/es/jest-diff.js +11 -21
  53. package/dist/es/jest-diff.js.map +1 -1
  54. package/dist/es/jest.js +8 -8
  55. package/dist/es/jest.js.map +1 -1
  56. package/dist/es/matcher-utils.js +12 -28
  57. package/dist/es/matcher-utils.js.map +1 -1
  58. package/dist/es/matchers-jestonly.js +39 -48
  59. package/dist/es/matchers-jestonly.js.map +1 -1
  60. package/dist/es/matchers.js +196 -196
  61. package/dist/es/matchers.js.map +1 -1
  62. package/dist/es/meta-helper.js +40 -60
  63. package/dist/es/meta-helper.js.map +1 -1
  64. package/dist/es/test-utils.js +26 -47
  65. package/dist/es/test-utils.js.map +1 -1
  66. package/dist/es/utils/natural-join.js +10 -23
  67. package/dist/es/utils/natural-join.js.map +1 -1
  68. package/dist/es/vitest.js +6 -6
  69. package/dist/es/vitest.js.map +1 -1
  70. package/dist/schema/elements.json +6 -0
  71. package/dist/tsdoc-metadata.json +1 -1
  72. package/dist/types/browser.d.ts +78 -32
  73. package/dist/types/index.d.ts +105 -34
  74. package/package.json +15 -15
package/dist/es/cli.js CHANGED
@@ -1,532 +1,560 @@
1
- import { l as legacyRequire, F as FileSystemConfigLoader, n as nodejsResolver } from './core-nodejs.js';
2
- import { g as getFormatter$1, U as UserError, e as ensureError, i as ignore, d as deepmerge, H as HtmlValidate, x as Reporter } from './core.js';
1
+ import { l as legacyRequire, F as FileSystemConfigLoader, c as cjsResolver } from './core-nodejs.js';
2
+ import { U as UserError, e as ensureError, g as getFormatter$1, i as ignore, d as deepmerge, S as Severity, H as HtmlValidate, y as Reporter } from './core.js';
3
3
  import path from 'node:path';
4
4
  import fs from 'fs';
5
5
  import { globSync } from 'glob';
6
6
  import prompts from 'prompts';
7
7
  import './meta-helper.js';
8
+ import fs$1 from 'node:fs';
9
+ import betterAjvErrors from '@sidvind/better-ajv-errors';
8
10
  import kleur from 'kleur';
9
11
 
10
12
  const DEFAULT_EXTENSIONS = ["html"];
11
13
  function isDirectory(filename) {
12
- const st = fs.statSync(filename);
13
- return st.isDirectory();
14
+ const st = fs.statSync(filename);
15
+ return st.isDirectory();
14
16
  }
15
17
  function join(stem, filename) {
16
- if (path.isAbsolute(filename)) {
17
- return path.normalize(filename);
18
- }
19
- else {
20
- return path.normalize(path.join(stem, filename));
21
- }
18
+ if (path.isAbsolute(filename)) {
19
+ return path.normalize(filename);
20
+ } else {
21
+ return path.normalize(path.join(stem, filename));
22
+ }
22
23
  }
23
24
  function directoryPattern(extensions) {
24
- switch (extensions.length) {
25
- case 0:
26
- return "**/*";
27
- case 1:
28
- return `**/*.${extensions[0]}`;
29
- default:
30
- return `**/*.{${extensions.join(",")}}`;
31
- }
25
+ switch (extensions.length) {
26
+ case 0:
27
+ return "**/*";
28
+ case 1:
29
+ return `**/*.${extensions[0]}`;
30
+ default:
31
+ return `**/*.{${extensions.join(",")}}`;
32
+ }
32
33
  }
33
- /**
34
- * Takes a number of file patterns (globs) and returns array of expanded
35
- * filenames.
36
- */
37
34
  function expandFiles(patterns, options) {
38
- var _a, _b;
39
- const cwd = (_a = options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
40
- const extensions = (_b = options.extensions) !== null && _b !== void 0 ? _b : DEFAULT_EXTENSIONS;
41
- const files = patterns.reduce((result, pattern) => {
42
- /* process - as standard input */
43
- if (pattern === "-") {
44
- result.push("/dev/stdin");
45
- return result;
46
- }
47
- for (const filename of globSync(pattern, { cwd })) {
48
- /* if file is a directory recursively expand files from it */
49
- const fullpath = join(cwd, filename);
50
- if (isDirectory(fullpath)) {
51
- const dir = expandFiles([directoryPattern(extensions)], { ...options, cwd: fullpath });
52
- result = result.concat(dir.map((cur) => join(filename, cur)));
53
- continue;
54
- }
55
- result.push(fullpath);
56
- }
57
- return result.sort((a, b) => {
58
- const pa = a.split("/").length;
59
- const pb = b.split("/").length;
60
- if (pa !== pb) {
61
- return pa - pb;
62
- }
63
- else {
64
- return a > b ? 1 : -1;
65
- }
66
- });
67
- }, []);
68
- /* only return unique matches */
69
- return Array.from(new Set(files));
35
+ const cwd = options.cwd ?? process.cwd();
36
+ const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
37
+ const files = patterns.reduce((result, pattern) => {
38
+ if (pattern === "-") {
39
+ result.push("/dev/stdin");
40
+ return result;
41
+ }
42
+ for (const filename of globSync(pattern, { cwd })) {
43
+ const fullpath = join(cwd, filename);
44
+ if (isDirectory(fullpath)) {
45
+ const dir = expandFiles([directoryPattern(extensions)], { ...options, cwd: fullpath });
46
+ result = result.concat(dir.map((cur) => join(filename, cur)));
47
+ continue;
48
+ }
49
+ result.push(fullpath);
50
+ }
51
+ return result.sort((a, b) => {
52
+ const pa = a.split("/").length;
53
+ const pb = b.split("/").length;
54
+ if (pa !== pb) {
55
+ return pa - pb;
56
+ } else {
57
+ return a > b ? 1 : -1;
58
+ }
59
+ });
60
+ }, []);
61
+ return Array.from(new Set(files));
70
62
  }
71
63
 
72
64
  function wrap(formatter, dst) {
73
- return (results) => {
74
- const output = formatter(results);
75
- if (dst) {
76
- const dir = path.dirname(dst);
77
- if (!fs.existsSync(dir)) {
78
- fs.mkdirSync(dir, { recursive: true });
79
- }
80
- fs.writeFileSync(dst, output, "utf-8");
81
- return "";
82
- }
83
- else {
84
- return output;
85
- }
86
- };
65
+ return (results) => {
66
+ const output = formatter(results);
67
+ if (dst) {
68
+ const dir = path.dirname(dst);
69
+ if (!fs.existsSync(dir)) {
70
+ fs.mkdirSync(dir, { recursive: true });
71
+ }
72
+ fs.writeFileSync(dst, output, "utf-8");
73
+ return "";
74
+ } else {
75
+ return output;
76
+ }
77
+ };
87
78
  }
88
79
  function loadFormatter(name) {
89
- const fn = getFormatter$1(name);
90
- if (fn) {
91
- return fn;
92
- }
93
- try {
94
- return legacyRequire(name);
95
- }
96
- catch (error) {
97
- throw new UserError(`No formatter named "${name}"`, ensureError(error));
98
- }
80
+ const fn = getFormatter$1(name);
81
+ if (fn) {
82
+ return fn;
83
+ }
84
+ try {
85
+ return legacyRequire(name);
86
+ } catch (error) {
87
+ throw new UserError(`No formatter named "${name}"`, ensureError(error));
88
+ }
99
89
  }
100
90
  function getFormatter(formatters) {
101
- const fn = formatters.split(",").map((cur) => {
102
- const [name, dst] = cur.split("=", 2);
103
- const fn = loadFormatter(name);
104
- return wrap(fn, dst);
105
- });
106
- return (report) => {
107
- return fn
108
- .map((formatter) => formatter(report.results))
109
- .filter(Boolean)
110
- .join("\n");
111
- };
91
+ const fn = formatters.split(",").map((cur) => {
92
+ const [name, dst] = cur.split("=", 2);
93
+ const fn2 = loadFormatter(name);
94
+ return wrap(fn2, dst);
95
+ });
96
+ return (report) => {
97
+ return fn.map((formatter) => formatter(report.results)).filter(Boolean).join("\n");
98
+ };
112
99
  }
113
100
 
114
101
  class IsIgnored {
115
- constructor() {
116
- this.cacheIgnore = new Map();
117
- }
118
- /**
119
- * Searches ".htmlvalidateignore" files from filesystem and returns `true` if
120
- * one of them contains a pattern matching given filename.
121
- */
122
- isIgnored(filename) {
123
- return this.match(filename);
124
- }
125
- /**
126
- * Clear cache
127
- */
128
- clearCache() {
129
- this.cacheIgnore.clear();
130
- }
131
- match(target) {
132
- let current = path.dirname(target);
133
- // eslint-disable-next-line no-constant-condition -- breaks out when filesystem is traversed
134
- while (true) {
135
- const relative = path.relative(current, target);
136
- const filename = path.join(current, ".htmlvalidateignore");
137
- /* test filename (relative to the ignore file) against the patterns */
138
- const ig = this.parseFile(filename);
139
- if (ig && ig.ignores(relative)) {
140
- return true;
141
- }
142
- /* get the parent directory */
143
- const child = current;
144
- current = path.dirname(current);
145
- /* stop if this is the root directory */
146
- if (current === child) {
147
- break;
148
- }
149
- }
150
- return false;
151
- }
152
- parseFile(filename) {
153
- if (this.cacheIgnore.has(filename)) {
154
- return this.cacheIgnore.get(filename);
155
- }
156
- if (!fs.existsSync(filename)) {
157
- this.cacheIgnore.set(filename, undefined);
158
- return undefined;
159
- }
160
- const content = fs.readFileSync(filename, "utf-8");
161
- const ig = ignore().add(content);
162
- this.cacheIgnore.set(filename, ig);
163
- return ig;
164
- }
102
+ constructor() {
103
+ this.cacheIgnore = /* @__PURE__ */ new Map();
104
+ }
105
+ /**
106
+ * Searches ".htmlvalidateignore" files from filesystem and returns `true` if
107
+ * one of them contains a pattern matching given filename.
108
+ */
109
+ isIgnored(filename) {
110
+ return this.match(filename);
111
+ }
112
+ /**
113
+ * Clear cache
114
+ */
115
+ clearCache() {
116
+ this.cacheIgnore.clear();
117
+ }
118
+ match(target) {
119
+ let current = path.dirname(target);
120
+ while (true) {
121
+ const relative = path.relative(current, target);
122
+ const filename = path.join(current, ".htmlvalidateignore");
123
+ const ig = this.parseFile(filename);
124
+ if (ig && ig.ignores(relative)) {
125
+ return true;
126
+ }
127
+ const child = current;
128
+ current = path.dirname(current);
129
+ if (current === child) {
130
+ break;
131
+ }
132
+ }
133
+ return false;
134
+ }
135
+ parseFile(filename) {
136
+ if (this.cacheIgnore.has(filename)) {
137
+ return this.cacheIgnore.get(filename);
138
+ }
139
+ if (!fs.existsSync(filename)) {
140
+ this.cacheIgnore.set(filename, void 0);
141
+ return void 0;
142
+ }
143
+ const content = fs.readFileSync(filename, "utf-8");
144
+ const ig = ignore().add(content);
145
+ this.cacheIgnore.set(filename, ig);
146
+ return ig;
147
+ }
165
148
  }
166
149
 
167
- var Frameworks;
168
- (function (Frameworks) {
169
- Frameworks["angularjs"] = "AngularJS";
170
- Frameworks["vuejs"] = "Vue.js";
171
- Frameworks["markdown"] = "Markdown";
172
- })(Frameworks || (Frameworks = {}));
173
150
  const frameworkConfig = {
174
- [Frameworks.angularjs]: {
175
- transform: {
176
- "^.*\\.js$": "html-validate-angular/js",
177
- "^.*\\.html$": "html-validate-angular/html",
178
- },
179
- },
180
- [Frameworks.vuejs]: {
181
- plugins: ["html-validate-vue"],
182
- extends: ["html-validate-vue:recommended"],
183
- transform: {
184
- "^.*\\.vue$": "html-validate-vue",
185
- },
186
- },
187
- [Frameworks.markdown]: {
188
- transform: {
189
- "^.*\\.md$": "html-validate-markdown",
190
- },
191
- },
151
+ ["AngularJS" /* angularjs */]: {
152
+ transform: {
153
+ "^.*\\.js$": "html-validate-angular/js",
154
+ "^.*\\.html$": "html-validate-angular/html"
155
+ }
156
+ },
157
+ ["Vue.js" /* vuejs */]: {
158
+ plugins: ["html-validate-vue"],
159
+ extends: ["html-validate-vue:recommended"],
160
+ transform: {
161
+ "^.*\\.vue$": "html-validate-vue"
162
+ }
163
+ },
164
+ ["Markdown" /* markdown */]: {
165
+ transform: {
166
+ "^.*\\.md$": "html-validate-markdown"
167
+ }
168
+ }
192
169
  };
193
170
  function addFrameworks(src, frameworks) {
194
- let config = src;
195
- for (const framework of frameworks) {
196
- config = deepmerge(config, frameworkConfig[framework]);
197
- }
198
- return config;
171
+ let config = src;
172
+ for (const framework of frameworks) {
173
+ config = deepmerge(config, frameworkConfig[framework]);
174
+ }
175
+ return config;
199
176
  }
200
177
  function writeConfig(dst, config) {
201
- return new Promise((resolve, reject) => {
202
- fs.writeFile(dst, JSON.stringify(config, null, 2), (err) => {
203
- if (err)
204
- reject(err);
205
- resolve();
206
- });
178
+ return new Promise((resolve, reject) => {
179
+ fs.writeFile(dst, JSON.stringify(config, null, 2), (err) => {
180
+ if (err)
181
+ reject(err);
182
+ resolve();
207
183
  });
184
+ });
208
185
  }
209
186
  async function init$1(cwd) {
210
- const filename = `${cwd}/.htmlvalidate.json`;
211
- const exists = fs.existsSync(filename);
212
- const initialConfig = {
213
- elements: ["html5"],
214
- extends: ["html-validate:recommended"],
215
- };
216
- /* confirm overwrite */
217
- if (exists) {
218
- const result = await prompts({
219
- name: "overwrite",
220
- type: "confirm",
221
- message: "A .htmlvalidate.json file already exists, do you want to overwrite it?",
222
- });
223
- if (!result.overwrite) {
224
- return Promise.reject();
225
- }
226
- }
227
- const questions = [
228
- {
229
- name: "frameworks",
230
- type: "multiselect",
231
- choices: [
232
- { title: Frameworks.angularjs, value: Frameworks.angularjs },
233
- { title: Frameworks.vuejs, value: Frameworks.vuejs },
234
- { title: Frameworks.markdown, value: Frameworks.markdown },
235
- ],
236
- message: "Support additional frameworks?",
237
- },
238
- ];
239
- /* prompt user for questions */
240
- const answers = await prompts(questions);
241
- /* write configuration to file */
242
- let config = initialConfig;
243
- config = addFrameworks(config, answers.frameworks);
244
- await writeConfig(filename, config);
245
- return {
246
- filename,
247
- };
187
+ const filename = `${cwd}/.htmlvalidate.json`;
188
+ const exists = fs.existsSync(filename);
189
+ const initialConfig = {
190
+ elements: ["html5"],
191
+ extends: ["html-validate:recommended"]
192
+ };
193
+ if (exists) {
194
+ const result = await prompts({
195
+ name: "overwrite",
196
+ type: "confirm",
197
+ message: "A .htmlvalidate.json file already exists, do you want to overwrite it?"
198
+ });
199
+ if (!result.overwrite) {
200
+ return Promise.reject();
201
+ }
202
+ }
203
+ const questions = [
204
+ {
205
+ name: "frameworks",
206
+ type: "multiselect",
207
+ choices: [
208
+ { title: "AngularJS" /* angularjs */, value: "AngularJS" /* angularjs */ },
209
+ { title: "Vue.js" /* vuejs */, value: "Vue.js" /* vuejs */ },
210
+ { title: "Markdown" /* markdown */, value: "Markdown" /* markdown */ }
211
+ ],
212
+ message: "Support additional frameworks?"
213
+ }
214
+ ];
215
+ const answers = await prompts(questions);
216
+ let config = initialConfig;
217
+ config = addFrameworks(config, answers.frameworks);
218
+ await writeConfig(filename, config);
219
+ return {
220
+ filename
221
+ };
222
+ }
223
+
224
+ function parseSeverity(ruleId, severity) {
225
+ switch (severity) {
226
+ case "off":
227
+ case "0":
228
+ return Severity.DISABLED;
229
+ case "warn":
230
+ case "1":
231
+ return Severity.WARN;
232
+ case "error":
233
+ case "2":
234
+ return Severity.ERROR;
235
+ default:
236
+ throw new Error(`Invalid severity "${severity}" for rule "${ruleId}"`);
237
+ }
238
+ }
239
+
240
+ function parseItem(value) {
241
+ if (value.includes(":")) {
242
+ const [ruleId, severity] = value.split(":", 2);
243
+ return { ruleId, severity: parseSeverity(ruleId, severity) };
244
+ } else {
245
+ return { ruleId: value, severity: Severity.ERROR };
246
+ }
247
+ }
248
+ function getRuleConfig(values) {
249
+ if (typeof values === "string") {
250
+ return getRuleConfig([values]);
251
+ }
252
+ return values.reduce((parsedRules, value) => {
253
+ const { ruleId, severity } = parseItem(value.trim());
254
+ return { [ruleId]: severity, ...parsedRules };
255
+ }, {});
248
256
  }
249
257
 
250
258
  const defaultConfig = {
251
- extends: ["html-validate:recommended"],
259
+ extends: ["html-validate:recommended"]
252
260
  };
253
261
  function getBaseConfig(filename) {
254
- if (filename) {
255
- const resolver = nodejsResolver();
256
- const configData = resolver.resolveConfig(path.resolve(filename), { cache: false });
257
- if (!configData) {
258
- throw new UserError(`Failed to read configuration from "${filename}"`);
259
- }
260
- return configData;
261
- }
262
- else {
263
- return defaultConfig;
264
- }
262
+ if (filename) {
263
+ const resolver = cjsResolver();
264
+ const configData = resolver.resolveConfig(path.resolve(filename), { cache: false });
265
+ if (!configData) {
266
+ throw new UserError(`Failed to read configuration from "${filename}"`);
267
+ }
268
+ return configData;
269
+ } else {
270
+ return defaultConfig;
271
+ }
265
272
  }
266
- /**
267
- * @public
268
- */
269
273
  class CLI {
270
- /**
271
- * Create new CLI helper.
272
- *
273
- * Can be used to create tooling with similar properties to bundled CLI
274
- * script.
275
- */
276
- constructor(options) {
277
- this.options = options !== null && options !== void 0 ? options : {};
278
- this.config = this.resolveConfig();
279
- this.loader = new FileSystemConfigLoader(this.config);
280
- this.ignored = new IsIgnored();
281
- }
282
- /**
283
- * Returns list of files matching patterns and are not ignored. Filenames will
284
- * have absolute paths.
285
- *
286
- * @public
287
- */
288
- expandFiles(patterns, options = {}) {
289
- return expandFiles(patterns, options).filter((filename) => !this.isIgnored(filename));
290
- }
291
- getFormatter(formatters) {
292
- return getFormatter(formatters);
293
- }
294
- /**
295
- * Initialize project with a new configuration.
296
- *
297
- * A new `.htmlvalidate.json` file will be placed in the path provided by
298
- * `cwd`.
299
- */
300
- init(cwd) {
301
- return init$1(cwd);
302
- }
303
- /**
304
- * Searches ".htmlvalidateignore" files from filesystem and returns `true` if
305
- * one of them contains a pattern matching given filename.
306
- */
307
- isIgnored(filename) {
308
- return this.ignored.isIgnored(filename);
309
- }
310
- /**
311
- * Clear cache.
312
- *
313
- * Previously fetched [[HtmlValidate]] instances must either be fetched again
314
- * or call [[HtmlValidate.flushConfigCache]].
315
- */
316
- /* istanbul ignore next: each method is tested separately */
317
- clearCache() {
318
- this.loader.flushCache();
319
- this.ignored.clearCache();
320
- }
321
- /**
322
- * Get HtmlValidate instance with configuration based on options passed to the
323
- * constructor.
324
- *
325
- * @internal
326
- */
327
- getLoader() {
328
- return this.loader;
329
- }
330
- /**
331
- * Get HtmlValidate instance with configuration based on options passed to the
332
- * constructor.
333
- *
334
- * @public
335
- */
336
- getValidator() {
337
- const loader = this.getLoader();
338
- return new HtmlValidate(loader);
339
- }
340
- /**
341
- * @internal
342
- */
343
- getConfig() {
344
- return this.config;
345
- }
346
- resolveConfig() {
347
- const { options } = this;
348
- const config = getBaseConfig(options.configFile);
349
- if (options.rules) {
350
- const rules = Array.isArray(options.rules) ? options.rules : [options.rules];
351
- try {
352
- const severityMap = { off: 0, warn: 1, error: 2 };
353
- const ruleConfig = rules.reduce((parsedRules, rule) => {
354
- var _a;
355
- const [ruleName, ruleSeverity] = rule.trim().split(":");
356
- const severityValue = (_a = severityMap[ruleSeverity]) !== null && _a !== void 0 ? _a : parseInt(ruleSeverity, 10);
357
- if (!Object.values(severityMap).includes(severityValue)) {
358
- throw new Error(`Invalid severity value for rule "${ruleName}": ${ruleSeverity}`);
359
- }
360
- parsedRules[ruleName] = severityValue;
361
- return parsedRules;
362
- }, {});
363
- config.extends = [];
364
- config.rules = ruleConfig;
365
- }
366
- catch (err) /* istanbul ignore next */ {
367
- const message = err instanceof Error ? err.message : String(err);
368
- throw new UserError(`Error while parsing --rule option "{${rules.join(",")}": ${message}.\n`);
369
- }
370
- }
371
- return config;
274
+ /**
275
+ * Create new CLI helper.
276
+ *
277
+ * Can be used to create tooling with similar properties to bundled CLI
278
+ * script.
279
+ */
280
+ constructor(options) {
281
+ this.options = options ?? {};
282
+ this.config = null;
283
+ this.loader = null;
284
+ this.ignored = new IsIgnored();
285
+ }
286
+ /**
287
+ * Returns list of files matching patterns and are not ignored. Filenames will
288
+ * have absolute paths.
289
+ *
290
+ * @public
291
+ */
292
+ expandFiles(patterns, options = {}) {
293
+ return expandFiles(patterns, options).filter((filename) => !this.isIgnored(filename));
294
+ }
295
+ getFormatter(formatters) {
296
+ return getFormatter(formatters);
297
+ }
298
+ /**
299
+ * Initialize project with a new configuration.
300
+ *
301
+ * A new `.htmlvalidate.json` file will be placed in the path provided by
302
+ * `cwd`.
303
+ */
304
+ init(cwd) {
305
+ return init$1(cwd);
306
+ }
307
+ /**
308
+ * Searches ".htmlvalidateignore" files from filesystem and returns `true` if
309
+ * one of them contains a pattern matching given filename.
310
+ */
311
+ isIgnored(filename) {
312
+ return this.ignored.isIgnored(filename);
313
+ }
314
+ /**
315
+ * Clear cache.
316
+ *
317
+ * Previously fetched [[HtmlValidate]] instances must either be fetched again
318
+ * or call [[HtmlValidate.flushConfigCache]].
319
+ */
320
+ /* istanbul ignore next: each method is tested separately */
321
+ clearCache() {
322
+ if (this.loader) {
323
+ this.loader.flushCache();
324
+ }
325
+ this.ignored.clearCache();
326
+ }
327
+ /**
328
+ * Get HtmlValidate instance with configuration based on options passed to the
329
+ * constructor.
330
+ *
331
+ * @internal
332
+ */
333
+ getLoader() {
334
+ if (!this.loader) {
335
+ this.loader = new FileSystemConfigLoader(this.getConfig());
336
+ }
337
+ return this.loader;
338
+ }
339
+ /**
340
+ * Get HtmlValidate instance with configuration based on options passed to the
341
+ * constructor.
342
+ *
343
+ * @public
344
+ */
345
+ getValidator() {
346
+ const loader = this.getLoader();
347
+ return new HtmlValidate(loader);
348
+ }
349
+ /**
350
+ * @internal
351
+ */
352
+ getConfig() {
353
+ if (!this.config) {
354
+ this.config = this.resolveConfig();
355
+ }
356
+ return this.config;
357
+ }
358
+ resolveConfig() {
359
+ const { options } = this;
360
+ const config = getBaseConfig(options.configFile);
361
+ if (options.rules) {
362
+ config.extends = [];
363
+ config.rules = getRuleConfig(options.rules);
372
364
  }
365
+ return config;
366
+ }
367
+ }
368
+
369
+ function prettyError(err) {
370
+ let json;
371
+ if (err.filename && fs$1.existsSync(err.filename)) {
372
+ json = fs$1.readFileSync(err.filename, "utf-8");
373
+ }
374
+ return betterAjvErrors(err.schema, err.obj, err.errors, {
375
+ format: "cli",
376
+ indent: 2,
377
+ json
378
+ });
379
+ }
380
+ function handleSchemaValidationError(console, err) {
381
+ if (err.filename) {
382
+ const filename = path.relative(process.cwd(), err.filename);
383
+ console.error(kleur.red(`A configuration error was found in "${filename}":`));
384
+ } else {
385
+ console.error(kleur.red(`A configuration error was found:`));
386
+ }
387
+ console.group();
388
+ {
389
+ console.error(prettyError(err));
390
+ }
391
+ console.groupEnd();
373
392
  }
374
393
 
375
- /**
376
- * @internal
377
- */
378
- var Mode;
379
- (function (Mode) {
380
- Mode[Mode["LINT"] = 0] = "LINT";
381
- Mode[Mode["INIT"] = 1] = "INIT";
382
- Mode[Mode["DUMP_EVENTS"] = 2] = "DUMP_EVENTS";
383
- Mode[Mode["DUMP_TOKENS"] = 3] = "DUMP_TOKENS";
384
- Mode[Mode["DUMP_TREE"] = 4] = "DUMP_TREE";
385
- Mode[Mode["DUMP_SOURCE"] = 5] = "DUMP_SOURCE";
386
- Mode[Mode["PRINT_CONFIG"] = 6] = "PRINT_CONFIG";
387
- })(Mode || (Mode = {}));
394
+ var Mode = /* @__PURE__ */ ((Mode2) => {
395
+ Mode2[Mode2["LINT"] = 0] = "LINT";
396
+ Mode2[Mode2["INIT"] = 1] = "INIT";
397
+ Mode2[Mode2["DUMP_EVENTS"] = 2] = "DUMP_EVENTS";
398
+ Mode2[Mode2["DUMP_TOKENS"] = 3] = "DUMP_TOKENS";
399
+ Mode2[Mode2["DUMP_TREE"] = 4] = "DUMP_TREE";
400
+ Mode2[Mode2["DUMP_SOURCE"] = 5] = "DUMP_SOURCE";
401
+ Mode2[Mode2["PRINT_CONFIG"] = 6] = "PRINT_CONFIG";
402
+ return Mode2;
403
+ })(Mode || {});
388
404
  function modeToFlag(mode) {
389
- switch (mode) {
390
- case Mode.LINT:
391
- return null;
392
- case Mode.INIT:
393
- return "--init";
394
- case Mode.DUMP_EVENTS:
395
- return "--dump-events";
396
- case Mode.DUMP_TOKENS:
397
- return "--dump-tokens";
398
- case Mode.DUMP_TREE:
399
- return "--dump-tree";
400
- case Mode.DUMP_SOURCE:
401
- return "--dump-source";
402
- case Mode.PRINT_CONFIG:
403
- return "--print-config";
404
- }
405
+ switch (mode) {
406
+ case 0 /* LINT */:
407
+ return null;
408
+ case 1 /* INIT */:
409
+ return "--init";
410
+ case 2 /* DUMP_EVENTS */:
411
+ return "--dump-events";
412
+ case 3 /* DUMP_TOKENS */:
413
+ return "--dump-tokens";
414
+ case 4 /* DUMP_TREE */:
415
+ return "--dump-tree";
416
+ case 5 /* DUMP_SOURCE */:
417
+ return "--dump-source";
418
+ case 6 /* PRINT_CONFIG */:
419
+ return "--print-config";
420
+ }
405
421
  }
406
422
 
407
423
  function renameStdin(report, filename) {
408
- const stdin = report.results.find((cur) => cur.filePath === "/dev/stdin");
409
- if (stdin) {
410
- stdin.filePath = filename;
411
- }
424
+ const stdin = report.results.find((cur) => cur.filePath === "/dev/stdin");
425
+ if (stdin) {
426
+ stdin.filePath = filename;
427
+ }
412
428
  }
413
- function lint(htmlvalidate, output, files, options) {
414
- const reports = files.map((filename) => {
415
- try {
416
- return htmlvalidate.validateFileSync(filename);
417
- }
418
- catch (err) {
419
- const message = kleur.red(`Validator crashed when parsing "${filename}"`);
420
- output.write(`${message}\n`);
421
- throw err;
422
- }
423
- });
424
- const merged = Reporter.merge(reports);
425
- /* rename stdin if an explicit filename was passed */
426
- if (options.stdinFilename) {
427
- renameStdin(merged, options.stdinFilename);
428
- }
429
- output.write(options.formatter(merged));
430
- if (options.maxWarnings >= 0 && merged.warningCount > options.maxWarnings) {
431
- output.write(`\nhtml-validate found too many warnings (maximum: ${options.maxWarnings}).\n`);
432
- return Promise.resolve(false);
433
- }
434
- return Promise.resolve(merged.valid);
429
+ async function lint(htmlvalidate, output, files, options) {
430
+ const reports = files.map(async (filename) => {
431
+ try {
432
+ return await htmlvalidate.validateFile(filename);
433
+ } catch (err) {
434
+ const message = kleur.red(`Validator crashed when parsing "${filename}"`);
435
+ output.write(`${message}
436
+ `);
437
+ throw err;
438
+ }
439
+ });
440
+ const merged = await Reporter.merge(reports);
441
+ if (options.stdinFilename) {
442
+ renameStdin(merged, options.stdinFilename);
443
+ }
444
+ output.write(options.formatter(merged));
445
+ if (options.maxWarnings >= 0 && merged.warningCount > options.maxWarnings) {
446
+ output.write(`
447
+ html-validate found too many warnings (maximum: ${options.maxWarnings}).
448
+ `);
449
+ return false;
450
+ }
451
+ return merged.valid;
435
452
  }
436
453
 
437
454
  async function init(cli, output, options) {
438
- const result = await cli.init(options.cwd);
439
- output.write(`Configuration written to "${result.filename}"\n`);
440
- return true;
455
+ const result = await cli.init(options.cwd);
456
+ output.write(`Configuration written to "${result.filename}"
457
+ `);
458
+ return true;
441
459
  }
442
460
 
443
461
  async function printConfig(htmlvalidate, output, files) {
444
- if (files.length > 1) {
445
- output.write(`\`--print-config\` expected a single filename but got multiple:\n\n`);
446
- for (const filename of files) {
447
- output.write(` - ${filename}\n`);
448
- }
449
- output.write("\n");
450
- return false;
462
+ if (files.length > 1) {
463
+ output.write(`\`--print-config\` expected a single filename but got multiple:
464
+
465
+ `);
466
+ for (const filename of files) {
467
+ output.write(` - ${filename}
468
+ `);
451
469
  }
452
- const config = await htmlvalidate.getConfigFor(files[0]);
453
- const json = JSON.stringify(config.getConfigData(), null, 2);
454
- output.write(`${json}\n`);
455
- return true;
470
+ output.write("\n");
471
+ return false;
472
+ }
473
+ const config = await htmlvalidate.getConfigFor(files[0]);
474
+ const json = JSON.stringify(config.getConfigData(), null, 2);
475
+ output.write(`${json}
476
+ `);
477
+ return true;
456
478
  }
457
479
 
458
480
  const jsonIgnored = [
459
- "annotation",
460
- "blockedRules",
461
- "cache",
462
- "closed",
463
- "depth",
464
- "disabledRules",
465
- "nodeType",
466
- "unique",
467
- "voidElement",
481
+ "annotation",
482
+ "blockedRules",
483
+ "cache",
484
+ "closed",
485
+ "depth",
486
+ "disabledRules",
487
+ "nodeType",
488
+ "unique",
489
+ "voidElement"
468
490
  ];
469
491
  const jsonFiltered = [
470
- "childNodes",
471
- "children",
472
- "data",
473
- "meta",
474
- "metaElement",
475
- "originalData",
476
- "parent",
492
+ "childNodes",
493
+ "children",
494
+ "data",
495
+ "meta",
496
+ "metaElement",
497
+ "originalData",
498
+ "parent"
477
499
  ];
478
500
  function isLocation(key, value) {
479
- return Boolean(value && (key === "location" || key.endsWith("Location")));
501
+ return Boolean(value && (key === "location" || key.endsWith("Location")));
480
502
  }
481
503
  function isIgnored(key) {
482
- return Boolean(key.startsWith("_") || jsonIgnored.includes(key));
504
+ return Boolean(key.startsWith("_") || jsonIgnored.includes(key));
483
505
  }
484
506
  function isFiltered(key, value) {
485
- return Boolean(value && jsonFiltered.includes(key));
507
+ return Boolean(value && jsonFiltered.includes(key));
486
508
  }
487
509
  function eventReplacer(key, value) {
488
- if (isLocation(key, value)) {
489
- return `${value.filename}:${value.line}:${value.column}`;
490
- }
491
- if (isIgnored(key)) {
492
- return undefined;
493
- }
494
- if (isFiltered(key, value)) {
495
- return "[truncated]";
496
- }
497
- return value;
510
+ if (isLocation(key, value)) {
511
+ return `${value.filename}:${value.line}:${value.column}`;
512
+ }
513
+ if (isIgnored(key)) {
514
+ return void 0;
515
+ }
516
+ if (isFiltered(key, value)) {
517
+ return "[truncated]";
518
+ }
519
+ return value;
498
520
  }
499
521
  function eventFormatter(entry) {
500
- const strdata = JSON.stringify(entry.data, eventReplacer, 2);
501
- return `${entry.event}: ${strdata}`;
522
+ const strdata = JSON.stringify(entry.data, eventReplacer, 2);
523
+ return `${entry.event}: ${strdata}`;
502
524
  }
503
525
 
504
526
  function dump(htmlvalidate, output, files, mode) {
505
- let lines = [];
506
- switch (mode) {
507
- case Mode.DUMP_EVENTS:
508
- lines = files.map((filename) => htmlvalidate.dumpEvents(filename).map(eventFormatter));
509
- break;
510
- case Mode.DUMP_TOKENS:
511
- lines = files.map((filename) => htmlvalidate.dumpTokens(filename).map((entry) => {
512
- const data = JSON.stringify(entry.data);
513
- return `TOKEN: ${entry.token}\n Data: ${data}\n Location: ${entry.location}`;
514
- }));
515
- break;
516
- case Mode.DUMP_TREE:
517
- lines = files.map((filename) => htmlvalidate.dumpTree(filename));
518
- break;
519
- case Mode.DUMP_SOURCE:
520
- lines = files.map((filename) => htmlvalidate.dumpSource(filename));
521
- break;
522
- default:
523
- throw new Error(`Unknown mode "${mode}"`);
524
- }
525
- const flat = lines.reduce((s, c) => s.concat(c), []);
526
- output.write(flat.join("\n"));
527
- output.write("\n");
528
- return Promise.resolve(true);
527
+ let lines = [];
528
+ switch (mode) {
529
+ case Mode.DUMP_EVENTS:
530
+ lines = files.map(
531
+ (filename) => htmlvalidate.dumpEvents(filename).map(eventFormatter)
532
+ );
533
+ break;
534
+ case Mode.DUMP_TOKENS:
535
+ lines = files.map(
536
+ (filename) => htmlvalidate.dumpTokens(filename).map((entry) => {
537
+ const data = JSON.stringify(entry.data);
538
+ return `TOKEN: ${entry.token}
539
+ Data: ${data}
540
+ Location: ${entry.location}`;
541
+ })
542
+ );
543
+ break;
544
+ case Mode.DUMP_TREE:
545
+ lines = files.map((filename) => htmlvalidate.dumpTree(filename));
546
+ break;
547
+ case Mode.DUMP_SOURCE:
548
+ lines = files.map((filename) => htmlvalidate.dumpSource(filename));
549
+ break;
550
+ default:
551
+ throw new Error(`Unknown mode "${mode}"`);
552
+ }
553
+ const flat = lines.reduce((s, c) => s.concat(c), []);
554
+ output.write(flat.join("\n"));
555
+ output.write("\n");
556
+ return Promise.resolve(true);
529
557
  }
530
558
 
531
- export { CLI as C, Mode as M, dump as d, init as i, lint as l, modeToFlag as m, printConfig as p };
559
+ export { CLI as C, Mode as M, dump as d, handleSchemaValidationError as h, init as i, lint as l, modeToFlag as m, printConfig as p };
532
560
  //# sourceMappingURL=cli.js.map