-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathno-invalid-html.js
84 lines (79 loc) · 2.78 KB
/
no-invalid-html.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { XParser } from '@netflix/x-element/x-parser.js';
const onToken = () => {};
const validate = strings => XParser.parse(strings, onToken);
// Attempts to put a squiggle beneath the whole _line_ which is problematic.
const getLoc = (node, quasis, strings, context) => {
if (context) {
const { index, start } = context;
const quasi = quasis[index];
if (quasi) {
// Most cases will fall here.
const startLine = quasi.loc.start.line;
const startColumn = quasi.loc.start.column;
const string = strings[index];
const stringPrefix = string.slice(0, start ?? 0);
const problemStartLine = stringPrefix.split('\n').length;
const problemLine = string.split('\n')[problemStartLine - 1];
const indentationMatch = problemLine.match(/(^ *)[^ ]/);
const indentation = indentationMatch ? indentationMatch[1].length : 0;
const line = startLine + problemStartLine - 1;
if (line === startLine) {
return { start: { line, column: startColumn + 1 + indentation }, end: { line, column: startColumn + 1 + problemLine.length } };
} else {
return { start: { line, column: indentation }, end: { line, column: problemLine.length } };
}
} else {
// Certain exit criteria will fall here (e.g., missing closing tag).
const nodeEndLine = node.loc.end.line;
const nodeEndColumn = node.loc.end.column;
const line = nodeEndLine;
return { start: { line, column: nodeEndColumn - 1 }, end: { line, column: nodeEndColumn } };
}
} else {
return node.tag.loc;
}
};
// Shortens the full error since we will have contextualization within the IDE.
const format = error => {
const message = error.message;
const firstLine = message.split('\n')[0];
return firstLine;
};
const validateTaggedTemplateExpression = (context, node) => {
if (node.tag.name === 'html') {
const quasis = node.quasi.quasis;
if (quasis) {
const strings = quasis.map(value => value.value.cooked);
strings.raw = quasis.map(value => value.value.raw);
Object.freeze(strings.raw);
Object.freeze(strings);
try {
validate(strings);
} catch (error) {
// https://eslint.org/docs/latest/extend/custom-rules#reporting-problems
context.report({
node,
message: format(error),
loc: getLoc(node, quasis, strings, XParser.getErrorContext(error)),
});
}
}
}
};
export default {
meta: {
name: 'XElement',
type: 'problem',
docs: {
description: 'Enforce html template validation for x-element.',
url: 'https://github.com/Netflix/eslint-plugin-x-element',
},
},
create: context => {
return {
TaggedTemplateExpression: node => {
validateTaggedTemplateExpression(context, node);
},
};
},
};