fileoverview import { HttpHeaders } from '@hint/utils-types' ; import { normalizeString } from '@hint/utils-string' ; import { isLocalFile } from '@hint/utils-network' ; import { HTMLDocument , HTMLElement } from '@hint/utils-dom' ; import { IHint , TraverseEnd } from 'hint/dist/src/lib/types' ; import { HintContext } from 'hint/dist/src/lib/hint-context' ; import { Severity } from '@hint/utils-types' ; import meta from './meta' ; import { getMessage } from './i18n.import' ; export default class HighestAvailableDocumentModeHint implements IHint { public static readonly meta = meta ; public constructor ( context ) { let requireMetaElement = false ; let suggestRemoval = false ; const checkHeader = ( resource , responseHeaders ) => { const originalHeaderValue = responseHeaders [ 'x-ua-compatible' ] ; const headerValue = normalizeString ( originalHeaderValue ) ; if ( headerValue === null ) { if ( ! requireMetaElement && ! suggestRemoval ) { context . report ( resource , getMessage ( 'responseShouldInclude' , context . language ) , { severity : Severity . hint } ) ; } return ; } const codeSnippet = `Content-Type: ${ originalHeaderValue }` ; const codeLanguage = 'http' ; if ( suggestRemoval ) { context . report ( resource , getMessage ( 'responseUnneeded' , context . language ) , { codeLanguage , codeSnippet , severity : Severity . hint } ) ; return ; } if ( headerValue !== 'ie=edge' ) { context . report ( resource , getMessage ( 'headerValueShouldBe' , context . language ) , { codeLanguage , codeSnippet , severity : Severity . error } ) ; } } ; const checkMetaElement = ( resource ) => { const pageDOM = context . pageDOM as HTMLDocument ; const XUACompatibleMetaElements = pageDOM . querySelectorAll ( 'meta[http-equiv=x-ua-compatible i]' ) ; if ( ! requireMetaElement || suggestRemoval ) { if ( XUACompatibleMetaElements . length !== 0 ) { const errorMessage = suggestRemoval ? getMessage ( 'metaElementShouldNotBeSpecified' , context . language ) : getMessage ( 'metaElementShouldNotBeSpecifiedUseHeader' , context . language ) ; for ( const metaElement of XUACompatibleMetaElements ) { context . report ( resource , errorMessage , { element : metaElement , severity : Severity . hint } ) ; } } return ; } if ( XUACompatibleMetaElements . length === 0 ) { context . report ( resource , getMessage ( 'metaElementShouldBeSpecified' , context . language ) , { severity : Severity . error } ) ; return ; } const XUACompatibleMetaElement = XUACompatibleMetaElements [ 0 ] ; const contentValue = XUACompatibleMetaElement . getAttribute ( 'content' ) ; if ( normalizeString ( contentValue ) !== 'ie=edge' ) { const message = getMessage ( 'metaElementValueShouldBe' , context . language ) ; context . report ( resource , message , { element : XUACompatibleMetaElement , severity : Severity . error } ) ; } const headElements = pageDOM . querySelectorAll ( 'head *' ) ; let metaElementIsBeforeRequiredElements = true ; for ( const headElement of headElements ) { if ( headElement . isSame ( XUACompatibleMetaElement ) ) { if ( ! metaElementIsBeforeRequiredElements ) { const message = getMessage ( 'metaElementWrongPosition' , context . language ) ; context . report ( resource , message , { element : XUACompatibleMetaElement , severity : Severity . error } ) ; } break ; } if ( ! [ 'title' , 'meta' ] . includes ( headElement . nodeName . toLowerCase ( ) ) ) { metaElementIsBeforeRequiredElements = false ; } } const bodyMetaElements = pageDOM . querySelectorAll ( 'body meta[http-equiv=x-ua-compatible i]' ) ; if ( ( bodyMetaElements . length > 0 ) && bodyMetaElements [ 0 ] . isSame ( XUACompatibleMetaElement ) ) { const message = getMessage ( 'metaElementNotBody' , context . language ) ; context . report ( resource , message , { element : XUACompatibleMetaElement , severity : Severity . error } ) ; return ; } if ( XUACompatibleMetaElements . length > 1 ) { const metaElements = XUACompatibleMetaElements . slice ( 1 ) ; for ( const metaElement of metaElements ) { const message = getMessage ( 'metaElementDuplicated' , context . language ) ; context . report ( resource , message , { element : metaElement , severity : Severity . warning } ) ; } } } ; const loadHintConfigs = ( ) => { requireMetaElement = ( context . hintOptions && context . hintOptions . requireMetaElement ) || false ; suggestRemoval = [ 'ie 8' , 'ie 9' , 'ie 10' ] . every ( ( e ) => { return ! context . targetedBrowsers . includes ( e ) ; } ) ; } ; const validate = ( { resource } ) => { if ( ! isLocalFile ( resource ) && context . pageHeaders ) { checkHeader ( resource , context . pageHeaders ) ; } checkMetaElement ( resource ) ; } ; loadHintConfigs ( ) ; context . on ( 'traverse::end' , validate ) ; } }