Skip to content

Commit 518a1a2

Browse files
thedaviddiasa.obitskyi
authored and
Christopher Quadflieg
committed
feat: Add tags checking rule - allows specify rules for any tag and validate that (#384)
* adding tags check rule * fix missing commas * add polifil for old JS engines * add polifil for old JS engines * fix missing commas * fix indexOf * incrace code covarage * incrace code covarage * review fix * fix formating * fixing issues Co-authored-by: a.obitskyi <[email protected]>
1 parent fe4f3c6 commit 518a1a2

File tree

3 files changed

+184
-269
lines changed

3 files changed

+184
-269
lines changed

src/rules/index.js

+29-31
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,29 @@
1-
export { default as altRequire } from './alt-require'
2-
export { default as attrLowercase } from './attr-lowercase'
3-
export { default as attrSort } from './attr-sorted'
4-
export { default as attrNoDuplication } from './attr-no-duplication'
5-
export { default as attrUnsafeChars } from './attr-unsafe-chars'
6-
export { default as attrValueDoubleQuotes } from './attr-value-double-quotes'
7-
export { default as attrValueNotEmpty } from './attr-value-not-empty'
8-
export { default as attrValueSingleQuotes } from './attr-value-single-quotes'
9-
export { default as attrWhitespace } from './attr-whitespace'
10-
export { default as doctypeFirst } from './doctype-first'
11-
export { default as doctypeHTML5 } from './doctype-html5'
12-
export { default as headScriptDisabled } from './head-script-disabled'
13-
export { default as hrefAbsOrRel } from './href-abs-or-rel'
14-
export { default as idClsasAdDisabled } from './id-class-ad-disabled'
15-
export { default as idClassValue } from './id-class-value'
16-
export { default as idUnique } from './id-unique'
17-
export { default as inlineScriptDisabled } from './inline-script-disabled'
18-
export { default as inlineStyleDisabled } from './inline-style-disabled'
19-
export { default as inputRequiresLabel } from './input-requires-label'
20-
export { default as scriptDisabled } from './script-disabled'
21-
export { default as spaceTabMixedDisabled } from './space-tab-mixed-disabled'
22-
export { default as specCharEscape } from './spec-char-escape'
23-
export { default as srcNotEmpty } from './src-not-empty'
24-
export { default as styleDisabled } from './style-disabled'
25-
export { default as tagPair } from './tag-pair'
26-
export { default as tagSelfClose } from './tag-self-close'
27-
export { default as tagnameLowercase } from './tagname-lowercase'
28-
export { default as tagnameSpecialChars } from './tagname-specialchars'
29-
export { default as titleRequire } from './title-require'
30-
export { default as tagsCheck } from './tags-check'
31-
export { default as attrNoUnnecessaryWhitespace } from './attr-no-unnecessary-whitespace'
1+
export { default as altRequire } from './alt-require';
2+
export { default as attrLowercase } from './attr-lowercase';
3+
export { default as attrSort } from './attr-sorted';
4+
export { default as attrNoDuplication } from './attr-no-duplication';
5+
export { default as attrUnsafeChars } from './attr-unsafe-chars';
6+
export { default as attrValueDoubleQuotes } from './attr-value-double-quotes';
7+
export { default as attrValueNotEmpty } from './attr-value-not-empty';
8+
export { default as attrValueSingleQuotes } from './attr-value-single-quotes';
9+
export { default as attrWhitespace } from './attr-whitespace';
10+
export { default as doctypeFirst } from './doctype-first';
11+
export { default as doctypeHTML5 } from './doctype-html5';
12+
export { default as headScriptDisabled } from './head-script-disabled';
13+
export { default as hrefAbsOrRel } from './href-abs-or-rel';
14+
export { default as idClsasAdDisabled } from './id-class-ad-disabled';
15+
export { default as idClassValue } from './id-class-value';
16+
export { default as idUnique } from './id-unique';
17+
export { default as inlineScriptDisabled } from './inline-script-disabled';
18+
export { default as inlineStyleDisabled } from './inline-style-disabled';
19+
export { default as scriptDisabled } from './script-disabled';
20+
export { default as spaceTabMixedDisabled } from './space-tab-mixed-disabled';
21+
export { default as specCharEscape } from './spec-char-escape';
22+
export { default as srcNotEmpty } from './src-not-empty';
23+
export { default as styleDisabled } from './style-disabled';
24+
export { default as tagPair } from './tag-pair';
25+
export { default as tagSelfClose } from './tag-self-close';
26+
export { default as tagnameLowercase } from './tagname-lowercase';
27+
export { default as tagnameSpecialChars } from './tagname-specialchars';
28+
export { default as titleRequire } from './title-require';
29+
export { default as tagsCheck } from './tags-check';

src/rules/tags-check.js

+99-183
Original file line numberDiff line numberDiff line change
@@ -1,202 +1,118 @@
1+
12
var tagsTypings = {
2-
a: {
3-
selfclosing: false,
4-
attrsRequired: ['href', 'title'],
5-
redundantAttrs: ['alt'],
6-
},
7-
div: {
8-
selfclosing: false,
9-
},
10-
main: {
11-
selfclosing: false,
12-
redundantAttrs: ['role'],
13-
},
14-
nav: {
15-
selfclosing: false,
16-
redundantAttrs: ['role'],
17-
},
18-
script: {
19-
attrsOptional: [
20-
['async', 'async'],
21-
['defer', 'defer'],
22-
],
23-
},
24-
img: {
25-
selfclosing: true,
26-
attrsRequired: ['src', 'alt', 'title'],
27-
},
28-
}
3+
a: {
4+
selfclosing: false,
5+
attrsRequired: ['href', 'title'],
6+
redundantAttrs: ['alt']
7+
},
8+
div: {
9+
selfclosing: false
10+
},
11+
main: {
12+
selfclosing: false,
13+
redundantAttrs: ['role']
14+
},
15+
nav: {
16+
selfclosing: false,
17+
redundantAttrs: ['role']
18+
},
19+
script: {
20+
attrsOptional: [['async', 'async'], ['defer', 'defer']]
21+
},
22+
img: {
23+
selfclosing: true,
24+
attrsRequired: [
25+
'src', 'alt', 'title'
26+
]
27+
}
28+
};
2929

