diff --git a/dist/core/htmlparser.js b/dist/core/htmlparser.js index 180a1de92..1304989f8 100644 --- a/dist/core/htmlparser.js +++ b/dist/core/htmlparser.js @@ -17,7 +17,7 @@ class HTMLParser { } parse(html) { const mapCdataTags = this._mapCdataTags; - const regTag = /<(?:\/([^\s>]+)\s*|!--([\s\S]*?)--|!([^>]*?)|([\w\-:]+)((?:\s+[^\s"'>\/=\x00-\x0F\x7F\x80-\x9F]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]*))?)*?)\s*(\/?))>/g; + const regTag = /<(?:\/([^\s>]+)\s*|!--([\s\S]*?)--|!([^>]*?)|([\w\-:]+)((?:\s*[^\s"'>\/=\x00-\x0F\x7F\x80-\x9F]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]*))?)*?)\s*(\/?))>/g; const regAttr = /\s*([^\s"'>\/=\x00-\x0F\x7F\x80-\x9F]+)(?:\s*=\s*(?:(")([^"]*)"|(')([^']*)'|([^\s"'>]*)))?/g; const regLine = /\r?\n/g; let match; diff --git a/dist/core/rules/index.js b/dist/core/rules/index.js index f9bc873aa..b946393d9 100644 --- a/dist/core/rules/index.js +++ b/dist/core/rules/index.js @@ -1,14 +1,18 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.attrNoUnnecessaryWhitespace = exports.tagsCheck = exports.titleRequire = exports.tagnameSpecialChars = exports.tagnameLowercase = exports.emptyTagNotSelfClosed = exports.tagSelfClose = exports.tagPair = exports.styleDisabled = exports.srcNotEmpty = exports.specCharEscape = exports.spaceTabMixedDisabled = exports.scriptDisabled = exports.inputRequiresLabel = exports.inlineStyleDisabled = exports.inlineScriptDisabled = exports.idUnique = exports.idClassValue = exports.idClsasAdDisabled = exports.htmlLangRequire = exports.hrefAbsOrRel = exports.headScriptDisabled = exports.doctypeHTML5 = exports.doctypeFirst = exports.attrWhitespace = exports.attrValueSingleQuotes = exports.attrValueNotEmpty = exports.attrValueDoubleQuotes = exports.attrUnsafeChars = exports.attrNoDuplication = exports.attrSort = exports.attrLowercase = exports.altRequire = void 0; +exports.titleRequire = exports.tagsCheck = exports.tagnameSpecialChars = exports.tagnameLowercase = exports.tagSelfClose = exports.tagPair = exports.styleDisabled = exports.srcNotEmpty = exports.specCharEscape = exports.spaceTabMixedDisabled = exports.scriptDisabled = exports.inputRequiresLabel = exports.inlineStyleDisabled = exports.inlineScriptDisabled = exports.idUnique = exports.idClassValue = exports.idClsasAdDisabled = exports.htmlLangRequire = exports.hrefAbsOrRel = exports.headScriptDisabled = exports.emptyTagNotSelfClosed = exports.doctypeHTML5 = exports.doctypeFirst = exports.attrWhitespace = exports.attrValueSingleQuotes = exports.attrValueNotEmpty = exports.attrValueDoubleQuotes = exports.attrUnsafeChars = exports.attrSpaceBetween = exports.attrSort = exports.attrNoUnnecessaryWhitespace = exports.attrNoDuplication = exports.attrLowercase = exports.altRequire = void 0; var alt_require_1 = require("./alt-require"); Object.defineProperty(exports, "altRequire", { enumerable: true, get: function () { return alt_require_1.default; } }); var attr_lowercase_1 = require("./attr-lowercase"); Object.defineProperty(exports, "attrLowercase", { enumerable: true, get: function () { return attr_lowercase_1.default; } }); -var attr_sorted_1 = require("./attr-sorted"); -Object.defineProperty(exports, "attrSort", { enumerable: true, get: function () { return attr_sorted_1.default; } }); var attr_no_duplication_1 = require("./attr-no-duplication"); Object.defineProperty(exports, "attrNoDuplication", { enumerable: true, get: function () { return attr_no_duplication_1.default; } }); +var attr_no_unnecessary_whitespace_1 = require("./attr-no-unnecessary-whitespace"); +Object.defineProperty(exports, "attrNoUnnecessaryWhitespace", { enumerable: true, get: function () { return attr_no_unnecessary_whitespace_1.default; } }); +var attr_sorted_1 = require("./attr-sorted"); +Object.defineProperty(exports, "attrSort", { enumerable: true, get: function () { return attr_sorted_1.default; } }); +var attr_space_between_1 = require("./attr-space-between"); +Object.defineProperty(exports, "attrSpaceBetween", { enumerable: true, get: function () { return attr_space_between_1.default; } }); var attr_unsafe_chars_1 = require("./attr-unsafe-chars"); Object.defineProperty(exports, "attrUnsafeChars", { enumerable: true, get: function () { return attr_unsafe_chars_1.default; } }); var attr_value_double_quotes_1 = require("./attr-value-double-quotes"); @@ -23,6 +27,8 @@ var doctype_first_1 = require("./doctype-first"); Object.defineProperty(exports, "doctypeFirst", { enumerable: true, get: function () { return doctype_first_1.default; } }); var doctype_html5_1 = require("./doctype-html5"); Object.defineProperty(exports, "doctypeHTML5", { enumerable: true, get: function () { return doctype_html5_1.default; } }); +var empty_tag_not_self_closed_1 = require("./empty-tag-not-self-closed"); +Object.defineProperty(exports, "emptyTagNotSelfClosed", { enumerable: true, get: function () { return empty_tag_not_self_closed_1.default; } }); var head_script_disabled_1 = require("./head-script-disabled"); Object.defineProperty(exports, "headScriptDisabled", { enumerable: true, get: function () { return head_script_disabled_1.default; } }); var href_abs_or_rel_1 = require("./href-abs-or-rel"); @@ -55,16 +61,12 @@ var tag_pair_1 = require("./tag-pair"); Object.defineProperty(exports, "tagPair", { enumerable: true, get: function () { return tag_pair_1.default; } }); var tag_self_close_1 = require("./tag-self-close"); Object.defineProperty(exports, "tagSelfClose", { enumerable: true, get: function () { return tag_self_close_1.default; } }); -var empty_tag_not_self_closed_1 = require("./empty-tag-not-self-closed"); -Object.defineProperty(exports, "emptyTagNotSelfClosed", { enumerable: true, get: function () { return empty_tag_not_self_closed_1.default; } }); var tagname_lowercase_1 = require("./tagname-lowercase"); Object.defineProperty(exports, "tagnameLowercase", { enumerable: true, get: function () { return tagname_lowercase_1.default; } }); var tagname_specialchars_1 = require("./tagname-specialchars"); Object.defineProperty(exports, "tagnameSpecialChars", { enumerable: true, get: function () { return tagname_specialchars_1.default; } }); -var title_require_1 = require("./title-require"); -Object.defineProperty(exports, "titleRequire", { enumerable: true, get: function () { return title_require_1.default; } }); var tags_check_1 = require("./tags-check"); Object.defineProperty(exports, "tagsCheck", { enumerable: true, get: function () { return tags_check_1.default; } }); -var attr_no_unnecessary_whitespace_1 = require("./attr-no-unnecessary-whitespace"); -Object.defineProperty(exports, "attrNoUnnecessaryWhitespace", { enumerable: true, get: function () { return attr_no_unnecessary_whitespace_1.default; } }); -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29yZS9ydWxlcy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2Q0FBcUQ7QUFBNUMseUdBQUEsT0FBTyxPQUFjO0FBQzlCLG1EQUEyRDtBQUFsRCwrR0FBQSxPQUFPLE9BQWlCO0FBQ2pDLDZDQUFtRDtBQUExQyx1R0FBQSxPQUFPLE9BQVk7QUFDNUIsNkRBQW9FO0FBQTNELHdIQUFBLE9BQU8sT0FBcUI7QUFDckMseURBQWdFO0FBQXZELG9IQUFBLE9BQU8sT0FBbUI7QUFDbkMsdUVBQTZFO0FBQXBFLGlJQUFBLE9BQU8sT0FBeUI7QUFDekMsK0RBQXFFO0FBQTVELHlIQUFBLE9BQU8sT0FBcUI7QUFDckMsdUVBQTZFO0FBQXBFLGlJQUFBLE9BQU8sT0FBeUI7QUFDekMscURBQTZEO0FBQXBELGlIQUFBLE9BQU8sT0FBa0I7QUFDbEMsaURBQXlEO0FBQWhELDZHQUFBLE9BQU8sT0FBZ0I7QUFDaEMsaURBQXlEO0FBQWhELDZHQUFBLE9BQU8sT0FBZ0I7QUFDaEMsK0RBQXNFO0FBQTdELDBIQUFBLE9BQU8sT0FBc0I7QUFDdEMscURBQTJEO0FBQWxELCtHQUFBLE9BQU8sT0FBZ0I7QUFDaEMseURBQWdFO0FBQXZELG9IQUFBLE9BQU8sT0FBbUI7QUFDbkMsK0RBQXFFO0FBQTVELHlIQUFBLE9BQU8sT0FBcUI7QUFDckMsbURBQTBEO0FBQWpELDhHQUFBLE9BQU8sT0FBZ0I7QUFDaEMseUNBQWlEO0FBQXhDLHFHQUFBLE9BQU8sT0FBWTtBQUM1QixtRUFBMEU7QUFBakUsOEhBQUEsT0FBTyxPQUF3QjtBQUN4QyxpRUFBd0U7QUFBL0QsNEhBQUEsT0FBTyxPQUF1QjtBQUN2QywrREFBc0U7QUFBN0QsMEhBQUEsT0FBTyxPQUFzQjtBQUN0QyxxREFBNkQ7QUFBcEQsaUhBQUEsT0FBTyxPQUFrQjtBQUNsQyx1RUFBNkU7QUFBcEUsaUlBQUEsT0FBTyxPQUF5QjtBQUN6Qyx1REFBOEQ7QUFBckQsa0hBQUEsT0FBTyxPQUFrQjtBQUNsQyxpREFBd0Q7QUFBL0MsNEdBQUEsT0FBTyxPQUFlO0FBQy9CLG1EQUEyRDtBQUFsRCwrR0FBQSxPQUFPLE9BQWlCO0FBQ2pDLHVDQUErQztBQUF0QyxtR0FBQSxPQUFPLE9BQVc7QUFDM0IsbURBQTBEO0FBQWpELDhHQUFBLE9BQU8sT0FBZ0I7QUFDaEMseUVBQThFO0FBQXJFLGtJQUFBLE9BQU8sT0FBeUI7QUFDekMseURBQWlFO0FBQXhELHFIQUFBLE9BQU8sT0FBb0I7QUFDcEMsK0RBQXVFO0FBQTlELDJIQUFBLE9BQU8sT0FBdUI7QUFDdkMsaURBQXlEO0FBQWhELDZHQUFBLE9BQU8sT0FBZ0I7QUFDaEMsMkNBQW1EO0FBQTFDLHVHQUFBLE9BQU8sT0FBYTtBQUM3QixtRkFBeUY7QUFBaEYsNklBQUEsT0FBTyxPQUErQiJ9 \ No newline at end of file +var title_require_1 = require("./title-require"); +Object.defineProperty(exports, "titleRequire", { enumerable: true, get: function () { return title_require_1.default; } }); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29yZS9ydWxlcy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSw2Q0FBcUQ7QUFBNUMseUdBQUEsT0FBTyxPQUFjO0FBQzlCLG1EQUEyRDtBQUFsRCwrR0FBQSxPQUFPLE9BQWlCO0FBQ2pDLDZEQUFvRTtBQUEzRCx3SEFBQSxPQUFPLE9BQXFCO0FBQ3JDLG1GQUF5RjtBQUFoRiw2SUFBQSxPQUFPLE9BQStCO0FBQy9DLDZDQUFtRDtBQUExQyx1R0FBQSxPQUFPLE9BQVk7QUFDNUIsMkRBQWtFO0FBQXpELHNIQUFBLE9BQU8sT0FBb0I7QUFDcEMseURBQWdFO0FBQXZELG9IQUFBLE9BQU8sT0FBbUI7QUFDbkMsdUVBQTZFO0FBQXBFLGlJQUFBLE9BQU8sT0FBeUI7QUFDekMsK0RBQXFFO0FBQTVELHlIQUFBLE9BQU8sT0FBcUI7QUFDckMsdUVBQTZFO0FBQXBFLGlJQUFBLE9BQU8sT0FBeUI7QUFDekMscURBQTZEO0FBQXBELGlIQUFBLE9BQU8sT0FBa0I7QUFDbEMsaURBQXlEO0FBQWhELDZHQUFBLE9BQU8sT0FBZ0I7QUFDaEMsaURBQXlEO0FBQWhELDZHQUFBLE9BQU8sT0FBZ0I7QUFDaEMseUVBQThFO0FBQXJFLGtJQUFBLE9BQU8sT0FBeUI7QUFDekMsK0RBQXNFO0FBQTdELDBIQUFBLE9BQU8sT0FBc0I7QUFDdEMscURBQTJEO0FBQWxELCtHQUFBLE9BQU8sT0FBZ0I7QUFDaEMseURBQWdFO0FBQXZELG9IQUFBLE9BQU8sT0FBbUI7QUFDbkMsK0RBQXFFO0FBQTVELHlIQUFBLE9BQU8sT0FBcUI7QUFDckMsbURBQTBEO0FBQWpELDhHQUFBLE9BQU8sT0FBZ0I7QUFDaEMseUNBQWlEO0FBQXhDLHFHQUFBLE9BQU8sT0FBWTtBQUM1QixtRUFBMEU7QUFBakUsOEhBQUEsT0FBTyxPQUF3QjtBQUN4QyxpRUFBd0U7QUFBL0QsNEhBQUEsT0FBTyxPQUF1QjtBQUN2QywrREFBc0U7QUFBN0QsMEhBQUEsT0FBTyxPQUFzQjtBQUN0QyxxREFBNkQ7QUFBcEQsaUhBQUEsT0FBTyxPQUFrQjtBQUNsQyx1RUFBNkU7QUFBcEUsaUlBQUEsT0FBTyxPQUF5QjtBQUN6Qyx1REFBOEQ7QUFBckQsa0hBQUEsT0FBTyxPQUFrQjtBQUNsQyxpREFBd0Q7QUFBL0MsNEdBQUEsT0FBTyxPQUFlO0FBQy9CLG1EQUEyRDtBQUFsRCwrR0FBQSxPQUFPLE9BQWlCO0FBQ2pDLHVDQUErQztBQUF0QyxtR0FBQSxPQUFPLE9BQVc7QUFDM0IsbURBQTBEO0FBQWpELDhHQUFBLE9BQU8sT0FBZ0I7QUFDaEMseURBQWlFO0FBQXhELHFIQUFBLE9BQU8sT0FBb0I7QUFDcEMsK0RBQXVFO0FBQTlELDJIQUFBLE9BQU8sT0FBdUI7QUFDdkMsMkNBQW1EO0FBQTFDLHVHQUFBLE9BQU8sT0FBYTtBQUM3QixpREFBeUQ7QUFBaEQsNkdBQUEsT0FBTyxPQUFnQiJ9 \ No newline at end of file diff --git a/dist/htmlhint.js b/dist/htmlhint.js index 55753504b..74908405c 100644 --- a/dist/htmlhint.js +++ b/dist/htmlhint.js @@ -30,7 +30,7 @@ } parse(html) { const mapCdataTags = this._mapCdataTags; - const regTag = /<(?:\/([^\s>]+)\s*|!--([\s\S]*?)--|!([^>]*?)|([\w\-:]+)((?:\s+[^\s"'>\/=\x00-\x0F\x7F\x80-\x9F]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]*))?)*?)\s*(\/?))>/g; + const regTag = /<(?:\/([^\s>]+)\s*|!--([\s\S]*?)--|!([^>]*?)|([\w\-:]+)((?:\s*[^\s"'>\/=\x00-\x0F\x7F\x80-\x9F]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'>]*))?)*?)\s*(\/?))>/g; const regAttr = /\s*([^\s"'>\/=\x00-\x0F\x7F\x80-\x9F]+)(?:\s*=\s*(?:(")([^"]*)"|(')([^']*)'|([^\s"'>]*)))?/g; const regLine = /\r?\n/g; let match; @@ -451,6 +451,54 @@ }, }; + var attrNoDuplication = {}; + + Object.defineProperty(attrNoDuplication, "__esModule", { value: true }); + attrNoDuplication.default = { + id: 'attr-no-duplication', + description: 'Elements cannot have duplicate attributes.', + init(parser, reporter) { + parser.addListener('tagstart', (event) => { + const attrs = event.attrs; + let attr; + let attrName; + const col = event.col + event.tagName.length + 1; + const mapAttrName = {}; + for (let i = 0, l = attrs.length; i < l; i++) { + attr = attrs[i]; + attrName = attr.name; + if (mapAttrName[attrName] === true) { + reporter.error(`Duplicate of attribute name [ ${attr.name} ] was found.`, event.line, col + attr.index, this, attr.raw); + } + mapAttrName[attrName] = true; + } + }); + }, + }; + + var attrNoUnnecessaryWhitespace = {}; + + Object.defineProperty(attrNoUnnecessaryWhitespace, "__esModule", { value: true }); + attrNoUnnecessaryWhitespace.default = { + id: 'attr-no-unnecessary-whitespace', + description: 'No spaces between attribute names and values.', + init(parser, reporter, options) { + const exceptions = Array.isArray(options) ? options : []; + parser.addListener('tagstart', (event) => { + const attrs = event.attrs; + const col = event.col + event.tagName.length + 1; + for (let i = 0; i < attrs.length; i++) { + if (exceptions.indexOf(attrs[i].name) === -1) { + const match = /(\s*)=(\s*)/.exec(attrs[i].raw.trim()); + if (match && (match[1].length !== 0 || match[2].length !== 0)) { + reporter.error(`The attribute '${attrs[i].name}' must not have spaces between the name and value.`, event.line, col + attrs[i].index, this, attrs[i].raw); + } + } + } + }); + }, + }; + var attrSorted = {}; Object.defineProperty(attrSorted, "__esModule", { value: true }); @@ -501,26 +549,19 @@ }, }; - var attrNoDuplication = {}; + var attrSpaceBetween = {}; - Object.defineProperty(attrNoDuplication, "__esModule", { value: true }); - attrNoDuplication.default = { - id: 'attr-no-duplication', - description: 'Elements cannot have duplicate attributes.', + Object.defineProperty(attrSpaceBetween, "__esModule", { value: true }); + attrSpaceBetween.default = { + id: 'attr-space-between', + description: 'Attribute must have spaces between.', init(parser, reporter) { parser.addListener('tagstart', (event) => { - const attrs = event.attrs; - let attr; - let attrName; - const col = event.col + event.tagName.length + 1; - const mapAttrName = {}; - for (let i = 0, l = attrs.length; i < l; i++) { - attr = attrs[i]; - attrName = attr.name; - if (mapAttrName[attrName] === true) { - reporter.error(`Duplicate of attribute name [ ${attr.name} ] was found.`, event.line, col + attr.index, this, attr.raw); + for (const { index, name, raw } of event.attrs) { + const col = event.col + event.tagName.length + 1; + if (!raw.match(/^\s/)) { + reporter.error(`Attribute "${name}" must be separated with a space`, event.line, col + index, this, event.raw); } - mapAttrName[attrName] = true; } }); }, @@ -693,6 +734,25 @@ }, }; + var emptyTagNotSelfClosed = {}; + + Object.defineProperty(emptyTagNotSelfClosed, "__esModule", { value: true }); + emptyTagNotSelfClosed.default = { + id: 'empty-tag-not-self-closed', + description: 'Empty tags must not use self closed syntax.', + init(parser, reporter) { + const mapEmptyTags = parser.makeMap('area,base,basefont,bgsound,br,col,frame,hr,img,input,isindex,link,meta,param,embed,track,command,source,keygen,wbr'); + parser.addListener('tagstart', (event) => { + const tagName = event.tagName.toLowerCase(); + if (mapEmptyTags[tagName] !== undefined) { + if (event.close) { + reporter.error(`The empty tag : [ ${tagName} ] must not use self closed syntax.`, event.line, event.col, this, event.raw); + } + } + }); + }, + }; + var headScriptDisabled = {}; Object.defineProperty(headScriptDisabled, "__esModule", { value: true }); @@ -1214,25 +1274,6 @@ }, }; - var emptyTagNotSelfClosed = {}; - - Object.defineProperty(emptyTagNotSelfClosed, "__esModule", { value: true }); - emptyTagNotSelfClosed.default = { - id: 'empty-tag-not-self-closed', - description: 'Empty tags must not use self closed syntax.', - init(parser, reporter) { - const mapEmptyTags = parser.makeMap('area,base,basefont,bgsound,br,col,frame,hr,img,input,isindex,link,meta,param,embed,track,command,source,keygen,wbr'); - parser.addListener('tagstart', (event) => { - const tagName = event.tagName.toLowerCase(); - if (mapEmptyTags[tagName] !== undefined) { - if (event.close) { - reporter.error(`The empty tag : [ ${tagName} ] must not use self closed syntax.`, event.line, event.col, this, event.raw); - } - } - }); - }, - }; - var tagnameLowercase = {}; Object.defineProperty(tagnameLowercase, "__esModule", { value: true }); @@ -1270,46 +1311,6 @@ }, }; - var titleRequire = {}; - - Object.defineProperty(titleRequire, "__esModule", { value: true }); - titleRequire.default = { - id: 'title-require', - description: '