UNPKG

assetgraph

Version:

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

139 lines (119 loc) 3.99 kB
const postcss = require('postcss'); const Css = require('../assets/Css'); const urlModule = require('url'); // http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx module.exports = ( queryObj, { minimumIeVersion = 1, rulesPerStylesheetLimit } = {} ) => { if (typeof rulesPerStylesheetLimit === 'undefined') { if (minimumIeVersion === null) { rulesPerStylesheetLimit = Infinity; } else if (minimumIeVersion <= 9) { rulesPerStylesheetLimit = 4095; } else { rulesPerStylesheetLimit = 65534; } } function countRules(node) { let count = 0; if (node.selector) { count += node.selector.split(',').length; } if (Array.isArray(node.nodes)) { // FIXME: Verify that this counting algorithm is identical to what IE does for (const childNode of node.nodes) { count += countRules(childNode); } } return count; } function splitStyleSheet(cssAsset) { const output = []; let accumulatedRules = 0; let offset = 0; for (const [i, rule] of cssAsset.parseTree.nodes.entries()) { const selectors = countRules(rule); if (accumulatedRules + selectors <= rulesPerStylesheetLimit) { accumulatedRules += selectors; } else { output.push({ type: cssAsset.type, isPopulated: false, parseTree: postcss .root() .append( cssAsset.parseTree.nodes.slice(offset, i).map(Css.cloneWithRaws) ) }); offset = i; accumulatedRules = selectors; } } output.push({ type: cssAsset.type, isPopulated: false, parseTree: postcss .root() .append(cssAsset.parseTree.nodes.slice(offset).map(Css.cloneWithRaws)) }); return output; } return function splitCssIfIeLimitIsReached(assetGraph) { if (rulesPerStylesheetLimit === Infinity) { return; } for (const largeAsset of assetGraph.findAssets( Object.assign({ type: 'Css', isLoaded: true }, queryObj) )) { const count = countRules(largeAsset.parseTree); let replacements; if (count > rulesPerStylesheetLimit) { replacements = splitStyleSheet(largeAsset); const info = new Error( `${count} CSS rules, ${count - rulesPerStylesheetLimit} would be ignored by IE9 and below. Splitting into ${ replacements.length } chunks to resolve the problem.` ); info.asset = largeAsset; assetGraph.info(info); // Add replacement css assets to the graph replacements = replacements.map((replacement, i) => { const asset = assetGraph.addAsset(replacement); if (!largeAsset.isInline) { asset.url = new urlModule.URL( `${largeAsset.fileName.split('.').shift()}-${i}${ largeAsset.extension }`, largeAsset.url ).toString(); } assetGraph.addAsset(asset); return asset; }); // Add relations to new replacement assets to the graph for (const oldRelation of largeAsset.incomingRelations) { for (const replacement of replacements) { oldRelation.from.addRelation( { type: oldRelation.type, hrefType: oldRelation.hrefType, to: replacement }, 'before', oldRelation ); } oldRelation.detach(); } // Remove all traces of the original to large Css asset // We're reusing the parseTree of largeAsset, so make sure that // the relations don't get detached from it as a side effect // of AssetGraph#removeAsset: largeAsset.outgoingRelations = []; assetGraph.removeAsset(largeAsset); } } }; };