UNPKG

assetgraph

Version:

An auto discovery dependency graph based optimization framework for web pages and applications

224 lines (190 loc) 4.97 kB
const HtmlRelation = require('./HtmlRelation'); function getExtension(url) { const matches = url.match(/\.([^$#?]+)/); if (matches && matches[1]) { return matches[1]; } } /** * @constructor * @abstract * @augments HtmlRelation * * Abstract class for resource hint relations */ class HtmlResourceHint extends HtmlRelation { get href() { return this.node.getAttribute('href'); } set href(href) { this.node.setAttribute('href', href); } get contentType() { if ('_contentType' in this) { return this._contentType; } else { if ( this.to.contentType && this.to.contentType !== 'application/octet-stream' ) { this._contentType = this.to.contentType; } // Let's see if we can do slightly better by extension if ( !this._contentType || this._contentType === 'application/octet-stream' ) { // Snapped from https://github.com/jshttp/mime-db/blob/master/db.json const extensionToTypeMap = { woff: 'font/woff', woff2: 'font/woff2', ttf: 'font/ttf', eot: 'application/vnd.ms-fontobject', otf: 'font/otf', js: 'application/javascript' }; const betterContentType = extensionToTypeMap[getExtension(this.to.url)]; if (betterContentType) { this._contentType = betterContentType; } } return this._contentType; } } // Map Assetgraph relations to request destination (<link as="${destination}">). // See https://fetch.spec.whatwg.org/#concept-request-destination get requestDestination() { if (this.to.isImage) { return 'image'; } if (this.to.type === 'JavaScript') { return 'script'; } if (this.to.type === 'Css') { return 'style'; } const incomingRelations = this.to.incomingRelations; const firstOtherIncomingRelation = incomingRelations && incomingRelations.find(relation => relation !== this); if (firstOtherIncomingRelation) { const relType = firstOtherIncomingRelation.type; if (['HtmlStyle', 'CssImport'].includes(relType)) { return 'style'; } if ( this.to.type === 'Html' && (relType === 'HtmlFrame' || relType === 'HtmlIFrame') ) { return 'document'; } if (relType === 'HtmlAudio') { return 'media'; } if (relType === 'HtmlVideo') { return 'media'; } if (relType === 'CssFontFaceSrc') { return 'font'; } if (relType === 'HtmlEmbed') { return 'embed'; } if (relType === 'HtmlObject') { return 'object'; } } // If the asset hasn't been populated, fall back to best guess based on file extension if (this.to.url) { switch (getExtension(this.to.url)) { case 'css': return 'style'; case 'js': return 'script'; case 'svg': case 'jpg': case 'jpeg': case 'png': case 'gif': case 'webp': case 'ico': case 'tiff': case 'bmp': return 'image'; case 'html': return 'document'; case 'woff': case 'woff2': case 'ttf': case 'eot': case 'otf': return 'font'; } } return ''; } get as() { if (typeof this._as === 'undefined') { this._as = this.requestDestination; } return this._as; } get targetType() { const as = this.node && this.node.getAttribute('as'); if (as) { switch (as) { case 'script': case 'worker': return 'JavaScript'; case 'style': return 'Css'; case 'image': return 'Image'; case 'font': return 'Font'; } } } set as(type) { // See https://www.w3.org/TR/preload/#as-attribute const validRequestDestinations = [ '', 'media', 'audio', 'video', 'track', 'script', 'style', 'font', 'image', 'fetch', 'worker', 'embed', 'object', 'document' ]; if (!validRequestDestinations.includes(type)) { throw new Error( `relations/HtmlResourceHint: Not a valid type for the as-method: ${type}` ); } this._as = type; if (this.node) { this.node.setAttribute('as', type); } } attach(position, adjacentRelation) { super.attach(position, adjacentRelation); if (this.crossorigin) { // fonts should always be treated as crossorigin: https://w3c.github.io/preload/#h-note6 this.node.setAttribute('crossorigin', 'anonymous'); } } inlineHtmlRelation() { throw new Error( 'HtmlResourceHint: Inlining of resource hints is not allowed' ); } } HtmlResourceHint.prototype.preferredPosition = 'lastInHead'; module.exports = HtmlResourceHint;