-
-
Notifications
You must be signed in to change notification settings - Fork 378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add tags checking rule - allows specify rules for any tag and validate that #384
Changes from all commits
89d3229
5861fdd
0807e90
44be220
732bbe1
9eb38df
7e06e27
2bec1ff
610d5b0
1208c55
efff596
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
|
||
var tagsTypings = { | ||
a: { | ||
selfclosing: false, | ||
attrsRequired: ['href', 'title'], | ||
redundantAttrs: ['alt'] | ||
}, | ||
div: { | ||
selfclosing: false | ||
}, | ||
main: { | ||
selfclosing: false, | ||
redundantAttrs: ['role'] | ||
}, | ||
nav: { | ||
selfclosing: false, | ||
redundantAttrs: ['role'] | ||
}, | ||
script: { | ||
attrsOptional: [['async', 'async'], ['defer', 'defer']] | ||
}, | ||
img: { | ||
selfclosing: true, | ||
attrsRequired: [ | ||
'src', 'alt', 'title' | ||
] | ||
} | ||
}; | ||
|
||
var assign = function(target) { | ||
var _source; | ||
|
||
for (var i = 1; i < arguments.length; i++) { | ||
_source = arguments[i]; | ||
for (var prop in _source) { | ||
target[prop] = _source[prop]; | ||
} | ||
} | ||
return target; | ||
} | ||
|
||
export default { | ||
id: 'tags-check', | ||
description: 'Checks html tags.', | ||
init: function (parser, reporter, options) { | ||
var self = this; | ||
|
||
if (typeof options !== 'boolean') { | ||
assign(tagsTypings, options); | ||
} | ||
|
||
parser.addListener('tagstart', function (event) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should convert all of this functions to arrow-functions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's the plan right after we merge all old PRs :) |
||
var attrs = event.attrs; | ||
var col = event.col + event.tagName.length + 1; | ||
|
||
var tagName = event.tagName.toLowerCase(); | ||
|
||
if (tagsTypings[tagName]) { | ||
var currentTagType = tagsTypings[tagName]; | ||
|
||
if (currentTagType.selfclosing === true && !event.close) { | ||
reporter.warn('The <' + tagName + '> tag must be selfclosing.', event.line, event.col, self, event.raw); | ||
} else if (currentTagType.selfclosing === false && event.close) { | ||
reporter.warn('The <' + tagName +'> tag must not be selfclosing.', event.line, event.col, self, event.raw); | ||
} | ||
|
||
if (currentTagType.attrsRequired) { | ||
currentTagType.attrsRequired.forEach(function (id) { | ||
if (Array.isArray(id)) { | ||
var copyOfId = id.map(function (a) { return a;}); | ||
var realID = copyOfId.shift(); | ||
var values = copyOfId; | ||
|
||
if (attrs.some(function (attr) {return attr.name === realID;})) { | ||
attrs.forEach(function (attr) { | ||
if (attr.name === realID && values.indexOf(attr.value) === -1) { | ||
reporter.error('The <' + tagName +'> tag must have attr \'' + realID + '\' with one value of \'' + values.join('\' or \'') + '\'.', event.line, col, self, event.raw); | ||
} | ||
}); | ||
} else { | ||
reporter.error('The <' + tagName + '> tag must have attr \'' + realID + '\'.', event.line, col, self, event.raw); | ||
} | ||
} else if (!attrs.some(function (attr) {return id.split('|').indexOf(attr.name) !== -1;})) { | ||
reporter.error('The <' + tagName + '> tag must have attr \'' + id + '\'.', event.line, col, self, event.raw); | ||
} | ||
}); | ||
} | ||
if (currentTagType.attrsOptional) { | ||
currentTagType.attrsOptional.forEach(function (id) { | ||
if (Array.isArray(id)) { | ||
var copyOfId = id.map(function (a) { return a;}); | ||
var realID = copyOfId.shift(); | ||
var values = copyOfId; | ||
|
||
if (attrs.some(function (attr) {return attr.name === realID;})) { | ||
attrs.forEach(function (attr) { | ||
if (attr.name === realID && values.indexOf(attr.value) === -1) { | ||
reporter.error('The <' + tagName + '> tag must have optional attr \'' + realID + | ||
'\' with one value of \'' + values.join('\' or \'') + '\'.', event.line, col, self, event.raw); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
if (currentTagType.redundantAttrs) { | ||
currentTagType.redundantAttrs.forEach(function (attrName) { | ||
if (attrs.some(function (attr) { return attr.name === attrName;})) { | ||
reporter.error('The attr \'' + attrName + '\' is redundant for <' + tagName + '> and should be ommited.', event.line, col, self, event.raw); | ||
} | ||
}); | ||
} | ||
|
||
} | ||
}); | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
|
||
const expect = require("expect.js"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in this file singlequotes seems to be used |
||
|
||
const HTMLHint = require('../../dist/htmlhint.js').HTMLHint; | ||
|
||
const ruldId = 'tags-check', | ||
ruleOptions = {}; | ||
|
||
ruleOptions[ruldId] = { | ||
sometag: { | ||
selfclosing: true, | ||
attrsRequired: [['attrname', 'attrvalue']] | ||
} | ||
}; | ||
|
||
describe('Rules: ' + ruldId, function(){ | ||
it('Tag <a> should have requered attrs [title, href]', function(){ | ||
var code = '<a>blabla</a>'; | ||
var messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(2); | ||
expect(messages[0].rule.id).to.be(ruldId); | ||
expect(messages[1].rule.id).to.be(ruldId); | ||
}); | ||
it('Tag <a> should not be selfclosing', function(){ | ||
var code = '<a href="bbb" title="aaa"/>'; | ||
var messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(1); | ||
expect(messages[0].rule.id).to.be(ruldId); | ||
}); | ||
it('Tag <img> should be selfclosing', function(){ | ||
var code = '<img src="bbb" title="aaa" alt="asd"></img>'; | ||
var messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(1); | ||
expect(messages[0].rule.id).to.be(ruldId); | ||
}); | ||
it('Should check optional attributes', function(){ | ||
var code = '<script src="aaa" async="sad" />'; | ||
var messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(1); | ||
expect(messages[0].rule.id).to.be(ruldId); | ||
}); | ||
it('Should check redunant attributes', function(){ | ||
var code = '<main role="main" />'; | ||
var messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(2); | ||
expect(messages[0].rule.id).to.be(ruldId); | ||
expect(messages[1].rule.id).to.be(ruldId); | ||
}); | ||
it('Should be extendable trought config', function(){ | ||
var code = '<sometag></sometag>'; | ||
var messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(2); | ||
expect(messages[0].rule.id).to.be(ruldId); | ||
}); | ||
it('Should check required attributes with specifyed values', function(){ | ||
var code = '<sometag attrname="attrvalue" />'; | ||
var messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(0); | ||
code = '<sometag attrname="wrong_value" />'; | ||
messages = HTMLHint.verify(code, ruleOptions); | ||
expect(messages.length).to.be(1); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.