Skip to content

Commit

Permalink
feat: Add tags checking rule - allows specify rules for any tag and v…
Browse files Browse the repository at this point in the history
…alidate 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 <a.obitskyi@astoundcommerce.com>
  • Loading branch information
2 people authored and Christopher Quadflieg committed May 19, 2020
1 parent fe4f3c6 commit 518a1a2
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 269 deletions.
60 changes: 29 additions & 31 deletions src/rules/index.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
export { default as altRequire } from './alt-require'
export { default as attrLowercase } from './attr-lowercase'
export { default as attrSort } from './attr-sorted'
export { default as attrNoDuplication } from './attr-no-duplication'
export { default as attrUnsafeChars } from './attr-unsafe-chars'
export { default as attrValueDoubleQuotes } from './attr-value-double-quotes'
export { default as attrValueNotEmpty } from './attr-value-not-empty'
export { default as attrValueSingleQuotes } from './attr-value-single-quotes'
export { default as attrWhitespace } from './attr-whitespace'
export { default as doctypeFirst } from './doctype-first'
export { default as doctypeHTML5 } from './doctype-html5'
export { default as headScriptDisabled } from './head-script-disabled'
export { default as hrefAbsOrRel } from './href-abs-or-rel'
export { default as idClsasAdDisabled } from './id-class-ad-disabled'
export { default as idClassValue } from './id-class-value'
export { default as idUnique } from './id-unique'
export { default as inlineScriptDisabled } from './inline-script-disabled'
export { default as inlineStyleDisabled } from './inline-style-disabled'
export { default as inputRequiresLabel } from './input-requires-label'
export { default as scriptDisabled } from './script-disabled'
export { default as spaceTabMixedDisabled } from './space-tab-mixed-disabled'
export { default as specCharEscape } from './spec-char-escape'
export { default as srcNotEmpty } from './src-not-empty'
export { default as styleDisabled } from './style-disabled'
export { default as tagPair } from './tag-pair'
export { default as tagSelfClose } from './tag-self-close'
export { default as tagnameLowercase } from './tagname-lowercase'
export { default as tagnameSpecialChars } from './tagname-specialchars'
export { default as titleRequire } from './title-require'
export { default as tagsCheck } from './tags-check'
export { default as attrNoUnnecessaryWhitespace } from './attr-no-unnecessary-whitespace'
export { default as altRequire } from './alt-require';
export { default as attrLowercase } from './attr-lowercase';
export { default as attrSort } from './attr-sorted';
export { default as attrNoDuplication } from './attr-no-duplication';
export { default as attrUnsafeChars } from './attr-unsafe-chars';
export { default as attrValueDoubleQuotes } from './attr-value-double-quotes';
export { default as attrValueNotEmpty } from './attr-value-not-empty';
export { default as attrValueSingleQuotes } from './attr-value-single-quotes';
export { default as attrWhitespace } from './attr-whitespace';
export { default as doctypeFirst } from './doctype-first';
export { default as doctypeHTML5 } from './doctype-html5';
export { default as headScriptDisabled } from './head-script-disabled';
export { default as hrefAbsOrRel } from './href-abs-or-rel';
export { default as idClsasAdDisabled } from './id-class-ad-disabled';
export { default as idClassValue } from './id-class-value';
export { default as idUnique } from './id-unique';
export { default as inlineScriptDisabled } from './inline-script-disabled';
export { default as inlineStyleDisabled } from './inline-style-disabled';
export { default as scriptDisabled } from './script-disabled';
export { default as spaceTabMixedDisabled } from './space-tab-mixed-disabled';
export { default as specCharEscape } from './spec-char-escape';
export { default as srcNotEmpty } from './src-not-empty';
export { default as styleDisabled } from './style-disabled';
export { default as tagPair } from './tag-pair';
export { default as tagSelfClose } from './tag-self-close';
export { default as tagnameLowercase } from './tagname-lowercase';
export { default as tagnameSpecialChars } from './tagname-specialchars';
export { default as titleRequire } from './title-require';
export { default as tagsCheck } from './tags-check';
282 changes: 99 additions & 183 deletions src/rules/tags-check.js
Original file line number Diff line number Diff line change
@@ -1,202 +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'],
},
}
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
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]
for (var i = 1; i < arguments.length; i++) {
_source = arguments[i];
for (var prop in _source) {
target[prop] = _source[prop];
}
}
}

return target
return target;
}

export default {
id: 'tags-check',
description: 'Checks html tags.',
init: function (parser, reporter, options) {
var self = this
id: 'tags-check',
description: 'Checks html tags.',
init: function (parser, reporter, options) {
var self = this;

if (typeof options !== 'boolean') {
assign(tagsTypings, options)
}
if (typeof options !== 'boolean') {
assign(tagsTypings, options);
}

parser.addListener('tagstart', function (event) {
var attrs = event.attrs
var col = event.col + event.tagName.length + 1
parser.addListener('tagstart', function (event) {
var attrs = event.attrs;
var col = event.col + event.tagName.length + 1;

var tagName = event.tagName.toLowerCase()
var tagName = event.tagName.toLowerCase();

if (tagsTypings[tagName]) {
var currentTagType = tagsTypings[tagName]
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.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 (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
)
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);
}
});
}
} 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 (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 (
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);
}
});
}
}
})
}

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
)
}
})
}
}
})
},
}
}
});
}
};
Loading

0 comments on commit 518a1a2

Please sign in to comment.