Skip to content

Commit 840f5b7

Browse files
authored
New Rule: Meta Viewport Require (#1618)
* New Rule: Meta Viewport Require Learn more: - https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Viewport_meta_element * Update list-rules.md
1 parent 044e6b3 commit 840f5b7

File tree

8 files changed

+159
-27
lines changed

8 files changed

+159
-27
lines changed

.cspell.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
"msapplication",
3838
"nocolor",
3939
"nodir",
40-
"noopener",
4140
"nomix",
41+
"noopener",
4242
"npmjs",
4343
"onttt",
4444
"opencollective",
@@ -62,6 +62,7 @@
6262
"ufff",
6363
"uffff",
6464
"unmin",
65+
"webp",
6566
"withastro",
6667
"xiang",
6768
"Yanis"

dist/core/rules/index.js

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/core/rules/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export { default as inlineStyleDisabled } from './inline-style-disabled'
2424
export { default as inputRequiresLabel } from './input-requires-label'
2525
export { default as mainRequire } from './main-require'
2626
export { default as metaDescriptionRequire } from './meta-description-require'
27+
export { default as metaViewportRequire } from './meta-viewport-require'
2728
export { default as scriptDisabled } from './script-disabled'
2829
export { default as spaceTabMixedDisabled } from './space-tab-mixed-disabled'
2930
export { default as specCharEscape } from './spec-char-escape'
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Block, Listener } from '../htmlparser'
2+
import { Rule } from '../types'
3+
4+
export default {
5+
id: 'meta-viewport-require',
6+
description:
7+
'<meta name="viewport"> with non-blank content must be present in <head> tag.',
8+
init(parser, reporter) {
9+
let headSeen = false
10+
let metaViewportSeen = false
11+
let metaViewportContent = ''
12+
let headEvent: Block | null = null
13+
14+
const onTagStart: Listener = (event) => {
15+
const tagName = event.tagName.toLowerCase()
16+
if (tagName === 'head') {
17+
headSeen = true
18+
headEvent = event
19+
} else if (tagName === 'meta') {
20+
const mapAttrs = parser.getMapAttrs(event.attrs)
21+
if (mapAttrs['name'] && mapAttrs['name'].toLowerCase() === 'viewport') {
22+
metaViewportSeen = true
23+
metaViewportContent = mapAttrs['content'] || ''
24+
}
25+
}
26+
}
27+
28+
parser.addListener('tagstart', onTagStart)
29+
parser.addListener('end', () => {
30+
if (headSeen && headEvent) {
31+
if (!metaViewportSeen) {
32+
reporter.error(
33+
'<meta name="viewport"> must be present in <head> tag.',
34+
headEvent.line,
35+
headEvent.col,
36+
this,
37+
headEvent.raw
38+
)
39+
} else if (metaViewportContent.trim() === '') {
40+
reporter.error(
41+
'<meta name="viewport"> content attribute must not be empty.',
42+
headEvent.line,
43+
headEvent.col,
44+
this,
45+
headEvent.raw
46+
)
47+
}
48+
}
49+
})
50+
},
51+
} as Rule
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const HTMLHint = require('../../dist/htmlhint.js').HTMLHint
2+
const ruleId = 'meta-viewport-require'
3+
4+
describe('Rule: meta-viewport-require', () => {
5+
it('should not report an error when a valid meta viewport is present', () => {
6+
const code = `<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1"></head><body></body></html>`
7+
const messages = HTMLHint.verify(code, { [ruleId]: true })
8+
expect(messages.length).toBe(0)
9+
})
10+
11+
it('should report an error when meta viewport is missing', () => {
12+
const code = `<!DOCTYPE html><html><head></head><body></body></html>`
13+
const messages = HTMLHint.verify(code, { [ruleId]: true })
14+
expect(messages.length).toBe(1)
15+
expect(messages[0].message).toBe(
16+
'<meta name="viewport"> must be present in <head> tag.'
17+
)
18+
})
19+
20+
it('should report an error when meta viewport content is blank', () => {
21+
const code = `<!DOCTYPE html><html><head><meta name="viewport" content=" "></head><body></body></html>`
22+
const messages = HTMLHint.verify(code, { [ruleId]: true })
23+
expect(messages.length).toBe(1)
24+
expect(messages[0].message).toBe(
25+
'<meta name="viewport"> content attribute must not be empty.'
26+
)
27+
})
28+
29+
it('should report an error when meta viewport is missing and only other meta tags are present', () => {
30+
const code = `<!DOCTYPE html><html><head><meta name="description" content="desc"></head><body></body></html>`
31+
const messages = HTMLHint.verify(code, { [ruleId]: true })
32+
expect(messages.length).toBe(1)
33+
expect(messages[0].message).toBe(
34+
'<meta name="viewport"> must be present in <head> tag.'
35+
)
36+
})
37+
})

website/src/content/docs/list-rules.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,41 @@ description: A complete list of all the rules for HTMLHint
88