30-
var assign = function (target) {
31-
var _source
30+
var assign = function(target) {
31+
var _source;
3232

33-
for (var i = 1; i < arguments.length; i++) {
34-
_source = arguments[i]
35-
for (var prop in _source) {
36-
target[prop] = _source[prop]
33+
for (var i = 1; i < arguments.length; i++) {
34+
_source = arguments[i];
35+
for (var prop in _source) {
36+
target[prop] = _source[prop];
37+
}
3738
}
38-
}
39-
40-
return target
39+
return target;
4140
}
4241

4342
export default {
44-
id: 'tags-check',
45-
description: 'Checks html tags.',
46-
init: function (parser, reporter, options) {
47-
var self = this
43+
id: 'tags-check',
44+
description: 'Checks html tags.',
45+
init: function (parser, reporter, options) {
46+
var self = this;
4847

49-
if (typeof options !== 'boolean') {
50-
assign(tagsTypings, options)
51-
}
48+
if (typeof options !== 'boolean') {
49+
assign(tagsTypings, options);
50+
}
5251

53-
parser.addListener('tagstart', function (event) {
54-
var attrs = event.attrs
55-
var col = event.col + event.tagName.length + 1
52+
parser.addListener('tagstart', function (event) {
53+
var attrs = event.attrs;
54+
var col = event.col + event.tagName.length + 1;
5655

57-
var tagName = event.tagName.toLowerCase()
56+
var tagName = event.tagName.toLowerCase();
5857

59-
if (tagsTypings[tagName]) {
60-
var currentTagType = tagsTypings[tagName]
58+
if (tagsTypings[tagName]) {
59+
var currentTagType = tagsTypings[tagName];
6160

62-
if (currentTagType.selfclosing === true && !event.close) {
63-
reporter.warn(
64-
'The <' + tagName + '> tag must be selfclosing.',
65-
event.line,
66-
event.col,
67-
self,
68-
event.raw
69-
)
70-
} else if (currentTagType.selfclosing === false && event.close) {
71-
reporter.warn(
72-
'The <' + tagName + '> tag must not be selfclosing.',
73-
event.line,
74-
event.col,
75-
self,
76-
event.raw
77-
)
78-
}
61+
if (currentTagType.selfclosing === true && !event.close) {
62+
reporter.warn('The <' + tagName + '> tag must be selfclosing.', event.line, event.col, self, event.raw);
63+
} else if (currentTagType.selfclosing === false && event.close) {
64+
reporter.warn('The <' + tagName +'> tag must not be selfclosing.', event.line, event.col, self, event.raw);
65+
}
7966

80-
if (currentTagType.attrsRequired) {
81-
currentTagType.attrsRequired.forEach(function (id) {
82-
if (Array.isArray(id)) {
83-
var copyOfId = id.map(function (a) {
84-
return a
85-
})
86-
var realID = copyOfId.shift()
87-
var values = copyOfId
67+
if (currentTagType.attrsRequired) {
68+
currentTagType.attrsRequired.forEach(function (id) {
69+
if (Array.isArray(id)) {
70+
var copyOfId = id.map(function (a) { return a;});
71+
var realID = copyOfId.shift();
72+
var values = copyOfId;
8873

89-
if (
90-
attrs.some(function (attr) {
91-
return attr.name === realID
92-
})
93-
) {
94-
attrs.forEach(function (attr) {
95-
if (
96-
attr.name === realID &&
97-
values.indexOf(attr.value) === -1
98-
) {
99-
reporter.error(
100-
'The <' +
101-
tagName +
102-
"> tag must have attr '" +
103-
realID +
104-
"' with one value of '" +
105-
values.join("' or '") +
106-
"'.",
107-
event.line,
108-
col,
109-
self,
110-
event.raw
111-
)
112-
}
113-
})
114-
} else {
115-
reporter.error(
116-
'The <' + tagName + "> tag must have attr '" + realID + "'.",
117-
event.line,
118-
col,
119-
self,
120-
event.raw
121-
)
74+
if (attrs.some(function (attr) {return attr.name === realID;})) {
75+
attrs.forEach(function (attr) {
76+
if (attr.name === realID && values.indexOf(attr.value) === -1) {
77+
reporter.error('The <' + tagName +'> tag must have attr \'' + realID + '\' with one value of \'' + values.join('\' or \'') + '\'.', event.line, col, self, event.raw);
78+
}
79+
});
80+
} else {
81+
reporter.error('The <' + tagName + '> tag must have attr \'' + realID + '\'.', event.line, col, self, event.raw);
82+
}
83+
} else if (!attrs.some(function (attr) {return id.split('|').indexOf(attr.name) !== -1;})) {
84+
reporter.error('The <' + tagName + '> tag must have attr \'' + id + '\'.', event.line, col, self, event.raw);
85+
}
86+
});
12287
}
123-
} else if (
124-
!attrs.some(function (attr) {
125-
return id.split('|').indexOf(attr.name) !== -1
126-
})
127-
) {
128-
reporter.error(
129-
'The <' + tagName + "> tag must have attr '" + id + "'.",
130-
event.line,
131-
col,
132-
self,
133-
event.raw
134-
)
135-
}
136-
})
137-
}
88+
if (currentTagType.attrsOptional) {
89+
currentTagType.attrsOptional.forEach(function (id) {
90+
if (Array.isArray(id)) {
91+
var copyOfId = id.map(function (a) { return a;});
92+
var realID = copyOfId.shift();
93+
var values = copyOfId;
13894

139-
if (currentTagType.attrsOptional) {
140-
currentTagType.attrsOptional.forEach(function (id) {
141-
if (Array.isArray(id)) {
142-
var copyOfId = id.map(function (a) {
143-
return a
144-
})
145-
var realID = copyOfId.shift()
146-
var values = copyOfId
95+
if (attrs.some(function (attr) {return attr.name === realID;})) {
96+
attrs.forEach(function (attr) {
97+
if (attr.name === realID && values.indexOf(attr.value) === -1) {
98+
reporter.error('The <' + tagName + '> tag must have optional attr \'' + realID +
99+
'\' with one value of \'' + values.join('\' or \'') + '\'.', event.line, col, self, event.raw);
100+
}
101+
});
102+
}
103+
}
104+
});
105+
}
147106

148-
if (
149-
attrs.some(function (attr) {
150-
return attr.name === realID
151-
})
152-
) {
153-
attrs.forEach(function (attr) {
154-
if (
155-
attr.name === realID &&
156-
values.indexOf(attr.value) === -1
157-
) {
158-
reporter.error(
159-
'The <' +
160-
tagName +
161-
"> tag must have optional attr '" +
162-
realID +
163-
"' with one value of '" +
164-
values.join("' or '") +
165-
"'.",
166-
event.line,
167-
col,
168-
self,
169-
event.raw
170-
)
171-
}
172-
})
107+
if (currentTagType.redundantAttrs) {
108+
currentTagType.redundantAttrs.forEach(function (attrName) {
109+
if (attrs.some(function (attr) { return attr.name === attrName;})) {
110+
reporter.error('The attr \'' + attrName + '\' is redundant for <' + tagName + '> and should be ommited.', event.line, col, self, event.raw);
111+
}
112+
});
173113
}
174-
}
175-
})
176-
}
177114

178-
if (currentTagType.redundantAttrs) {
179-
currentTagType.redundantAttrs.forEach(function (attrName) {
180-
if (
181-
attrs.some(function (attr) {
182-
return attr.name === attrName
183-
})
184-
) {
185-
reporter.error(
186-
"The attr '" +
187-
attrName +
188-
"' is redundant for <" +
189-
tagName +
190-
'> and should be ommited.',
191-
event.line,
192-
col,
193-
self,
194-
event.raw
195-
)
196-
}
197-
})
198-
}
199-
}
200-
})
201-
},
202-
}
115+
}
116+
});
117+
}
118+
};

0 commit comments

Comments
 (0)