react-scripts
Version:
Configuration and scripts for Create React App.
178 lines (163 loc) • 5.96 kB
JavaScript
/*!
* HTMLLint (to be used in conjunction with HTMLMinifier)
*
* Copyright (c) 2010-2016 Juriy "kangax" Zaytsev
* Licensed under the MIT license.
*
*/
;
function isPresentationalElement(tag) {
return /^(?:big|small|hr|blink|marquee)$/.test(tag);
}
function isDeprecatedElement(tag) {
return /^(?:applet|basefont|center|dir|font|isindex|strike)$/.test(tag);
}
function isEventAttribute(attrName) {
return /^on[a-z]+/.test(attrName);
}
function isStyleAttribute(attrName) {
return attrName.toLowerCase() === 'style';
}
function isDeprecatedAttribute(tag, attrName) {
return (
attrName === 'align' &&
/^(?:caption|applet|iframe|img|imput|object|legend|table|hr|div|h[1-6]|p)$/.test(tag) ||
attrName === 'alink' && tag === 'body' ||
attrName === 'alt' && tag === 'applet' ||
attrName === 'archive' && tag === 'applet' ||
attrName === 'background' && tag === 'body' ||
attrName === 'bgcolor' && /^(?:table|t[rdh]|body)$/.test(tag) ||
attrName === 'border' && /^(?:img|object)$/.test(tag) ||
attrName === 'clear' && tag === 'br' ||
attrName === 'code' && tag === 'applet' ||
attrName === 'codebase' && tag === 'applet' ||
attrName === 'color' && /^(?:base(?:font)?)$/.test(tag) ||
attrName === 'compact' && /^(?:dir|[dou]l|menu)$/.test(tag) ||
attrName === 'face' && /^base(?:font)?$/.test(tag) ||
attrName === 'height' && /^(?:t[dh]|applet)$/.test(tag) ||
attrName === 'hspace' && /^(?:applet|img|object)$/.test(tag) ||
attrName === 'language' && tag === 'script' ||
attrName === 'link' && tag === 'body' ||
attrName === 'name' && tag === 'applet' ||
attrName === 'noshade' && tag === 'hr' ||
attrName === 'nowrap' && /^t[dh]$/.test(tag) ||
attrName === 'object' && tag === 'applet' ||
attrName === 'prompt' && tag === 'isindex' ||
attrName === 'size' && /^(?:hr|font|basefont)$/.test(tag) ||
attrName === 'start' && tag === 'ol' ||
attrName === 'text' && tag === 'body' ||
attrName === 'type' && /^(?:li|ol|ul)$/.test(tag) ||
attrName === 'value' && tag === 'li' ||
attrName === 'version' && tag === 'html' ||
attrName === 'vlink' && tag === 'body' ||
attrName === 'vspace' && /^(?:applet|img|object)$/.test(tag) ||
attrName === 'width' && /^(?:hr|td|th|applet|pre)$/.test(tag)
);
}
function isInaccessibleAttribute(attrName, attrValue) {
return attrName === 'href' && /^\s*javascript\s*:\s*void\s*(\s+0|\(\s*0\s*\))\s*$/i.test(attrValue);
}
function Lint() {
this.log = [];
this._lastElement = null;
this._isElementRepeated = false;
}
Lint.prototype.testElement = function(tag) {
this._attributes = Object.create(null);
if (isDeprecatedElement(tag)) {
this.log.push(
'Found <span class="deprecated-element">deprecated</span> <strong><code><' +
tag + '></code></strong> element'
);
}
else if (isPresentationalElement(tag)) {
this.log.push(
'Found <span class="presentational-element">presentational</span> <strong><code><' +
tag + '></code></strong> element'
);
}
else {
this.checkRepeatingElement(tag);
}
};
Lint.prototype.checkRepeatingElement = function(tag) {
if (tag === 'br' && this._lastElement === 'br') {
this._isElementRepeated = true;
}
else if (this._isElementRepeated) {
this._reportRepeatingElement();
this._isElementRepeated = false;
}
this._lastElement = tag;
};
Lint.prototype._reportRepeatingElement = function() {
this.log.push('Found <code><br></code> sequence. Try replacing it with styling.');
};
Lint.prototype.testAttribute = function(tag, attrName, attrValue) {
if (this._attributes[attrName]) {
this.log.push(
'Found <span class="repeating-attribute">repeating attribute</span> (<strong>' +
attrName + '</strong>) on <strong><code><' + tag + '></code></strong> element.'
);
}
else {
this._attributes[attrName] = true;
}
if (isEventAttribute(attrName)) {
this.log.push(
'Found <span class="event-attribute">event attribute</span> (<strong>' +
attrName + '</strong>) on <strong><code><' + tag + '></code></strong> element.'
);
}
else if (isDeprecatedAttribute(tag, attrName)) {
this.log.push(
'Found <span class="deprecated-attribute">deprecated</span> <strong>' +
attrName + '</strong> attribute on <strong><code><' + tag + '></code></strong> element.'
);
}
else if (isStyleAttribute(attrName)) {
this.log.push(
'Found <span class="style-attribute">style attribute</span> on <strong><code><' +
tag + '></code></strong> element.'
);
}
else if (isInaccessibleAttribute(attrName, attrValue)) {
this.log.push(
'Found <span class="inaccessible-attribute">inaccessible attribute</span> ' +
'(on <strong><code><' + tag + '></code></strong> element).'
);
}
};
Lint.prototype.testChars = function(chars) {
this._lastElement = '';
if (/( \s*){2,}/.test(chars)) {
this.log.push('Found repeating <strong><code>&nbsp;</code></strong> sequence. Try replacing it with styling.');
}
};
Lint.prototype.test = function(tag, attrName, attrValue) {
this.testElement(tag);
this.testAttribute(tag, attrName, attrValue);
};
Lint.prototype.testDoctype = function(doctype) {
this._doctype = doctype;
};
Lint.prototype.populate = function(writeToElement) {
if (!this._doctype) {
this.log.push('No DOCTYPE found.');
}
if (this._isElementRepeated) {
this._reportRepeatingElement();
}
var output;
if (writeToElement) {
output = this.log.map(function(line) {
return '<li>' + line + '</li>';
}).join('');
writeToElement.innerHTML = '<ol>' + output + '</ol>';
}
else if (this.log.length) {
output = this.log.join('\n - ').replace(/<[^>]+>/g, '');
console.log(' - ' + output.replace(/</g, '<').replace(/>/g, '>'));
}
};
exports.HTMLLint = Lint;