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.
- package/dist/cjs/browser.js +7 -21
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/cli.js +483 -451
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core-browser.js +9 -20
- package/dist/cjs/core-browser.js.map +1 -1
- package/dist/cjs/core-nodejs.js +203 -297
- package/dist/cjs/core-nodejs.js.map +1 -1
- package/dist/cjs/core.js +9735 -10454
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +2311 -2296
- package/dist/cjs/elements.js.map +1 -1
- package/dist/cjs/html-validate.js +148 -169
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/html5.js.map +1 -1
- package/dist/cjs/index.js +8 -21
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/jest-diff.js +26 -36
- package/dist/cjs/jest-diff.js.map +1 -1
- package/dist/cjs/jest.js +8 -8
- package/dist/cjs/jest.js.map +1 -1
- package/dist/cjs/matcher-utils.js +12 -28
- package/dist/cjs/matcher-utils.js.map +1 -1
- package/dist/cjs/matchers-jestonly.js +38 -47
- package/dist/cjs/matchers-jestonly.js.map +1 -1
- package/dist/cjs/matchers.js +196 -196
- package/dist/cjs/matchers.js.map +1 -1
- package/dist/cjs/meta-helper.js +40 -60
- package/dist/cjs/meta-helper.js.map +1 -1
- package/dist/cjs/test-utils.js +26 -47
- package/dist/cjs/test-utils.js.map +1 -1
- package/dist/cjs/tsdoc-metadata.json +1 -1
- package/dist/cjs/utils/natural-join.js +10 -23
- package/dist/cjs/utils/natural-join.js.map +1 -1
- package/dist/cjs/vitest.js +6 -6
- package/dist/cjs/vitest.js.map +1 -1
- package/dist/es/browser.js +2 -2
- package/dist/es/cli.js +482 -454
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core-browser.js +10 -21
- package/dist/es/core-browser.js.map +1 -1
- package/dist/es/core-nodejs.js +204 -299
- package/dist/es/core-nodejs.js.map +1 -1
- package/dist/es/core.js +9730 -10455
- package/dist/es/core.js.map +1 -1
- package/dist/es/elements.js +2311 -2296
- package/dist/es/elements.js.map +1 -1
- package/dist/es/html-validate.js +150 -171
- package/dist/es/html-validate.js.map +1 -1
- package/dist/es/html5.js.map +1 -1
- package/dist/es/index.js +3 -3
- package/dist/es/jest-diff.js +11 -21
- package/dist/es/jest-diff.js.map +1 -1
- package/dist/es/jest.js +8 -8
- package/dist/es/jest.js.map +1 -1
- package/dist/es/matcher-utils.js +12 -28
- package/dist/es/matcher-utils.js.map +1 -1
- package/dist/es/matchers-jestonly.js +39 -48
- package/dist/es/matchers-jestonly.js.map +1 -1
- package/dist/es/matchers.js +196 -196
- package/dist/es/matchers.js.map +1 -1
- package/dist/es/meta-helper.js +40 -60
- package/dist/es/meta-helper.js.map +1 -1
- package/dist/es/test-utils.js +26 -47
- package/dist/es/test-utils.js.map +1 -1
- package/dist/es/utils/natural-join.js +10 -23
- package/dist/es/utils/natural-join.js.map +1 -1
- package/dist/es/vitest.js +6 -6
- package/dist/es/vitest.js.map +1 -1
- package/dist/schema/elements.json +6 -0
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/types/browser.d.ts +78 -32
- package/dist/types/index.d.ts +105 -34
- package/package.json +15 -15
package/dist/es/cli.js
CHANGED
|
@@ -1,532 +1,560 @@
|
|
|
1
|
-
import { l as legacyRequire, F as FileSystemConfigLoader,
|
|
2
|
-
import {
|
|
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
|
-
|
|
13
|
-
|
|
14
|
+
const st = fs.statSync(filename);
|
|
15
|
+
return st.isDirectory();
|
|
14
16
|
}
|
|
15
17
|
function join(stem, filename) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
259
|
+
extends: ["html-validate:recommended"]
|
|
252
260
|
};
|
|
253
261
|
function getBaseConfig(filename) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
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
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
492
|
+
"childNodes",
|
|
493
|
+
"children",
|
|
494
|
+
"data",
|
|
495
|
+
"meta",
|
|
496
|
+
"metaElement",
|
|
497
|
+
"originalData",
|
|
498
|
+
"parent"
|
|
477
499
|
];
|
|
478
500
|
function isLocation(key, value) {
|
|
479
|
-
|
|
501
|
+
return Boolean(value && (key === "location" || key.endsWith("Location")));
|
|
480
502
|
}
|
|
481
503
|
function isIgnored(key) {
|
|
482
|
-
|
|
504
|
+
return Boolean(key.startsWith("_") || jsonIgnored.includes(key));
|
|
483
505
|
}
|
|
484
506
|
function isFiltered(key, value) {
|
|
485
|
-
|
|
507
|
+
return Boolean(value && jsonFiltered.includes(key));
|
|
486
508
|
}
|
|
487
509
|
function eventReplacer(key, value) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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
|
-
|
|
501
|
-
|
|
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
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|