UNPKG

@hokify/penthouse

Version:

Generate critical path CSS for web pages

134 lines (110 loc) 4.59 kB
"use strict"; var _cssTree = _interopRequireDefault(require("css-tree")); var _cssMediaquery = _interopRequireDefault(require("css-mediaquery")); var _debug = _interopRequireDefault(require("debug")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const debuglog = (0, _debug.default)('penthouse:preformatting:nonMatchingMediaQueryRemover'); // only filters out: // - @print // - min-width > width OR min-height > height // (the latter only if !keepLargerMediaQueries -- which is the default) // - min-width > width AND max-width > width function _isMatchingMediaQuery(mediaQuery, matchConfig) { // TODO: use the media query parsing from css-tree instead let mediaAST; try { mediaAST = _cssMediaquery.default.parse(mediaQuery); } catch (e) { // cant parse, most likely browser cant either return false; } var keep = mediaAST.some(function (mq) { // not sure why css-mediaquery library sometimes flags the inverse as type, // rather than the inverse field, but for our purposes we want to treat // them the same. const isInverse = mq.inverse || mq.type === 'not'; // f.e. @media all {} // go for false positives over false negatives, // i.e. accept @media randomThing {} if (mq.expressions.length === 0) { // the checks below are only valid if the media query has no other properties other than the media type if (!isInverse && mq.type === 'print' || isInverse && mq.type === 'screen') { return false; } return true; } /* costructing the test to match against the mediaquery if the mediaquery (mq) has "AND" conditions, mq.expressions is an array of feature objects { modifier, feature, value } mq.expressions.length > 1 if the mediaquery (mq) has "OR" conditions, the mediaquery is split in _n_ mq objects, each having an expressions array of 1 feature objects { modifier, feature, value } */ return mq.expressions.some(function ({ modifier, feature, value }) { if (modifier === 'min') { const constructedQuery = `(min-${feature}: ${value})`; // css-mediaquery does not match inversed queries correctly, hence the if..else below if (!isInverse) { return _cssMediaquery.default.match(constructedQuery, matchConfig); } else { return !_cssMediaquery.default.match(constructedQuery, matchConfig); } } else { if (mq.expressions.length > 1) { const constructedQuery = mq.expressions.map(({ modifier, feature, value }) => `(${modifier}-${feature}: ${value})`).join(' and '); // css-mediaquery does not match inversed queries correctly, hence the if..else below if (!isInverse) { return _cssMediaquery.default.match(constructedQuery, matchConfig); } else { return !_cssMediaquery.default.match(constructedQuery, matchConfig); } } else { return true; } } }); }); return keep; } function nonMatchingMediaQueryRemover(ast, width, height, keepLargerMediaQueries = false) { debuglog('BEFORE'); const matchConfig = { type: 'screen', width: width + 'px', height: height + 'px' }; const matchLargerMQConfig = { type: 'screen', width: '99999px', height: '99999px' }; debuglog('matchConfig: ' + JSON.stringify(matchConfig, null, 2) + '\n' + 'keepLargerMediaQueries: ' + keepLargerMediaQueries); _cssTree.default.walk(ast, { visit: 'Atrule', enter: (atrule, item, list) => { // ignore (keep) all non media query rules if (_cssTree.default.keyword(atrule.name).name !== 'media') { return; } // this can happen - why? (atrule.prelude === null) // and should we remove this rule here, or keep it? if (!atrule.prelude) { return; } const mediaQuery = _cssTree.default.generate(atrule.prelude); // ismatching true when mq must be kept // if keep larger mq, keep if matching OR matching matchKeepLargeConfig const isMatching = keepLargerMediaQueries ? _isMatchingMediaQuery(mediaQuery, matchConfig) || _isMatchingMediaQuery(mediaQuery, matchLargerMQConfig) : _isMatchingMediaQuery(mediaQuery, matchConfig); if (!isMatching) { debuglog('DROP: ' + `(${mediaQuery}), `); list.remove(item); } else { debuglog('KEEP: ' + `(${mediaQuery}), `); } } }); return ast; } module.exports = nonMatchingMediaQueryRemover;