html-validate 8.7.3 → 8.8.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 (73) 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 +468 -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 +9511 -10450
  10. package/dist/cjs/core.js.map +1 -1
  11. package/dist/cjs/elements.js +2293 -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 +467 -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 +9506 -10451
  45. package/dist/es/core.js.map +1 -1
  46. package/dist/es/elements.js +2293 -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/tsdoc-metadata.json +1 -1
  71. package/dist/types/browser.d.ts +62 -30
  72. package/dist/types/index.d.ts +89 -32
  73. package/package.json +22 -18
package/dist/es/cli.js CHANGED
@@ -1,532 +1,545 @@
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 { g as getFormatter$1, U as UserError, e as ensureError, i as ignore, d as deepmerge, 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
+ };
248
222
  }
249
223
 
250
224
  const defaultConfig = {
251
- extends: ["html-validate:recommended"],
225
+ extends: ["html-validate:recommended"]
252
226
  };
253
227
  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
- }
228
+ if (filename) {
229
+ const resolver = cjsResolver();
230
+ const configData = resolver.resolveConfig(path.resolve(filename), { cache: false });
231
+ if (!configData) {
232
+ throw new UserError(`Failed to read configuration from "${filename}"`);
233
+ }
234
+ return configData;
235
+ } else {
236
+ return defaultConfig;
237
+ }
265
238
  }
266
- /**
267
- * @public
268
- */
269
239
  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;
240
+ /**
241
+ * Create new CLI helper.
242
+ *
243
+ * Can be used to create tooling with similar properties to bundled CLI
244
+ * script.
245
+ */
246
+ constructor(options) {
247
+ this.options = options ?? {};
248
+ this.config = null;
249
+ this.loader = null;
250
+ this.ignored = new IsIgnored();
251
+ }
252
+ /**
253
+ * Returns list of files matching patterns and are not ignored. Filenames will
254
+ * have absolute paths.
255
+ *
256
+ * @public
257
+ */
258
+ expandFiles(patterns, options = {}) {
259
+ return expandFiles(patterns, options).filter((filename) => !this.isIgnored(filename));
260
+ }
261
+ getFormatter(formatters) {
262
+ return getFormatter(formatters);
263
+ }
264
+ /**
265
+ * Initialize project with a new configuration.
266
+ *
267
+ * A new `.htmlvalidate.json` file will be placed in the path provided by
268
+ * `cwd`.
269
+ */
270
+ init(cwd) {
271
+ return init$1(cwd);
272
+ }
273
+ /**
274
+ * Searches ".htmlvalidateignore" files from filesystem and returns `true` if
275
+ * one of them contains a pattern matching given filename.
276
+ */
277
+ isIgnored(filename) {
278
+ return this.ignored.isIgnored(filename);
279
+ }
280
+ /**
281
+ * Clear cache.
282
+ *
283
+ * Previously fetched [[HtmlValidate]] instances must either be fetched again
284
+ * or call [[HtmlValidate.flushConfigCache]].
285
+ */
286
+ /* istanbul ignore next: each method is tested separately */
287
+ clearCache() {
288
+ if (this.loader) {
289
+ this.loader.flushCache();
290
+ }
291
+ this.ignored.clearCache();
292
+ }
293
+ /**
294
+ * Get HtmlValidate instance with configuration based on options passed to the
295
+ * constructor.
296
+ *
297
+ * @internal
298
+ */
299
+ getLoader() {
300
+ if (!this.loader) {
301
+ this.loader = new FileSystemConfigLoader(this.getConfig());
302
+ }
303
+ return this.loader;
304
+ }
305
+ /**
306
+ * Get HtmlValidate instance with configuration based on options passed to the
307
+ * constructor.
308
+ *
309
+ * @public
310
+ */
311
+ getValidator() {
312
+ const loader = this.getLoader();
313
+ return new HtmlValidate(loader);
314
+ }
315
+ /**
316
+ * @internal
317
+ */
318
+ getConfig() {
319
+ if (!this.config) {
320
+ this.config = this.resolveConfig();
321
+ }
322
+ return this.config;
323
+ }
324
+ resolveConfig() {
325
+ const { options } = this;
326
+ const config = getBaseConfig(options.configFile);
327
+ if (options.rules) {
328
+ const rules = Array.isArray(options.rules) ? options.rules : [options.rules];
329
+ try {
330
+ const severityMap = { off: 0, warn: 1, error: 2 };
331
+ const ruleConfig = rules.reduce((parsedRules, rule) => {
332
+ const [ruleName, ruleSeverity] = rule.trim().split(":");
333
+ const severityValue = severityMap[ruleSeverity] ?? parseInt(ruleSeverity, 10);
334
+ if (!Object.values(severityMap).includes(severityValue)) {
335
+ throw new Error(`Invalid severity value for rule "${ruleName}": ${ruleSeverity}`);
336
+ }
337
+ parsedRules[ruleName] = severityValue;
338
+ return parsedRules;
339
+ }, {});
340
+ config.extends = [];
341
+ config.rules = ruleConfig;
342
+ } catch (err) {
343
+ const message = err instanceof Error ? err.message : String(err);
344
+ throw new UserError(
345
+ `Error while parsing --rule option "{${rules.join(",")}": ${message}.
346
+ `
347
+ );
348
+ }
372
349
  }
350
+ return config;
351
+ }
352
+ }
353
+
354
+ function prettyError(err) {
355
+ let json;
356
+ if (err.filename && fs$1.existsSync(err.filename)) {
357
+ json = fs$1.readFileSync(err.filename, "utf-8");
358
+ }
359
+ return betterAjvErrors(err.schema, err.obj, err.errors, {
360
+ format: "cli",
361
+ indent: 2,
362
+ json
363
+ });
364
+ }
365
+ function handleSchemaValidationError(console, err) {
366
+ if (err.filename) {
367
+ const filename = path.relative(process.cwd(), err.filename);
368
+ console.error(kleur.red(`A configuration error was found in "${filename}":`));
369
+ } else {
370
+ console.error(kleur.red(`A configuration error was found:`));
371
+ }
372
+ console.group();
373
+ {
374
+ console.error(prettyError(err));
375
+ }
376
+ console.groupEnd();
373
377
  }
