fileoverview import { URL } from 'url' ; import { relative } from 'path' ; import { IHint , HintContext , NetworkData } from 'hint' ; import { ProblemLocation , Severity } from '@hint/utils-types' ; import { ManifestEvents , ManifestParsed } from '@hint/parser-manifest' ; import { debug as d } from '@hint/utils-debug' ; import meta from './meta' ; import { getMessage } from './i18n.import' ; const debug = d ( __filename ) ; export default class ManifestScopedUrlsHint implements IHint { public static readonly meta = meta ; public constructor ( context ) { /**
         * See if the `start_url` is accessible.
         */ const startUrlAccessible = async ( startUrl , resource , startUrllocation ) : Promise < boolean > => { let networkData ; try { networkData = await context . fetchContent ( startUrl ) ; } catch ( e ) { debug ( `Failed to fetch ${ startUrl }` ) ; const message = getMessage ( 'requestFailedFor' , context . language ) ; context . report ( resource , message , { location : startUrllocation , severity : Severity . error } ) ; return false ; } const response = networkData . response ; if ( response . statusCode !== 200 ) { const message = getMessage ( 'specifiedStartUrlNotAccessible' , context . language , response . statusCode . toString ( ) ) ; context . report ( resource , message , { location : startUrllocation , severity : Severity . error } ) ; return false ; } return true ; } ; param parsedContent param resource const startUrlInScope = ( startURL , scope , resource , startUrllocation ) : boolean => { const relativePath = relative ( scope , startURL ) ; const inScope = relativePath && ! relativePath . startsWith ( '..' ) ; if ( relativePath === '' ) { return true ; } if ( ! inScope ) { const message = getMessage ( 'startUrlNotInScope' , context . language ) ; context . report ( resource , message , { location : startUrllocation , severity : Severity . error } ) ; return false ; } return true ; } ; const validate = async ( { getLocation , parsedContent : { start_url : startURL , scope } , resource } ) => { const resourceURL = new URL ( resource ) ; const hostnameWithProtocol = `${ resourceURL . protocol }//${ resourceURL . host }` ; debug ( `Validating hint manifest-scoped-urls` ) ; if ( startURL ) { const startUrlLocation = getLocation ( 'start_url' ) ! ; const notSameOrigin = startURL . startsWith ( 'http://' ) || startURL . startsWith ( 'https://' ) ; const computedScope = scope || `${ resource }/` ; if ( notSameOrigin ) { const message = getMessage ( 'startUrlMustHaveSameOrigin' , context . language ) ; context . report ( resource , message , { location : startUrlLocation , severity : Severity . error } ) ; return ; } startUrlInScope ( startURL , computedScope , resource , startUrlLocation ) ; const separator = startURL . startsWith ( '/' ) ? '' : '/' ; const absoluteStartUrl = hostnameWithProtocol + separator + startURL ; await startUrlAccessible ( absoluteStartUrl , resource , startUrlLocation ) ; } else { const message = getMessage ( 'propertyNotFound' , context . language ) ; context . report ( resource , message , { severity : Severity . warning } ) ; } } ; context . on ( 'parse::end::manifest' , validate ) ; } }