UNPKG

assetgraph

Version:

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

151 lines (139 loc) 4.63 kB
const _ = require('lodash'); const errors = require('../errors'); const Text = require('./Text'); class CacheManifest extends Text { get parseTree() { if (!this._parseTree) { const parseTree = {}; const syntaxErrors = []; let currentSectionName = 'CACHE'; for (const [i, line] of this.text.split(/\r?\n|\n?\r/).entries()) { if (i === 0) { if (line === 'CACHE MANIFEST') { continue; // Skip } else { syntaxErrors.push( new errors.SyntaxError({ message: 'CacheManifest.parseTree getter: The first line of the cache manifest wasn\'t "CACHE MANIFEST:"', asset: this, }) ); } } const matchNewSection = line.match(/^(CACHE|NETWORK|FALLBACK):\s*$/); if (matchNewSection) { currentSectionName = matchNewSection[1]; } else if (!/^\s*$/.test(line)) { if (!parseTree[currentSectionName]) { parseTree[currentSectionName] = []; } if (/^\s*#/.test(line)) { parseTree[currentSectionName].push({ comment: line.replace(/^\s*#/, ''), }); } else { const tokens = line.replace(/^\s+|\s+$/g).split(' '); // Trim just in case if (tokens.length === (currentSectionName === 'FALLBACK' ? 2 : 1)) { parseTree[currentSectionName].push({ tokens }); } else { syntaxErrors.push( new errors.SyntaxError({ message: `CacheManifest.parseTree getter: Parse error in section ${currentSectionName}, line ${i}: ${line}`, line: i, asset: this, }) ); } } } } if (syntaxErrors.length > 0) { if (this.assetGraph) { for (const syntaxError of syntaxErrors) { this.assetGraph.warn(syntaxError); } } else { throw new Error(_.map(syntaxErrors, 'message').join('\n')); } } this._parseTree = parseTree; } return this._parseTree; } set parseTree(parseTree) { this.unload(); this._parseTree = parseTree; this._lastKnownByteLength = undefined; if (this.assetGraph) { this.populate(); } this.markDirty(); } get text() { function getSectionText(nodes) { return `${nodes .map((node) => { if (typeof node.comment !== 'undefined') { return `#${node.comment}`; } else { return node.tokens.join(' '); } }) .join('\n')}\n`; } if (typeof this._text !== 'string') { if (this._parseTree) { this._text = 'CACHE MANIFEST\n'; // The heading for the CACHE section can be omitted if it's the first thing in the manifest, // so put it first if there is one. if (this._parseTree.CACHE) { this._text += getSectionText(this._parseTree.CACHE); } for (const sectionName of Object.keys(this._parseTree)) { const nodes = this._parseTree[sectionName]; if (sectionName !== 'CACHE' && nodes.length) { this._text += `${sectionName}:\n${getSectionText(nodes)}`; } } } else { this._text = this._getTextFromRawSrc(); } } return this._text; } set text(text) { super.text = text; } findOutgoingRelationsInParseTree() { const outgoingRelations = super.findOutgoingRelationsInParseTree(); if (!this.isLoaded) { return outgoingRelations; } // Traverse the sections in alphabetical order so the order of the relations is predictable for (const sectionName of Object.keys(this.parseTree).sort()) { const nodes = this.parseTree[sectionName]; if (sectionName !== 'NETWORK') { for (const node of nodes) { if (node.tokens) { // In the CACHE section there's only one token per entry, in FALLBACK // there's the online URL followed by the offline URL (the one we want). // Just pick the last token as the url. outgoingRelations.push({ type: 'CacheManifestEntry', href: node.tokens[node.tokens.length - 1], node, sectionName, }); } } } } return outgoingRelations; } } Object.assign(CacheManifest.prototype, { contentType: 'text/cache-manifest', supportedExtensions: ['.appcache'], }); module.exports = CacheManifest;