374
378
 
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 = {}));
379
+ var Mode = /* @__PURE__ */ ((Mode2) => {
380
+ Mode2[Mode2["LINT"] = 0] = "LINT";
381
+ Mode2[Mode2["INIT"] = 1] = "INIT";
382
+ Mode2[Mode2["DUMP_EVENTS"] = 2] = "DUMP_EVENTS";
383
+ Mode2[Mode2["DUMP_TOKENS"] = 3] = "DUMP_TOKENS";
384
+ Mode2[Mode2["DUMP_TREE"] = 4] = "DUMP_TREE";
385
+ Mode2[Mode2["DUMP_SOURCE"] = 5] = "DUMP_SOURCE";
386
+ Mode2[Mode2["PRINT_CONFIG"] = 6] = "PRINT_CONFIG";
387
+ return Mode2;
388
+ })(Mode || {});
388
389
  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
- }
390
+ switch (mode) {
391
+ case 0 /* LINT */:
392
+ return null;
393
+ case 1 /* INIT */:
394
+ return "--init";
395
+ case 2 /* DUMP_EVENTS */:
396
+ return "--dump-events";
397
+ case 3 /* DUMP_TOKENS */:
398
+ return "--dump-tokens";
399
+ case 4 /* DUMP_TREE */:
400
+ return "--dump-tree";
401
+ case 5 /* DUMP_SOURCE */:
402
+ return "--dump-source";
403
+ case 6 /* PRINT_CONFIG */:
404
+ return "--print-config";
405
+ }
405
406
  }
406
407
 
407
408
  function renameStdin(report, filename) {
408
- const stdin = report.results.find((cur) => cur.filePath === "/dev/stdin");
409
- if (stdin) {
410
- stdin.filePath = filename;
411
- }
409
+ const stdin = report.results.find((cur) => cur.filePath === "/dev/stdin");
410
+ if (stdin) {
411
+ stdin.filePath = filename;
412
+ }
412
413
  }
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);
414
+ async function lint(htmlvalidate, output, files, options) {
415
+ const reports = files.map(async (filename) => {
416
+ try {
417
+ return await htmlvalidate.validateFile(filename);
418
+ } catch (err) {
419
+ const message = kleur.red(`Validator crashed when parsing "${filename}"`);
420
+ output.write(`${message}
421
+ `);
422
+ throw err;
423
+ }
424
+ });
425
+ const merged = await Reporter.merge(reports);
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(`
432
+ html-validate found too many warnings (maximum: ${options.maxWarnings}).
433
+ `);
434
+ return false;
435
+ }
436
+ return merged.valid;
435
437
  }
436
438
 
437
439
  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;
