From ec171ac847b98f495006845836d17f1d19d79b06 Mon Sep 17 00:00:00 2001 From: Simon Pieters Date: Fri, 29 Oct 2021 03:30:08 +0200 Subject: [PATCH] feat(rules): add empty-tag-not-self-closed rule (#696) Fixes #311. --- src/core/rules/empty-tag-not-self-closed.ts | 26 +++++++++++++++++ src/core/rules/index.ts | 1 + src/core/rules/tag-self-close.ts | 2 +- src/core/types.ts | 1 + test/rules/empty-tag-not-self-closed.spec.js | 30 ++++++++++++++++++++ test/rules/tag-self-close.spec.js | 1 - 6 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/core/rules/empty-tag-not-self-closed.ts create mode 100644 test/rules/empty-tag-not-self-closed.spec.js diff --git a/src/core/rules/empty-tag-not-self-closed.ts b/src/core/rules/empty-tag-not-self-closed.ts new file mode 100644 index 000000000..56d47c88f --- /dev/null +++ b/src/core/rules/empty-tag-not-self-closed.ts @@ -0,0 +1,26 @@ +import { Rule } from '../types' + +export 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' + ) //HTML 4.01 + HTML 5 + + 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 + ) + } + } + }) + }, +} as Rule diff --git a/src/core/rules/index.ts b/src/core/rules/index.ts index 6909c457c..5838a9915 100644 --- a/src/core/rules/index.ts +++ b/src/core/rules/index.ts @@ -25,6 +25,7 @@ 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 emptyTagNotSelfClosed } from './empty-tag-not-self-closed' export { default as tagnameLowercase } from './tagname-lowercase' export { default as tagnameSpecialChars } from './tagname-specialchars' export { default as titleRequire } from './title-require' diff --git a/src/core/rules/tag-self-close.ts b/src/core/rules/tag-self-close.ts index 63b4822ed..e7f6b12c4 100644 --- a/src/core/rules/tag-self-close.ts +++ b/src/core/rules/tag-self-close.ts @@ -5,7 +5,7 @@ export default { description: 'Empty tags must be self closed.', init(parser, reporter) { const mapEmptyTags = parser.makeMap( - 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,track,command,source,keygen,wbr' + 'area,base,basefont,bgsound,br,col,frame,hr,img,input,isindex,link,meta,param,embed,track,command,source,keygen,wbr' ) //HTML 4.01 + HTML 5 parser.addListener('tagstart', (event) => { diff --git a/src/core/types.ts b/src/core/types.ts index b7d230076..afac7c874 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -20,6 +20,7 @@ export interface Ruleset { 'attr-whitespace'?: boolean 'doctype-first'?: boolean 'doctype-html5'?: boolean + 'empty-tag-not-self-closed'?: boolean 'head-script-disabled'?: boolean 'href-abs-or-rel'?: 'abs' | 'rel' 'id-class-ad-disabled'?: boolean diff --git a/test/rules/empty-tag-not-self-closed.spec.js b/test/rules/empty-tag-not-self-closed.spec.js new file mode 100644 index 000000000..9c2b69e00 --- /dev/null +++ b/test/rules/empty-tag-not-self-closed.spec.js @@ -0,0 +1,30 @@ +const expect = require('expect.js') + +const HTMLHint = require('../../dist/htmlhint.js').HTMLHint + +const ruldId = 'empty-tag-not-self-closed' +const ruleOptions = {} + +ruleOptions[ruldId] = true + +describe(`Rules: ${ruldId}`, () => { + it('The empty tag no closed should not result in an error', () => { + const code = '
' + const messages = HTMLHint.verify(code, ruleOptions) + expect(messages.length).to.be(0) + }) + + it('Closed empty tag should result in an error', () => { + const code = '
' + const messages = HTMLHint.verify(code, ruleOptions) + expect(messages.length).to.be(2) + expect(messages[0].rule.id).to.be(ruldId) + expect(messages[0].line).to.be(1) + expect(messages[0].col).to.be(1) + expect(messages[0].type).to.be('error') + expect(messages[1].rule.id).to.be(ruldId) + expect(messages[1].line).to.be(1) + expect(messages[1].col).to.be(7) + expect(messages[1].type).to.be('error') + }) +}) diff --git a/test/rules/tag-self-close.spec.js b/test/rules/tag-self-close.spec.js index 93843a747..88be006c9 100644 --- a/test/rules/tag-self-close.spec.js +++ b/test/rules/tag-self-close.spec.js @@ -15,7 +15,6 @@ describe(`Rules: ${ruldId}`, () => { expect(messages[0].rule.id).to.be(ruldId) expect(messages[0].line).to.be(1) expect(messages[0].col).to.be(1) - console.log('lll', messages[0].type) expect(messages[0].type).to.be('warning') expect(messages[1].rule.id).to.be(ruldId) expect(messages[1].line).to.be(1)