99
- [`doctype-first`](/docs/user-guide/rules/doctype-first): Doctype must be declared first.
1010
- [`doctype-html5`](/docs/user-guide/rules/doctype-html5): Invalid doctype.
11-
- [`html-lang-require`](/docs/user-guide/rules/html-lang-require): The HTML lang attribute is required.
1211
- [`head-script-disabled`](/docs/user-guide/rules/head-script-disabled): The `<script>` tag cannot be used in `<head>` tag.
13-
- [`style-disabled`](/docs/user-guide/rules/style-disabled): `<style>` tags cannot be used.
12+
- [`html-lang-require`](/docs/user-guide/rules/html-lang-require): The HTML lang attribute is required.
13+
- [`meta-description-require`](/docs/user-guide/rules/meta-description-require): `<meta name="description">` with non-blank content must be present in `<head>` tag.
14+
- [`meta-viewport-require`](/docs/user-guide/rules/meta-viewport-require): `<meta name="viewport">` with non-blank content must be present in `<head>` tag.
1415
- [`script-disabled`](/docs/user-guide/rules/script-disabled): `<script>` tags cannot be used.
16+
- [`style-disabled`](/docs/user-guide/rules/style-disabled): `<style>` tags cannot be used.
1517
- [`title-require`](/docs/user-guide/rules/title-require): `<title>` must be present in `<head>` tag.
16-
- [`meta-description-require`](/docs/user-guide/rules/meta-description-require): `<meta name="description">` with non-blank content must be present in `<head>` tag.
1718

1819
## Attributes
1920

21+
- [`alt-require`](/docs/user-guide/rules/alt-require): The alt attribute of an img element must be present and alt attribute of area[href] and input[type=image] must have a value.
2022
- [`attr-lowercase`](/docs/user-guide/rules/attr-lowercase): All attribute names must be in lowercase.
2123
- [`attr-no-duplication`](/docs/user-guide/rules/attr-no-duplication): Elements cannot have duplicate attributes.
2224
- [`attr-no-unnecessary-whitespace`](/docs/user-guide/rules/attr-no-unnecessary-whitespace): No spaces between attribute names and values.
25+
- [`attr-sorted`](/docs/user-guide/rules/attr-sorted): Attributes should be sorted in order.
2326
- [`attr-unsafe-chars`](/docs/user-guide/rules/attr-unsafe-chars): Attribute values cannot contain unsafe chars.
2427
- [`attr-value-double-quotes`](/docs/user-guide/rules/attr-value-double-quotes): Attribute values must be in double quotes.
25-
- [`attr-value-single-quotes`](/docs/user-guide/rules/attr-value-single-quotes): Attribute values must be in single quotes.
2628
- [`attr-value-not-empty`](/docs/user-guide/rules/attr-value-not-empty): All attributes must have values.
27-
- [`attr-sorted`](/docs/user-guide/rules/attr-sorted): Attributes should be sorted in order.
29+
- [`attr-value-single-quotes`](/docs/user-guide/rules/attr-value-single-quotes): Attribute values must be in single quotes.
2830
- [`attr-whitespace`](/docs/user-guide/rules/attr-whitespace): No leading or trailing spaces in attribute values.
29-
- [`alt-require`](/docs/user-guide/rules/alt-require): The alt attribute of an img element must be present and alt attribute of area[href] and input[type=image] must have a value.
3031
- [`button-type-require`](/docs/user-guide/rules/button-type-require): The type attribute of a button element must be present with a valid value: "button", "submit", or "reset".
3132
- [`input-requires-label`](/docs/user-guide/rules/input-requires-label): All [ input ] tags must have a corresponding [ label ] tag.
3233

3334
## Tags
3435

35-
- [`tags-check`](/docs/user-guide/rules/tags-check): Allowing specify rules for any tag and validate that
36+
- [`empty-tag-not-self-closed`](/docs/user-guide/rules/empty-tag-not-self-closed): The empty tag should not be closed by self.
37+
- [`h1-require`](/docs/user-guide/rules/h1-require): A document must have at least one `<h1>` element.
38+
- [`href-abs-or-rel`](/docs/user-guide/rules/href-abs-or-rel): An href attribute must be either absolute or relative.
39+
- [`main-require`](/docs/user-guide/rules/main-require): A document must have at least one `<main>` element in the `<body>` tag.
40+
- [`src-not-empty`](/docs/user-guide/rules/src-not-empty): The src attribute of an img(script,link) must have a value.
3641
- [`tag-pair`](/docs/user-guide/rules/tag-pair): Tag must be paired.
3742
- [`tag-self-close`](/docs/user-guide/rules/tag-self-close): Empty tags must be self closed.
3843
- [`tagname-lowercase`](/docs/user-guide/rules/tagname-lowercase): All HTML element names must be in lowercase.
3944
- [`tagname-specialchars`](/docs/user-guide/rules/tagname-specialchars): Tag names can only contain letters, numbers, "-", ":" or "\_".
40-
- [`empty-tag-not-self-closed`](/docs/user-guide/rules/empty-tag-not-self-closed): The empty tag should not be closed by self.
41-
- [`src-not-empty`](/docs/user-guide/rules/src-not-empty): The src attribute of an img(script,link) must have a value.
42-
- [`href-abs-or-rel`](/docs/user-guide/rules/href-abs-or-rel): An href attribute must be either absolute or relative.
43-
- [`h1-require`](/docs/user-guide/rules/h1-require): A document must have at least one `<h1>` element.
44-
- [`main-require`](/docs/user-guide/rules/main-require): A document must have at least one `<main>` element in the `<body>` tag.
45+
- [`tags-check`](/docs/user-guide/rules/tags-check): Allowing specify rules for any tag and validate that
4546

4647
## Id
4748

0 commit comments

Comments
 (0)