440
+ const result = await cli.init(options.cwd);
441
+ output.write(`Configuration written to "${result.filename}"
442
+ `);
443
+ return true;
441
444
  }
442
445
 
443
446
  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;
447
+ if (files.length > 1) {
448
+ output.write(`\`--print-config\` expected a single filename but got multiple:
449
+
450
+ `);
451
+ for (const filename of files) {
452
+ output.write(` - ${filename}
453
+ `);
451
454
  }
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;
455
+ output.write("\n");
456
+ return false;
457
+ }
458
+ const config = await htmlvalidate.getConfigFor(files[0]);
459
+ const json = JSON.stringify(config.getConfigData(), null, 2);
460
+ output.write(`${json}
461
+ `);
462
+ return true;
456
463
  }
457
464
 
458
465
  const jsonIgnored = [
459
- "annotation",
460
- "blockedRules",
461
- "cache",
462
- "closed",
463
- "depth",
464
- "disabledRules",
465
- "nodeType",
466
- "unique",
467
- "voidElement",
466
+ "annotation",
467
+ "blockedRules",
468
+ "cache",
469
+ "closed",
470
+ "depth",
471
+ "disabledRules",
472
+ "nodeType",
473
+ "unique",
474
+ "voidElement"
468
475
  ];
469
476
  const jsonFiltered = [
470
- "childNodes",
471
- "children",
472
- "data",
473
- "meta",
474
- "metaElement",
475
- "originalData",
476
- "parent",
477
+ "childNodes",
478
+ "children",
479
+ "data",
480
+ "meta",
481
+ "metaElement",
482
+ "originalData",
483
+ "parent"
477
484
  ];
478
485
  function isLocation(key, value) {
479
- return Boolean(value && (key === "location" || key.endsWith("Location")));
486
+ return Boolean(value && (key === "location" || key.endsWith("Location")));
480
487
  }
481
488
  function isIgnored(key) {
482
- return Boolean(key.startsWith("_") || jsonIgnored.includes(key));
489
+ return Boolean(key.startsWith("_") || jsonIgnored.includes(key));
483
490
  }
484
491
  function isFiltered(key, value) {
485
- return Boolean(value && jsonFiltered.includes(key));
492
+ return Boolean(value && jsonFiltered.includes(key));
486
493
  }
487
494
  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;
495
+ if (isLocation(key, value)) {
496
+ return `${value.filename}:${value.line}:${value.column}`;
497
+ }
498
+ if (isIgnored(key)) {
499
+ return void 0;
500
+ }
501
+ if (isFiltered(key, value)) {
502
+ return "[truncated]";
503
+ }
504
+ return value;
498
505
  }
499
506
  function eventFormatter(entry) {
500
- const strdata = JSON.stringify(entry.data, eventReplacer, 2);
501
- return `${entry.event}: ${strdata}`;
507
+ const strdata = JSON.stringify(entry.data, eventReplacer, 2);
508
+ return `${entry.event}: ${strdata}`;
502
509
  }
503
510
 
504
511
  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);
512
+ let lines = [];
513
+ switch (mode) {
514
+ case Mode.DUMP_EVENTS:
515
+ lines = files.map(
516
+ (filename) => htmlvalidate.dumpEvents(filename).map(eventFormatter)
517
+ );
518
+ break;
519
+ case Mode.DUMP_TOKENS:
520
+ lines = files.map(
521
+ (filename) => htmlvalidate.dumpTokens(filename).map((entry) => {
522
+ const data = JSON.stringify(entry.data);
523
+ return `TOKEN: ${entry.token}
524
+ Data: ${data}
525
+ Location: ${entry.location}`;
526
+ })
527
+ );
528
+ break;
529
+ case Mode.DUMP_TREE:
530
+ lines = files.map((filename) => htmlvalidate.dumpTree(filename));
531
+ break;
532
+ case Mode.DUMP_SOURCE:
533
+ lines = files.map((filename) => htmlvalidate.dumpSource(filename));
534
+ break;
535
+ default:
536
+ throw new Error(`Unknown mode "${mode}"`);
537
+ }
538
+ const flat = lines.reduce((s, c) => s.concat(c), []);
539
+ output.write(flat.join("\n"));
540
+ output.write("\n");
541
+ return Promise.resolve(true);
529
542
  }
530
543
 
531
- export { CLI as C, Mode as M, dump as d, init as i, lint as l, modeToFlag as m, printConfig as p };
544
+ 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
545
  //# sourceMappingURL=cli.js.map