fileoverview import { IHint } from 'hint/dist/src/lib/types' ; import { HintContext } from 'hint/dist/src/lib/hint-context' ; import { HTMLEvents } from '@hint/parser-html' ; import { normalizeString } from '@hint/utils-string' ; import { Severity } from '@hint/utils-types' ; import meta from './meta' ; import { getMessage } from './i18n.import' ; export default class MetaCharsetUTF8Hint implements IHint { public static readonly meta = meta ; public constructor ( context ) { let validated = false ; context . on ( 'scan::end' , ( ) => { validated = false ; } ) ; context . on ( 'parse::end::html' , ( { document , html , resource } ) => { if ( validated ) { return ; } validated = true ; if ( document . isFragment ) { return ; } const charsetMetaElements = document . querySelectorAll ( 'meta[charset], meta[http-equiv="content-type" i]' ) ; if ( charsetMetaElements . length === 0 ) { context . report ( resource , getMessage ( 'metaElementNotSpecified' , context . language ) , { severity : Severity . warning } ) ; return ; } const charsetMetaElement = charsetMetaElements [ 0 ] ; if ( charsetMetaElement . getAttribute ( 'http-equiv' ) !== null ) { context . report ( resource , getMessage ( 'metaElementShorter' , context . language ) , { element : charsetMetaElement , severity : Severity . warning } ) ; } else { const metaValue = normalizeString ( charsetMetaElement . getAttribute ( 'charset' ) ) ; if ( metaValue !== 'utf-8' ) { const severity = metaValue === 'utf8' ? Severity . warning : Severity . error ; context . report ( resource , getMessage ( 'metaElementWrongValue' , context . language ) , { element : charsetMetaElement , severity } ) ; } } const firstHeadElement = document . querySelectorAll ( 'head :first-child' ) [ 0 ] ; const isCharsetMetaFirstHeadElement = charsetMetaElement && firstHeadElement && charsetMetaElement . isSame ( firstHeadElement ) ; const headElementContent = document . querySelectorAll ( 'head' ) [ 0 ] . outerHTML ; const isMetaElementFirstHeadContent = ( /^<head[^>]*>\s*<meta/ ) . test ( headElementContent ) ; if ( ! isCharsetMetaFirstHeadElement || ! isMetaElementFirstHeadContent ) { const severity = ( firstHeadElement . getLocation ( ) . endOffset || 0 ) <= 1024 ? Severity . hint : Severity . error ; context . report ( resource , getMessage ( 'metaElementFirstThing' , context . language ) , { element : charsetMetaElement , severity } ) ; } const bodyMetaElements = document . querySelectorAll ( 'body meta[charset], body meta[http-equiv="content-type" i]' ) ; if ( bodyMetaElements [ 0 ] && bodyMetaElements [ 0 ] . isSame ( charsetMetaElement ) ) { context . report ( resource , getMessage ( 'metaElementInBody' , context . language ) , { element : charsetMetaElement , severity : Severity . error } ) ; return ; } if ( charsetMetaElements . length > 1 ) { const metaElements = charsetMetaElements . slice ( 1 ) ; for ( const metaElement of metaElements ) { context . report ( resource , getMessage ( 'metaElementDuplicated' , context . language ) , { element : metaElement , severity : Severity . warning } ) ; } } } ) ; } }