[6a3a178] | 1 | function _interopDefault(ex) {
|
---|
| 2 | return ex && typeof ex === 'object' && 'default' in ex ? ex.default : ex;
|
---|
| 3 | }
|
---|
| 4 |
|
---|
| 5 | var path = _interopDefault(require('path'));
|
---|
| 6 | var prettyBytes = _interopDefault(require('pretty-bytes'));
|
---|
| 7 | var sources = _interopDefault(require('webpack-sources'));
|
---|
| 8 | var postcss = _interopDefault(require('postcss'));
|
---|
| 9 | var cssnano = _interopDefault(require('cssnano'));
|
---|
| 10 | var log = _interopDefault(require('webpack-log'));
|
---|
| 11 | var minimatch = _interopDefault(require('minimatch'));
|
---|
| 12 | var jsdom = require('jsdom');
|
---|
| 13 | var css = _interopDefault(require('css'));
|
---|
| 14 |
|
---|
| 15 | function _unsupportedIterableToArray(o, minLen) {
|
---|
| 16 | if (!o) return;
|
---|
| 17 | if (typeof o === 'string') return _arrayLikeToArray(o, minLen);
|
---|
| 18 | var n = Object.prototype.toString.call(o).slice(8, -1);
|
---|
| 19 | if (n === 'Object' && o.constructor) n = o.constructor.name;
|
---|
| 20 | if (n === 'Map' || n === 'Set') return Array.from(o);
|
---|
| 21 | if (n === 'Arguments' || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))
|
---|
| 22 | return _arrayLikeToArray(o, minLen);
|
---|
| 23 | }
|
---|
| 24 |
|
---|
| 25 | function _arrayLikeToArray(arr, len) {
|
---|
| 26 | if (len == null || len > arr.length) len = arr.length;
|
---|
| 27 |
|
---|
| 28 | for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
|
---|
| 29 |
|
---|
| 30 | return arr2;
|
---|
| 31 | }
|
---|
| 32 |
|
---|
| 33 | function _createForOfIteratorHelperLoose(o, allowArrayLike) {
|
---|
| 34 | var it;
|
---|
| 35 |
|
---|
| 36 | if (typeof Symbol === 'undefined' || o[Symbol.iterator] == null) {
|
---|
| 37 | if (
|
---|
| 38 | Array.isArray(o) ||
|
---|
| 39 | (it = _unsupportedIterableToArray(o)) ||
|
---|
| 40 | (allowArrayLike && o && typeof o.length === 'number')
|
---|
| 41 | ) {
|
---|
| 42 | if (it) o = it;
|
---|
| 43 | var i = 0;
|
---|
| 44 | return function () {
|
---|
| 45 | if (i >= o.length)
|
---|
| 46 | return {
|
---|
| 47 | done: true,
|
---|
| 48 | };
|
---|
| 49 | return {
|
---|
| 50 | done: false,
|
---|
| 51 | value: o[i++],
|
---|
| 52 | };
|
---|
| 53 | };
|
---|
| 54 | }
|
---|
| 55 |
|
---|
| 56 | throw new TypeError(
|
---|
| 57 | 'Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.'
|
---|
| 58 | );
|
---|
| 59 | }
|
---|
| 60 |
|
---|
| 61 | it = o[Symbol.iterator]();
|
---|
| 62 | return it.next.bind(it);
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | /**
|
---|
| 66 | * Copyright 2018 Google LLC
|
---|
| 67 | *
|
---|
| 68 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
---|
| 69 | * use this file except in compliance with the License. You may obtain a copy of
|
---|
| 70 | * the License at
|
---|
| 71 | *
|
---|
| 72 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
| 73 | *
|
---|
| 74 | * Unless required by applicable law or agreed to in writing, software
|
---|
| 75 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
---|
| 76 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
---|
| 77 | * License for the specific language governing permissions and limitations under
|
---|
| 78 | * the License.
|
---|
| 79 | */
|
---|
| 80 | /**
|
---|
| 81 | * Parse HTML into a mutable, serializable DOM Document, provided by JSDOM.
|
---|
| 82 | * @private
|
---|
| 83 | * @param {String} html HTML to parse into a Document instance
|
---|
| 84 | */
|
---|
| 85 |
|
---|
| 86 | function createDocument(html) {
|
---|
| 87 | var jsdom$1 = new jsdom.JSDOM(html, {
|
---|
| 88 | contentType: 'text/html',
|
---|
| 89 | });
|
---|
| 90 | var window = jsdom$1.window;
|
---|
| 91 | var document = window.document;
|
---|
| 92 | document.$jsdom = jsdom$1;
|
---|
| 93 | return document;
|
---|
| 94 | }
|
---|
| 95 | /**
|
---|
| 96 | * Serialize a Document to an HTML String
|
---|
| 97 | * @private
|
---|
| 98 | * @param {Document} document A Document, such as one created via `createDocument()`
|
---|
| 99 | */
|
---|
| 100 |
|
---|
| 101 | function serializeDocument(document) {
|
---|
| 102 | return document.$jsdom.serialize();
|
---|
| 103 | }
|
---|
| 104 | /** Like node.textContent, except it works */
|
---|
| 105 |
|
---|
| 106 | function setNodeText(node, text) {
|
---|
| 107 | while (node.lastChild) {
|
---|
| 108 | node.removeChild(node.lastChild);
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | node.appendChild(node.ownerDocument.createTextNode(text));
|
---|
| 112 | }
|
---|
| 113 |
|
---|
| 114 | /**
|
---|
| 115 | * Copyright 2018 Google LLC
|
---|
| 116 | *
|
---|
| 117 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
---|
| 118 | * use this file except in compliance with the License. You may obtain a copy of
|
---|
| 119 | * the License at
|
---|
| 120 | *
|
---|
| 121 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
| 122 | *
|
---|
| 123 | * Unless required by applicable law or agreed to in writing, software
|
---|
| 124 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
---|
| 125 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
---|
| 126 | * License for the specific language governing permissions and limitations under
|
---|
| 127 | * the License.
|
---|
| 128 | */
|
---|
| 129 | /**
|
---|
| 130 | * Parse a textual CSS Stylesheet into a Stylesheet instance.
|
---|
| 131 | * Stylesheet is a mutable ReworkCSS AST with format similar to CSSOM.
|
---|
| 132 | * @see https://github.com/reworkcss/css
|
---|
| 133 | * @private
|
---|
| 134 | * @param {String} stylesheet
|
---|
| 135 | * @returns {css.Stylesheet} ast
|
---|
| 136 | */
|
---|
| 137 |
|
---|
| 138 | function parseStylesheet(stylesheet) {
|
---|
| 139 | return css.parse(stylesheet);
|
---|
| 140 | }
|
---|
| 141 | /**
|
---|
| 142 | * Serialize a ReworkCSS Stylesheet to a String of CSS.
|
---|
| 143 | * @private
|
---|
| 144 | * @param {css.Stylesheet} ast A Stylesheet to serialize, such as one returned from `parseStylesheet()`
|
---|
| 145 | * @param {Object} options Options to pass to `css.stringify()`
|
---|
| 146 | * @param {Boolean} [options.compress] Compress CSS output (removes comments, whitespace, etc)
|
---|
| 147 | */
|
---|
| 148 |
|
---|
| 149 | function serializeStylesheet(ast, options) {
|
---|
| 150 | return css.stringify(ast, options);
|
---|
| 151 | }
|
---|
| 152 | /**
|
---|
| 153 | * Converts a walkStyleRules() iterator to mark nodes with `.$$remove=true` instead of actually removing them.
|
---|
| 154 | * This means they can be removed in a second pass, allowing the first pass to be nondestructive (eg: to preserve mirrored sheets).
|
---|
| 155 | * @private
|
---|
| 156 | * @param {Function} iterator Invoked on each node in the tree. Return `false` to remove that node.
|
---|
| 157 | * @returns {(rule) => void} nonDestructiveIterator
|
---|
| 158 | */
|
---|
| 159 |
|
---|
| 160 | function markOnly(predicate) {
|
---|
| 161 | return function (rule) {
|
---|
| 162 | var sel = rule.selectors;
|
---|
| 163 |
|
---|
| 164 | if (predicate(rule) === false) {
|
---|
| 165 | rule.$$remove = true;
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | rule.$$markedSelectors = rule.selectors;
|
---|
| 169 |
|
---|
| 170 | if (rule._other) {
|
---|
| 171 | rule._other.$$markedSelectors = rule._other.selectors;
|
---|
| 172 | }
|
---|
| 173 |
|
---|
| 174 | rule.selectors = sel;
|
---|
| 175 | };
|
---|
| 176 | }
|
---|
| 177 | /**
|
---|
| 178 | * Apply filtered selectors to a rule from a previous markOnly run.
|
---|
| 179 | * @private
|
---|
| 180 | * @param {css.Rule} rule The Rule to apply marked selectors to (if they exist).
|
---|
| 181 | */
|
---|
| 182 |
|
---|
| 183 | function applyMarkedSelectors(rule) {
|
---|
| 184 | if (rule.$$markedSelectors) {
|
---|
| 185 | rule.selectors = rule.$$markedSelectors;
|
---|
| 186 | }
|
---|
| 187 |
|
---|
| 188 | if (rule._other) {
|
---|
| 189 | applyMarkedSelectors(rule._other);
|
---|
| 190 | }
|
---|
| 191 | }
|
---|
| 192 | /**
|
---|
| 193 | * Recursively walk all rules in a stylesheet.
|
---|
| 194 | * @private
|
---|
| 195 | * @param {css.Rule} node A Stylesheet or Rule to descend into.
|
---|
| 196 | * @param {Function} iterator Invoked on each node in the tree. Return `false` to remove that node.
|
---|
| 197 | */
|
---|
| 198 |
|
---|
| 199 | function walkStyleRules(node, iterator) {
|
---|
| 200 | if (node.stylesheet) return walkStyleRules(node.stylesheet, iterator);
|
---|
| 201 | node.rules = node.rules.filter(function (rule) {
|
---|
| 202 | if (rule.rules) {
|
---|
| 203 | walkStyleRules(rule, iterator);
|
---|
| 204 | }
|
---|
| 205 |
|
---|
| 206 | rule._other = undefined;
|
---|
| 207 | rule.filterSelectors = filterSelectors;
|
---|
| 208 | return iterator(rule) !== false;
|
---|
| 209 | });
|
---|
| 210 | }
|
---|
| 211 | /**
|
---|
| 212 | * Recursively walk all rules in two identical stylesheets, filtering nodes into one or the other based on a predicate.
|
---|
| 213 | * @private
|
---|
| 214 | * @param {css.Rule} node A Stylesheet or Rule to descend into.
|
---|
| 215 | * @param {css.Rule} node2 A second tree identical to `node`
|
---|
| 216 | * @param {Function} iterator Invoked on each node in the tree. Return `false` to remove that node from the first tree, true to remove it from the second.
|
---|
| 217 | */
|
---|
| 218 |
|
---|
| 219 | function walkStyleRulesWithReverseMirror(node, node2, iterator) {
|
---|
| 220 | if (node2 === null) return walkStyleRules(node, iterator);
|
---|
| 221 | if (node.stylesheet)
|
---|
| 222 | return walkStyleRulesWithReverseMirror(
|
---|
| 223 | node.stylesheet,
|
---|
| 224 | node2.stylesheet,
|
---|
| 225 | iterator
|
---|
| 226 | );
|
---|
| 227 |
|
---|
| 228 | var _splitFilter = splitFilter(node.rules, node2.rules, function (
|
---|
| 229 | rule,
|
---|
| 230 | index,
|
---|
| 231 | rules,
|
---|
| 232 | rules2
|
---|
| 233 | ) {
|
---|
| 234 | var rule2 = rules2[index];
|
---|
| 235 |
|
---|
| 236 | if (rule.rules) {
|
---|
| 237 | walkStyleRulesWithReverseMirror(rule, rule2, iterator);
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | rule._other = rule2;
|
---|
| 241 | rule.filterSelectors = filterSelectors;
|
---|
| 242 | return iterator(rule) !== false;
|
---|
| 243 | });
|
---|
| 244 |
|
---|
| 245 | node.rules = _splitFilter[0];
|
---|
| 246 | node2.rules = _splitFilter[1];
|
---|
| 247 | } // Like [].filter(), but applies the opposite filtering result to a second copy of the Array without a second pass.
|
---|
| 248 | // This is just a quicker version of generating the compliment of the set returned from a filter operation.
|
---|
| 249 |
|
---|
| 250 | function splitFilter(a, b, predicate) {
|
---|
| 251 | var aOut = [];
|
---|
| 252 | var bOut = [];
|
---|
| 253 |
|
---|
| 254 | for (var index = 0; index < a.length; index++) {
|
---|
| 255 | if (predicate(a[index], index, a, b)) {
|
---|
| 256 | aOut.push(a[index]);
|
---|
| 257 | } else {
|
---|
| 258 | bOut.push(a[index]);
|
---|
| 259 | }
|
---|
| 260 | }
|
---|
| 261 |
|
---|
| 262 | return [aOut, bOut];
|
---|
| 263 | } // can be invoked on a style rule to subset its selectors (with reverse mirroring)
|
---|
| 264 |
|
---|
| 265 | function filterSelectors(predicate) {
|
---|
| 266 | if (this._other) {
|
---|
| 267 | var _splitFilter2 = splitFilter(
|
---|
| 268 | this.selectors,
|
---|
| 269 | this._other.selectors,
|
---|
| 270 | predicate
|
---|
| 271 | );
|
---|
| 272 | var a = _splitFilter2[0];
|
---|
| 273 | var b = _splitFilter2[1];
|
---|
| 274 |
|
---|
| 275 | this.selectors = a;
|
---|
| 276 | this._other.selectors = b;
|
---|
| 277 | } else {
|
---|
| 278 | this.selectors = this.selectors.filter(predicate);
|
---|
| 279 | }
|
---|
| 280 | }
|
---|
| 281 |
|
---|
| 282 | function tap(inst, hook, pluginName, async, callback) {
|
---|
| 283 | if (inst.hooks) {
|
---|
| 284 | var camel = hook.replace(/-([a-z])/g, function (s, i) {
|
---|
| 285 | return i.toUpperCase();
|
---|
| 286 | });
|
---|
| 287 | inst.hooks[camel][async ? 'tapAsync' : 'tap'](pluginName, callback);
|
---|
| 288 | } else {
|
---|
| 289 | inst.plugin(hook, callback);
|
---|
| 290 | }
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | function _catch(body, recover) {
|
---|
| 294 | try {
|
---|
| 295 | var result = body();
|
---|
| 296 | } catch (e) {
|
---|
| 297 | return recover(e);
|
---|
| 298 | }
|
---|
| 299 |
|
---|
| 300 | if (result && result.then) {
|
---|
| 301 | return result.then(void 0, recover);
|
---|
| 302 | }
|
---|
| 303 |
|
---|
| 304 | return result;
|
---|
| 305 | }
|
---|
| 306 |
|
---|
| 307 | var Critters = /* #__PURE__ */ (function () {
|
---|
| 308 | function Critters(options) {
|
---|
| 309 | this.options = Object.assign(
|
---|
| 310 | {
|
---|
| 311 | logLevel: 'info',
|
---|
| 312 | externalStylesheets: [],
|
---|
| 313 | path: '',
|
---|
| 314 | publicPath: '',
|
---|
| 315 | preload: 'media',
|
---|
| 316 | reduceInlineStyles: false, // compress: false,
|
---|
| 317 | },
|
---|
| 318 | options || {}
|
---|
| 319 | );
|
---|
| 320 | this.options.pruneSource = this.options.pruneSource !== false;
|
---|
| 321 | this.urlFilter = this.options.filter;
|
---|
| 322 |
|
---|
| 323 | if (this.urlFilter instanceof RegExp) {
|
---|
| 324 | this.urlFilter = this.urlFilter.test.bind(this.urlFilter);
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 | this.logger = {
|
---|
| 328 | warn: console.log,
|
---|
| 329 | info: console.log,
|
---|
| 330 | };
|
---|
| 331 | }
|
---|
| 332 | /**
|
---|
| 333 | * Read the contents of a file from Webpack's input filesystem
|
---|
| 334 | */
|
---|
| 335 |
|
---|
| 336 | var _proto = Critters.prototype;
|
---|
| 337 |
|
---|
| 338 | _proto.readFile = function readFile(filename) {
|
---|
| 339 | var fs = this.fs; // || compilation.outputFileSystem;
|
---|
| 340 |
|
---|
| 341 | return new Promise(function (resolve, reject) {
|
---|
| 342 | var callback = function callback(err, data) {
|
---|
| 343 | if (err) reject(err);
|
---|
| 344 | else resolve(data);
|
---|
| 345 | };
|
---|
| 346 |
|
---|
| 347 | if (fs && fs.readFile) {
|
---|
| 348 | fs.readFile(filename, callback);
|
---|
| 349 | } else {
|
---|
| 350 | require('fs').readFile(filename, 'utf8', callback);
|
---|
| 351 | }
|
---|
| 352 | });
|
---|
| 353 | };
|
---|
| 354 |
|
---|
| 355 | _proto.process = (function (_process) {
|
---|
| 356 | function process(_x) {
|
---|
| 357 | return _process.apply(this, arguments);
|
---|
| 358 | }
|
---|
| 359 |
|
---|
| 360 | process.toString = function () {
|
---|
| 361 | return _process.toString();
|
---|
| 362 | };
|
---|
| 363 |
|
---|
| 364 | return process;
|
---|
| 365 | })(function (html) {
|
---|
| 366 | try {
|
---|
| 367 | var _temp5 = function _temp5() {
|
---|
| 368 | // console.log('2 ', serializeDocument(document));
|
---|
| 369 | // go through all the style tags in the document and reduce them to only critical CSS
|
---|
| 370 | var styles = _this2.getAffectedStyleTags(document);
|
---|
| 371 |
|
---|
| 372 | return Promise.resolve(
|
---|
| 373 | Promise.all(
|
---|
| 374 | styles.map(function (style) {
|
---|
| 375 | return _this2.processStyle(style, document);
|
---|
| 376 | })
|
---|
| 377 | )
|
---|
| 378 | ).then(function () {
|
---|
| 379 | function _temp2() {
|
---|
| 380 | // serialize the document back to HTML and we're done
|
---|
| 381 | var output = serializeDocument(document);
|
---|
| 382 | // var end = process.hrtime.bigint();
|
---|
| 383 | // console.log('Time ' + parseFloat(end - start) / 1000000.0);
|
---|
| 384 | console.timeEnd('critters');
|
---|
| 385 |
|
---|
| 386 | return output;
|
---|
| 387 | }
|
---|
| 388 |
|
---|
| 389 | var _temp = (function () {
|
---|
| 390 | if (
|
---|
| 391 | _this2.options.mergeStylesheets !== false &&
|
---|
| 392 | styles.length !== 0
|
---|
| 393 | ) {
|
---|
| 394 | return Promise.resolve(
|
---|
| 395 | _this2.mergeStylesheets(document)
|
---|
| 396 | ).then(function () {});
|
---|
| 397 | }
|
---|
| 398 | })();
|
---|
| 399 |
|
---|
| 400 | return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
|
---|
| 401 | });
|
---|
| 402 | };
|
---|
| 403 |
|
---|
| 404 | var _this2 = this;
|
---|
| 405 |
|
---|
| 406 | var outputPath = _this2.options.path;
|
---|
| 407 | var publicPath = _this2.options.publicPath;
|
---|
| 408 | // var start = process.hrtime.bigint();
|
---|
| 409 | console.time('critters');
|
---|
| 410 | // Parse the generated HTML in a DOM we can mutate
|
---|
| 411 |
|
---|
| 412 | var document = createDocument(html); // if (this.options.additionalStylesheets) {
|
---|
| 413 | // const styleSheetsIncluded = [];
|
---|
| 414 | // (this.options.additionalStylesheets || []).forEach((cssFile) => {
|
---|
| 415 | // if (styleSheetsIncluded.includes(cssFile)) {
|
---|
| 416 | // return;
|
---|
| 417 | // }
|
---|
| 418 | // styleSheetsIncluded.push(cssFile);
|
---|
| 419 | // const webpackCssAssets = Object.keys(
|
---|
| 420 | // compilation.assets
|
---|
| 421 | // ).filter((file) => minimatch(file, cssFile));
|
---|
| 422 | // webpackCssAssets.map((asset) => {
|
---|
| 423 | // const tag = document.createElement('style');
|
---|
| 424 | // tag.innerHTML = compilation.assets[asset].source();
|
---|
| 425 | // document.head.appendChild(tag);
|
---|
| 426 | // });
|
---|
| 427 | // });
|
---|
| 428 | // }
|
---|
| 429 | // `external:false` skips processing of external sheets
|
---|
| 430 | // console.log('1 ', serializeDocument(document));
|
---|
| 431 |
|
---|
| 432 | var _temp6 = (function () {
|
---|
| 433 | if (_this2.options.external !== false) {
|
---|
| 434 | var externalSheets = [].slice.call(
|
---|
| 435 | document.querySelectorAll('link[rel="stylesheet"]')
|
---|
| 436 | );
|
---|
| 437 | return Promise.resolve(
|
---|
| 438 | Promise.all(
|
---|
| 439 | externalSheets.map(function (link) {
|
---|
| 440 | return _this2.embedLinkedStylesheet(
|
---|
| 441 | link,
|
---|
| 442 | outputPath,
|
---|
| 443 | publicPath
|
---|
| 444 | );
|
---|
| 445 | })
|
---|
| 446 | )
|
---|
| 447 | ).then(function () {});
|
---|
| 448 | }
|
---|
| 449 | })();
|
---|
| 450 |
|
---|
| 451 | return Promise.resolve(
|
---|
| 452 | _temp6 && _temp6.then ? _temp6.then(_temp5) : _temp5(_temp6)
|
---|
| 453 | );
|
---|
| 454 | } catch (e) {
|
---|
| 455 | return Promise.reject(e);
|
---|
| 456 | }
|
---|
| 457 | });
|
---|
| 458 |
|
---|
| 459 | _proto.getAffectedStyleTags = function getAffectedStyleTags(document) {
|
---|
| 460 | var styles = [].slice.call(document.querySelectorAll('style')); // `inline:false` skips processing of inline stylesheets
|
---|
| 461 |
|
---|
| 462 | if (this.options.reduceInlineStyles === false) {
|
---|
| 463 | return styles.filter(function (style) {
|
---|
| 464 | return style.$$external;
|
---|
| 465 | });
|
---|
| 466 | }
|
---|
| 467 |
|
---|
| 468 | return styles;
|
---|
| 469 | };
|
---|
| 470 |
|
---|
| 471 | _proto.mergeStylesheets = function mergeStylesheets(document) {
|
---|
| 472 | try {
|
---|
| 473 | var _temp9 = function _temp9() {
|
---|
| 474 | setNodeText(first, sheet);
|
---|
| 475 | };
|
---|
| 476 |
|
---|
| 477 | var _this4 = this;
|
---|
| 478 |
|
---|
| 479 | var styles = _this4.getAffectedStyleTags(document);
|
---|
| 480 |
|
---|
| 481 | if (styles.length === 0) {
|
---|
| 482 | // this.logger.warn('Merging inline stylesheets into a single <style> tag skipped, no inline stylesheets to merge');
|
---|
| 483 | return Promise.resolve();
|
---|
| 484 | }
|
---|
| 485 |
|
---|
| 486 | var first = styles[0];
|
---|
| 487 | var sheet = first.textContent;
|
---|
| 488 |
|
---|
| 489 | for (var i = 1; i < styles.length; i++) {
|
---|
| 490 | var node = styles[i];
|
---|
| 491 | sheet += node.textContent;
|
---|
| 492 | node.remove();
|
---|
| 493 | }
|
---|
| 494 |
|
---|
| 495 | var _temp10 = (function () {
|
---|
| 496 | if (_this4.options.compress !== false) {
|
---|
| 497 | var before = sheet;
|
---|
| 498 | // var processor = postcss([cssnano()]);
|
---|
| 499 | return Promise.resolve(
|
---|
| 500 | // processor.process(before, {
|
---|
| 501 | // from: undefined,
|
---|
| 502 | // })
|
---|
| 503 | before
|
---|
| 504 | ).then(function (result) {
|
---|
| 505 | // @todo sourcemap support (elsewhere first)
|
---|
| 506 | sheet = result.css;
|
---|
| 507 | });
|
---|
| 508 | }
|
---|
| 509 | })();
|
---|
| 510 |
|
---|
| 511 | return Promise.resolve(_temp9(_temp10));
|
---|
| 512 | } catch (e) {
|
---|
| 513 | return Promise.reject(e);
|
---|
| 514 | }
|
---|
| 515 | };
|
---|
| 516 |
|
---|
| 517 | _proto.embedLinkedStylesheet = function embedLinkedStylesheet(
|
---|
| 518 | link,
|
---|
| 519 | outputPath,
|
---|
| 520 | publicPath
|
---|
| 521 | ) {
|
---|
| 522 | try {
|
---|
| 523 | var _temp14 = function _temp14(_result2) {
|
---|
| 524 | if (_exit2) return _result2;
|
---|
| 525 | // CSS loader is only injected for the first sheet, then this becomes an empty string
|
---|
| 526 | var cssLoaderPreamble =
|
---|
| 527 | "function $loadcss(u,m,l){(l=document.createElement('link')).rel='stylesheet';l.href=u;document.head.appendChild(l)}";
|
---|
| 528 | var lazy = preloadMode === 'js-lazy';
|
---|
| 529 |
|
---|
| 530 | if (lazy) {
|
---|
| 531 | cssLoaderPreamble = cssLoaderPreamble.replace(
|
---|
| 532 | 'l.href',
|
---|
| 533 | "l.media='print';l.onload=function(){l.media=m};l.href"
|
---|
| 534 | );
|
---|
| 535 | } // console.log('sheet ', sheet);
|
---|
| 536 | // the reduced critical CSS gets injected into a new <style> tag
|
---|
| 537 |
|
---|
| 538 | console.log('here1');
|
---|
| 539 | var style = document.createElement('style');
|
---|
| 540 | style.$$external = true;
|
---|
| 541 | style.appendChild(document.createTextNode(sheet)); // style.innerHTML = sheet;
|
---|
| 542 |
|
---|
| 543 | link.parentNode.insertBefore(style, link);
|
---|
| 544 | console.log('here2');
|
---|
| 545 |
|
---|
| 546 | if (
|
---|
| 547 | _this6.options.inlineThreshold &&
|
---|
| 548 | sheet.length < _this6.options.inlineThreshold
|
---|
| 549 | ) {
|
---|
| 550 | style.$$reduce = false;
|
---|
| 551 |
|
---|
| 552 | _this6.logger.info(
|
---|
| 553 | '\x1B[32mInlined all of ' +
|
---|
| 554 | href +
|
---|
| 555 | ' (' +
|
---|
| 556 | sheet.length +
|
---|
| 557 | ' was below the threshold of ' +
|
---|
| 558 | _this6.options.inlineThreshold +
|
---|
| 559 | ')\x1B[39m'
|
---|
| 560 | ); // if (asset) {
|
---|
| 561 | // delete compilation.assets[relativePath];
|
---|
| 562 | // } else {
|
---|
| 563 | // this.logger.warn(
|
---|
| 564 | // ` > ${href} was not found in assets. the resource may still be emitted but will be unreferenced.`
|
---|
| 565 | // );
|
---|
| 566 | // }
|
---|
| 567 |
|
---|
| 568 | link.parentNode.removeChild(link);
|
---|
| 569 | return;
|
---|
| 570 | } // drop references to webpack asset locations onto the tag, used for later reporting and in-place asset updates
|
---|
| 571 |
|
---|
| 572 | style.$$name = href;
|
---|
| 573 | style.$$asset = asset;
|
---|
| 574 | style.$$assetName = relativePath; // style.$$assets = compilation.assets;
|
---|
| 575 |
|
---|
| 576 | style.$$links = [link]; // Allow disabling any mutation of the stylesheet link:
|
---|
| 577 |
|
---|
| 578 | if (preloadMode === false) return;
|
---|
| 579 | var noscriptFallback = false;
|
---|
| 580 |
|
---|
| 581 | if (preloadMode === 'body') {
|
---|
| 582 | document.body.appendChild(link);
|
---|
| 583 | } else {
|
---|
| 584 | link.setAttribute('rel', 'preload');
|
---|
| 585 | link.setAttribute('as', 'style');
|
---|
| 586 |
|
---|
| 587 | if (preloadMode === 'js' || preloadMode === 'js-lazy') {
|
---|
| 588 | var script = document.createElement('script');
|
---|
| 589 | var js =
|
---|
| 590 | cssLoaderPreamble +
|
---|
| 591 | '$loadcss(' +
|
---|
| 592 | JSON.stringify(href) +
|
---|
| 593 | (lazy ? ',' + JSON.stringify(media || 'all') : '') +
|
---|
| 594 | ')';
|
---|
| 595 | script.appendChild(document.createTextNode(js));
|
---|
| 596 | link.parentNode.insertBefore(script, link.nextSibling);
|
---|
| 597 | style.$$links.push(script);
|
---|
| 598 | cssLoaderPreamble = '';
|
---|
| 599 | noscriptFallback = true;
|
---|
| 600 | } else if (preloadMode === 'media') {
|
---|
| 601 | // @see https://github.com/filamentgroup/loadCSS/blob/af1106cfe0bf70147e22185afa7ead96c01dec48/src/loadCSS.js#L26
|
---|
| 602 | link.setAttribute('rel', 'stylesheet');
|
---|
| 603 | link.removeAttribute('as');
|
---|
| 604 | link.setAttribute('media', 'print');
|
---|
| 605 | link.setAttribute(
|
---|
| 606 | 'onload',
|
---|
| 607 | "this.media='" + (media || 'all') + "'"
|
---|
| 608 | );
|
---|
| 609 | noscriptFallback = true;
|
---|
| 610 | } else if (preloadMode === 'swap') {
|
---|
| 611 | link.setAttribute('onload', "this.rel='stylesheet'");
|
---|
| 612 | noscriptFallback = true;
|
---|
| 613 | } else {
|
---|
| 614 | var bodyLink = document.createElement('link');
|
---|
| 615 | bodyLink.setAttribute('rel', 'stylesheet');
|
---|
| 616 | if (media) bodyLink.setAttribute('media', media);
|
---|
| 617 | bodyLink.setAttribute('href', href);
|
---|
| 618 | document.body.appendChild(bodyLink);
|
---|
| 619 | style.$$links.push(bodyLink);
|
---|
| 620 | }
|
---|
| 621 | }
|
---|
| 622 |
|
---|
| 623 | if (_this6.options.noscriptFallback !== false && noscriptFallback) {
|
---|
| 624 | var noscript = document.createElement('noscript');
|
---|
| 625 | var noscriptLink = document.createElement('link');
|
---|
| 626 | noscriptLink.setAttribute('rel', 'stylesheet');
|
---|
| 627 | noscriptLink.setAttribute('href', href);
|
---|
| 628 | if (media) noscriptLink.setAttribute('media', media);
|
---|
| 629 | noscript.appendChild(noscriptLink);
|
---|
| 630 | link.parentNode.insertBefore(noscript, link.nextSibling);
|
---|
| 631 | style.$$links.push(noscript);
|
---|
| 632 | }
|
---|
| 633 | };
|
---|
| 634 |
|
---|
| 635 | var _exit2;
|
---|
| 636 |
|
---|
| 637 | var _this6 = this;
|
---|
| 638 |
|
---|
| 639 | var href = link.getAttribute('href');
|
---|
| 640 | var media = link.getAttribute('media');
|
---|
| 641 | var document = link.ownerDocument;
|
---|
| 642 | var preloadMode = _this6.options.preload; // skip filtered resources, or network resources if no filter is provided
|
---|
| 643 | // if (this.urlFilter ? this.urlFilter(href) : href.match(/^(https?:)?\/\//)) {
|
---|
| 644 |
|
---|
| 645 | if (
|
---|
| 646 | _this6.urlFilter
|
---|
| 647 | ? _this6.urlFilter(href)
|
---|
| 648 | : !(href || '').match(/\.css$/)
|
---|
| 649 | ) {
|
---|
| 650 | return Promise.resolve();
|
---|
| 651 | } // CHECK - the output path
|
---|
| 652 | // path on disk (with output.publicPath removed)
|
---|
| 653 |
|
---|
| 654 | var normalizedPath = href.replace(/^\//, '');
|
---|
| 655 | var pathPrefix = (publicPath || '').replace(/(^\/|\/$)/g, '') + '/';
|
---|
| 656 |
|
---|
| 657 | if (normalizedPath.indexOf(pathPrefix) === 0) {
|
---|
| 658 | normalizedPath = normalizedPath
|
---|
| 659 | .substring(pathPrefix.length)
|
---|
| 660 | .replace(/^\//, '');
|
---|
| 661 | }
|
---|
| 662 |
|
---|
| 663 | var filename = path.resolve(outputPath, normalizedPath); // try to find a matching asset by filename in webpack's output (not yet written to disk)
|
---|
| 664 |
|
---|
| 665 | var relativePath = path
|
---|
| 666 | .relative(outputPath, filename)
|
---|
| 667 | .replace(/^\.\//, '');
|
---|
| 668 | var asset = ''; // compilation.assets[relativePath];
|
---|
| 669 |
|
---|
| 670 | console.log('filename ', filename); // Attempt to read from assets, falling back to a disk read
|
---|
| 671 |
|
---|
| 672 | var sheet = asset && asset.source();
|
---|
| 673 |
|
---|
| 674 | var _temp15 = (function () {
|
---|
| 675 | if (!sheet) {
|
---|
| 676 | var _temp16 = _catch(
|
---|
| 677 | function () {
|
---|
| 678 | return Promise.resolve(_this6.readFile(filename)).then(function (
|
---|
| 679 | _this5$readFile
|
---|
| 680 | ) {
|
---|
| 681 | sheet = _this5$readFile;
|
---|
| 682 | }); // this.logger.warn(
|
---|
| 683 | // `Stylesheet "${relativePath}" not found in assets, but a file was located on disk.${
|
---|
| 684 | // this.options.pruneSource
|
---|
| 685 | // ? ' This means pruneSource will not be applied.'
|
---|
| 686 | // : ''
|
---|
| 687 | // }`
|
---|
| 688 | // );
|
---|
| 689 | },
|
---|
| 690 | function (e) {
|
---|
| 691 | console.log(e); // this.logger.warn(`Unable to locate stylesheet: ${relativePath}`);
|
---|
| 692 |
|
---|
| 693 | _exit2 = 1;
|
---|
| 694 | }
|
---|
| 695 | );
|
---|
| 696 |
|
---|
| 697 | if (_temp16 && _temp16.then) return _temp16.then(function () {});
|
---|
| 698 | }
|
---|
| 699 | })();
|
---|
| 700 |
|
---|
| 701 | return Promise.resolve(
|
---|
| 702 | _temp15 && _temp15.then ? _temp15.then(_temp14) : _temp14(_temp15)
|
---|
| 703 | );
|
---|
| 704 | } catch (e) {
|
---|
| 705 | return Promise.reject(e);
|
---|
| 706 | }
|
---|
| 707 | };
|
---|
| 708 | /**
|
---|
| 709 | * Parse the stylesheet within a <style> element, then reduce it to contain only rules used by the document.
|
---|
| 710 | */
|
---|
| 711 |
|
---|
| 712 | _proto.processStyle = function processStyle(style, document) {
|
---|
| 713 | try {
|
---|
| 714 | var _this8 = this;
|
---|
| 715 |
|
---|
| 716 | if (style.$$reduce === false) return Promise.resolve();
|
---|
| 717 | var name = style.$$name ? style.$$name.replace(/^\//, '') : 'inline CSS';
|
---|
| 718 | var options = _this8.options; // const document = style.ownerDocument;
|
---|
| 719 |
|
---|
| 720 | var head = document.querySelector('head');
|
---|
| 721 | var keyframesMode = options.keyframes || 'critical'; // we also accept a boolean value for options.keyframes
|
---|
| 722 |
|
---|
| 723 | if (keyframesMode === true) keyframesMode = 'all';
|
---|
| 724 | if (keyframesMode === false) keyframesMode = 'none'; // basically `.textContent`
|
---|
| 725 |
|
---|
| 726 | var sheet =
|
---|
| 727 | style.childNodes.length > 0 &&
|
---|
| 728 | [].map
|
---|
| 729 | .call(style.childNodes, function (node) {
|
---|
| 730 | return node.nodeValue;
|
---|
| 731 | })
|
---|
| 732 | .join('\n'); // store a reference to the previous serialized stylesheet for reporting stats
|
---|
| 733 |
|
---|
| 734 | var before = sheet; // Skip empty stylesheets
|
---|
| 735 |
|
---|
| 736 | if (!sheet) return Promise.resolve();
|
---|
| 737 | var ast = parseStylesheet(sheet);
|
---|
| 738 | var astInverse = options.pruneSource ? parseStylesheet(sheet) : null; // a string to search for font names (very loose)
|
---|
| 739 |
|
---|
| 740 | var criticalFonts = '';
|
---|
| 741 | var failedSelectors = [];
|
---|
| 742 | var criticalKeyframeNames = []; // Walk all CSS rules, marking unused rules with `.$$remove=true` for removal in the second pass.
|
---|
| 743 | // This first pass is also used to collect font and keyframe usage used in the second pass.
|
---|
| 744 |
|
---|
| 745 | walkStyleRules(
|
---|
| 746 | ast,
|
---|
| 747 | markOnly(function (rule) {
|
---|
| 748 | if (rule.type === 'rule') {
|
---|
| 749 | // Filter the selector list down to only those match
|
---|
| 750 | rule.filterSelectors(function (sel) {
|
---|
| 751 | // Strip pseudo-elements and pseudo-classes, since we only care that their associated elements exist.
|
---|
| 752 | // This means any selector for a pseudo-element or having a pseudo-class will be inlined if the rest of the selector matches.
|
---|
| 753 | if (sel !== ':root') {
|
---|
| 754 | sel = sel
|
---|
| 755 | .replace(/(?:>\s*)?::?[a-z-]+\s*(\{|$)/gi, '$1')
|
---|
| 756 | .trim();
|
---|
| 757 | }
|
---|
| 758 |
|
---|
| 759 | if (!sel) return false;
|
---|
| 760 |
|
---|
| 761 | try {
|
---|
| 762 | return document.querySelector(sel) != null;
|
---|
| 763 | } catch (e) {
|
---|
| 764 | failedSelectors.push(sel + ' -> ' + e.message);
|
---|
| 765 | return false;
|
---|
| 766 | }
|
---|
| 767 | }); // If there are no matched selectors, remove the rule:
|
---|
| 768 |
|
---|
| 769 | if (rule.selectors.length === 0) {
|
---|
| 770 | return false;
|
---|
| 771 | }
|
---|
| 772 |
|
---|
| 773 | if (rule.declarations) {
|
---|
| 774 | for (var i = 0; i < rule.declarations.length; i++) {
|
---|
| 775 | var decl = rule.declarations[i]; // detect used fonts
|
---|
| 776 |
|
---|
| 777 | if (
|
---|
| 778 | decl.property &&
|
---|
| 779 | decl.property.match(/\bfont(-family)?\b/i)
|
---|
| 780 | ) {
|
---|
| 781 | criticalFonts += ' ' + decl.value;
|
---|
| 782 | } // detect used keyframes
|
---|
| 783 |
|
---|
| 784 | if (
|
---|
| 785 | decl.property === 'animation' ||
|
---|
| 786 | decl.property === 'animation-name'
|
---|
| 787 | ) {
|
---|
| 788 | // @todo: parse animation declarations and extract only the name. for now we'll do a lazy match.
|
---|
| 789 | var names = decl.value.split(/\s+/);
|
---|
| 790 |
|
---|
| 791 | for (var j = 0; j < names.length; j++) {
|
---|
| 792 | var _name = names[j].trim();
|
---|
| 793 |
|
---|
| 794 | if (_name) criticalKeyframeNames.push(_name);
|
---|
| 795 | }
|
---|
| 796 | }
|
---|
| 797 | }
|
---|
| 798 | }
|
---|
| 799 | } // keep font rules, they're handled in the second pass:
|
---|
| 800 |
|
---|
| 801 | if (rule.type === 'font-face') return; // If there are no remaining rules, remove the whole rule:
|
---|
| 802 |
|
---|
| 803 | var rules =
|
---|
| 804 | rule.rules &&
|
---|
| 805 | rule.rules.filter(function (rule) {
|
---|
| 806 | return !rule.$$remove;
|
---|
| 807 | });
|
---|
| 808 | return !rules || rules.length !== 0;
|
---|
| 809 | })
|
---|
| 810 | );
|
---|
| 811 |
|
---|
| 812 | if (failedSelectors.length !== 0) {
|
---|
| 813 | _this8.logger.warn(
|
---|
| 814 | failedSelectors.length +
|
---|
| 815 | ' rules skipped due to selector errors:\n ' +
|
---|
| 816 | failedSelectors.join('\n ')
|
---|
| 817 | );
|
---|
| 818 | }
|
---|
| 819 |
|
---|
| 820 | var shouldPreloadFonts =
|
---|
| 821 | options.fonts === true || options.preloadFonts === true;
|
---|
| 822 | var shouldInlineFonts =
|
---|
| 823 | options.fonts !== false && options.inlineFonts === true;
|
---|
| 824 | var preloadedFonts = []; // Second pass, using data picked up from the first
|
---|
| 825 |
|
---|
| 826 | walkStyleRulesWithReverseMirror(ast, astInverse, function (rule) {
|
---|
| 827 | // remove any rules marked in the first pass
|
---|
| 828 | if (rule.$$remove === true) return false;
|
---|
| 829 | applyMarkedSelectors(rule); // prune @keyframes rules
|
---|
| 830 |
|
---|
| 831 | if (rule.type === 'keyframes') {
|
---|
| 832 | if (keyframesMode === 'none') return false;
|
---|
| 833 | if (keyframesMode === 'all') return true;
|
---|
| 834 | return criticalKeyframeNames.indexOf(rule.name) !== -1;
|
---|
| 835 | } // prune @font-face rules
|
---|
| 836 |
|
---|
| 837 | if (rule.type === 'font-face') {
|
---|
| 838 | var family, src;
|
---|
| 839 |
|
---|
| 840 | for (var i = 0; i < rule.declarations.length; i++) {
|
---|
| 841 | var decl = rule.declarations[i];
|
---|
| 842 |
|
---|
| 843 | if (decl.property === 'src') {
|
---|
| 844 | // @todo parse this properly and generate multiple preloads with type="font/woff2" etc
|
---|
| 845 | src = (decl.value.match(/url\s*\(\s*(['"]?)(.+?)\1\s*\)/) ||
|
---|
| 846 | [])[2];
|
---|
| 847 | } else if (decl.property === 'font-family') {
|
---|
| 848 | family = decl.value;
|
---|
| 849 | }
|
---|
| 850 | }
|
---|
| 851 |
|
---|
| 852 | if (src && shouldPreloadFonts && preloadedFonts.indexOf(src) === -1) {
|
---|
| 853 | preloadedFonts.push(src);
|
---|
| 854 | var preload = document.createElement('link');
|
---|
| 855 | preload.setAttribute('rel', 'preload');
|
---|
| 856 | preload.setAttribute('as', 'font');
|
---|
| 857 | preload.setAttribute('crossorigin', 'anonymous');
|
---|
| 858 | preload.setAttribute('href', src.trim());
|
---|
| 859 | head.appendChild(preload);
|
---|
| 860 | } // if we're missing info, if the font is unused, or if critical font inlining is disabled, remove the rule:
|
---|
| 861 |
|
---|
| 862 | if (
|
---|
| 863 | !family ||
|
---|
| 864 | !src ||
|
---|
| 865 | criticalFonts.indexOf(family) === -1 ||
|
---|
| 866 | !shouldInlineFonts
|
---|
| 867 | ) {
|
---|
| 868 | return false;
|
---|
| 869 | }
|
---|
| 870 | }
|
---|
| 871 | });
|
---|
| 872 | sheet = serializeStylesheet(ast, {
|
---|
| 873 | compress: _this8.options.compress !== false,
|
---|
| 874 | }).trim(); // If all rules were removed, get rid of the style element entirely
|
---|
| 875 |
|
---|
| 876 | if (sheet.trim().length === 0) {
|
---|
| 877 | if (style.parentNode) {
|
---|
| 878 | style.parentNode.removeChild(style);
|
---|
| 879 | }
|
---|
| 880 |
|
---|
| 881 | return Promise.resolve();
|
---|
| 882 | }
|
---|
| 883 |
|
---|
| 884 | var afterText = '';
|
---|
| 885 |
|
---|
| 886 | if (options.pruneSource) {
|
---|
| 887 | var sheetInverse = serializeStylesheet(astInverse, {
|
---|
| 888 | compress: _this8.options.compress !== false,
|
---|
| 889 | }); // const asset = style.$$asset;
|
---|
| 890 | // if (asset) {
|
---|
| 891 | // if external stylesheet would be below minimum size, just inline everything
|
---|
| 892 |
|
---|
| 893 | var minSize = _this8.options.minimumExternalSize;
|
---|
| 894 |
|
---|
| 895 | if (minSize && sheetInverse.length < minSize) {
|
---|
| 896 | _this8.logger.info(
|
---|
| 897 | '\x1B[32mInlined all of ' +
|
---|
| 898 | name +
|
---|
| 899 | ' (non-critical external stylesheet would have been ' +
|
---|
| 900 | sheetInverse.length +
|
---|
| 901 | 'b, which was below the threshold of ' +
|
---|
| 902 | minSize +
|
---|
| 903 | ')\x1B[39m'
|
---|
| 904 | );
|
---|
| 905 |
|
---|
| 906 | setNodeText(style, before); // remove any associated external resources/loaders:
|
---|
| 907 |
|
---|
| 908 | if (style.$$links) {
|
---|
| 909 | for (
|
---|
| 910 | var _iterator = _createForOfIteratorHelperLoose(style.$$links),
|
---|
| 911 | _step;
|
---|
| 912 | !(_step = _iterator()).done;
|
---|
| 913 |
|
---|
| 914 | ) {
|
---|
| 915 | var link = _step.value;
|
---|
| 916 | var parent = link.parentNode;
|
---|
| 917 | if (parent) parent.removeChild(link);
|
---|
| 918 | }
|
---|
| 919 | } // delete the webpack asset:
|
---|
| 920 |
|
---|
| 921 | delete style.$$assets[style.$$assetName];
|
---|
| 922 | return Promise.resolve();
|
---|
| 923 | }
|
---|
| 924 |
|
---|
| 925 | var percent = (sheetInverse.length / before.length) * 100;
|
---|
| 926 | afterText =
|
---|
| 927 | ', reducing non-inlined size ' +
|
---|
| 928 | (percent | 0) +
|
---|
| 929 | '% to ' +
|
---|
| 930 | prettyBytes(sheetInverse.length); // style.$$assets[style.$$assetName] = new sources.LineToLineMappedSource(
|
---|
| 931 | // sheetInverse,
|
---|
| 932 | // style.$$assetName,
|
---|
| 933 | // before
|
---|
| 934 | // );
|
---|
| 935 | // } else {
|
---|
| 936 | // this.logger.warn(
|
---|
| 937 | // 'pruneSource is enabled, but a style (' +
|
---|
| 938 | // name +
|
---|
| 939 | // ') has no corresponding Webpack asset.'
|
---|
| 940 | // );
|
---|
| 941 | // }
|
---|
| 942 | // }
|
---|
| 943 | // replace the inline stylesheet with its critical'd counterpart
|
---|
| 944 |
|
---|
| 945 | setNodeText(style, sheet); // output stats
|
---|
| 946 | // const percent = ((sheet.length / before.length) * 100) | 0;
|
---|
| 947 | // this.logger.info(
|
---|
| 948 | // '\u001b[32mInlined ' +
|
---|
| 949 | // prettyBytes(sheet.length) +
|
---|
| 950 | // ' (' +
|
---|
| 951 | // percent +
|
---|
| 952 | // '% of original ' +
|
---|
| 953 | // prettyBytes(before.length) +
|
---|
| 954 | // ') of ' +
|
---|
| 955 | // name +
|
---|
| 956 | // afterText +
|
---|
| 957 | // '.\u001b[39m'
|
---|
| 958 | // );
|
---|
| 959 | }
|
---|
| 960 |
|
---|
| 961 | return Promise.resolve();
|
---|
| 962 | } catch (e) {
|
---|
| 963 | return Promise.reject(e);
|
---|
| 964 | }
|
---|
| 965 | };
|
---|
| 966 |
|
---|
| 967 | return Critters;
|
---|
| 968 | })(); // Original code picks 1 html and during prune removes CSS files from assets. What if theres another dependency
|
---|
| 969 |
|
---|
| 970 | function _catch$1(body, recover) {
|
---|
| 971 | try {
|
---|
| 972 | var result = body();
|
---|
| 973 | } catch (e) {
|
---|
| 974 | return recover(e);
|
---|
| 975 | }
|
---|
| 976 |
|
---|
| 977 | if (result && result.then) {
|
---|
| 978 | return result.then(void 0, recover);
|
---|
| 979 | }
|
---|
| 980 |
|
---|
| 981 | return result;
|
---|
| 982 | }
|
---|
| 983 |
|
---|
| 984 | var PLUGIN_NAME = 'critters-webpack-plugin';
|
---|
| 985 | /**
|
---|
| 986 | * The mechanism to use for lazy-loading stylesheets.
|
---|
| 987 | * _[JS]_ indicates that a strategy requires JavaScript (falls back to `<noscript>`).
|
---|
| 988 | *
|
---|
| 989 | * - **default:** Move stylesheet links to the end of the document and insert preload meta tags in their place.
|
---|
| 990 | * - **"body":** Move all external stylesheet links to the end of the document.
|
---|
| 991 | * - **"media":** Load stylesheets asynchronously by adding `media="not x"` and removing once loaded. _[JS]_
|
---|
| 992 | * - **"swap":** Convert stylesheet links to preloads that swap to `rel="stylesheet"` once loaded. _[JS]_
|
---|
| 993 | * - **"js":** Inject an asynchronous CSS loader similar to [LoadCSS](https://github.com/filamentgroup/loadCSS) and use it to load stylesheets. _[JS]_
|
---|
| 994 | * - **"js-lazy":** Like `"js"`, but the stylesheet is disabled until fully loaded.
|
---|
| 995 | * @typedef {(default|'body'|'media'|'swap'|'js'|'js-lazy')} PreloadStrategy
|
---|
| 996 | * @public
|
---|
| 997 | */
|
---|
| 998 |
|
---|
| 999 | /**
|
---|
| 1000 | * Controls which keyframes rules are inlined.
|
---|
| 1001 | *
|
---|
| 1002 | * - **"critical":** _(default)_ inline keyframes rules that are used by the critical CSS.
|
---|
| 1003 | * - **"all":** Inline all keyframes rules.
|
---|
| 1004 | * - **"none":** Remove all keyframes rules.
|
---|
| 1005 | * @typedef {('critical'|'all'|'none')} KeyframeStrategy
|
---|
| 1006 | * @private
|
---|
| 1007 | * @property {String} keyframes Which {@link KeyframeStrategy keyframe strategy} to use (default: `critical`)_
|
---|
| 1008 | */
|
---|
| 1009 |
|
---|
| 1010 | /**
|
---|
| 1011 | * Controls log level of the plugin. Specifies the level the logger should use. A logger will
|
---|
| 1012 | * not produce output for any log level beneath the specified level. Available levels and order
|
---|
| 1013 | * are:
|
---|
| 1014 | *
|
---|
| 1015 | * - **"info"** _(default)_
|
---|
| 1016 | * - **"warn"**
|
---|
| 1017 | * - **"error"**
|
---|
| 1018 | * - **"trace"**
|
---|
| 1019 | * - **"debug"**
|
---|
| 1020 | * - **"silent"**
|
---|
| 1021 | * @typedef {('info'|'warn'|'error'|'trace'|'debug'|'silent')} LogLevel
|
---|
| 1022 | * @public
|
---|
| 1023 | */
|
---|
| 1024 |
|
---|
| 1025 | /**
|
---|
| 1026 | * All optional. Pass them to `new Critters({ ... })`.
|
---|
| 1027 | * @public
|
---|
| 1028 | * @typedef Options
|
---|
| 1029 | * @property {Boolean} external Inline styles from external stylesheets _(default: `true`)_
|
---|
| 1030 | * @property {Number} inlineThreshold Inline external stylesheets smaller than a given size _(default: `0`)_
|
---|
| 1031 | * @property {Number} minimumExternalSize If the non-critical external stylesheet would be below this size, just inline it _(default: `0`)_
|
---|
| 1032 | * @property {Boolean} pruneSource Remove inlined rules from the external stylesheet _(default: `true`)_
|
---|
| 1033 | * @property {Boolean} mergeStylesheets Merged inlined stylesheets into a single <style> tag _(default: `true`)_
|
---|
| 1034 | * @property {String[]} additionalStylesheets Glob for matching other stylesheets to be used while looking for critical CSS _(default: ``)_.
|
---|
| 1035 | * @property {String} preload Which {@link PreloadStrategy preload strategy} to use
|
---|
| 1036 | * @property {Boolean} noscriptFallback Add `<noscript>` fallback to JS-based strategies
|
---|
| 1037 | * @property {Boolean} inlineFonts Inline critical font-face rules _(default: `false`)_
|
---|
| 1038 | * @property {Boolean} preloadFonts Preloads critical fonts _(default: `true`)_
|
---|
| 1039 | * @property {Boolean} fonts Shorthand for setting `inlineFonts`+`preloadFonts`
|
---|
| 1040 | * - Values:
|
---|
| 1041 | * - `true` to inline critical font-face rules and preload the fonts
|
---|
| 1042 | * - `false` to don't inline any font-face rules and don't preload fonts
|
---|
| 1043 | * @property {String} keyframes Controls which keyframes rules are inlined.
|
---|
| 1044 | * - Values:
|
---|
| 1045 | * - `"critical"`: _(default)_ inline keyframes rules used by the critical CSS
|
---|
| 1046 | * - `"all"` inline all keyframes rules
|
---|
| 1047 | * - `"none"` remove all keyframes rules
|
---|
| 1048 | * @property {Boolean} compress Compress resulting critical CSS _(default: `true`)_
|
---|
| 1049 | * @property {String} logLevel Controls {@link LogLevel log level} of the plugin _(default: `"info"`)_
|
---|
| 1050 | */
|
---|
| 1051 |
|
---|
| 1052 | /**
|
---|
| 1053 | * Create a Critters plugin instance with the given options.
|
---|
| 1054 | * @public
|
---|
| 1055 | * @param {Options} options Options to control how Critters inlines CSS.
|
---|
| 1056 | * @example
|
---|
| 1057 | * // webpack.config.js
|
---|
| 1058 | * module.exports = {
|
---|
| 1059 | * plugins: [
|
---|
| 1060 | * new Critters({
|
---|
| 1061 | * // Outputs: <link rel="preload" onload="this.rel='stylesheet'">
|
---|
| 1062 | * preload: 'swap',
|
---|
| 1063 | *
|
---|
| 1064 | * // Don't inline critical font-face rules, but preload the font URLs:
|
---|
| 1065 | * preloadFonts: true
|
---|
| 1066 | * })
|
---|
| 1067 | * ]
|
---|
| 1068 | * }
|
---|
| 1069 | */
|
---|
| 1070 |
|
---|
| 1071 | var Critters$1 = /* #__PURE__ */ (function () {
|
---|
| 1072 | /** @private */
|
---|
| 1073 | function Critters(options) {
|
---|
| 1074 | this.options = Object.assign(
|
---|
| 1075 | {
|
---|
| 1076 | logLevel: 'info',
|
---|
| 1077 | externalStylesheets: [],
|
---|
| 1078 | },
|
---|
| 1079 | options || {}
|
---|
| 1080 | );
|
---|
| 1081 | this.options.pruneSource = this.options.pruneSource !== false;
|
---|
| 1082 | this.urlFilter = this.options.filter;
|
---|
| 1083 |
|
---|
| 1084 | if (this.urlFilter instanceof RegExp) {
|
---|
| 1085 | this.urlFilter = this.urlFilter.test.bind(this.urlFilter);
|
---|
| 1086 | }
|
---|
| 1087 |
|
---|
| 1088 | this.logger = log({
|
---|
| 1089 | name: 'Critters',
|
---|
| 1090 | unique: true,
|
---|
| 1091 | level: this.options.logLevel,
|
---|
| 1092 | });
|
---|
| 1093 | }
|
---|
| 1094 | /**
|
---|
| 1095 | * Invoked by Webpack during plugin initialization
|
---|
| 1096 | */
|
---|
| 1097 |
|
---|
| 1098 | var _proto = Critters.prototype;
|
---|
| 1099 |
|
---|
| 1100 | _proto.apply = function apply(compiler) {
|
---|
| 1101 | var _this = this;
|
---|
| 1102 |
|
---|
| 1103 | // hook into the compiler to get a Compilation instance...
|
---|
| 1104 | tap(compiler, 'compilation', PLUGIN_NAME, false, function (compilation) {
|
---|
| 1105 | // ... which is how we get an "after" hook into html-webpack-plugin's HTML generation.
|
---|
| 1106 | if (
|
---|
| 1107 | compilation.hooks &&
|
---|
| 1108 | compilation.hooks.htmlWebpackPluginAfterHtmlProcessing
|
---|
| 1109 | ) {
|
---|
| 1110 | tap(
|
---|
| 1111 | compilation,
|
---|
| 1112 | 'html-webpack-plugin-after-html-processing',
|
---|
| 1113 | PLUGIN_NAME,
|
---|
| 1114 | true,
|
---|
| 1115 | function (htmlPluginData, callback) {
|
---|
| 1116 | _this
|
---|
| 1117 | .process(compiler, compilation, htmlPluginData.html)
|
---|
| 1118 | .then(function (html) {
|
---|
| 1119 | callback(null, {
|
---|
| 1120 | html: html,
|
---|
| 1121 | });
|
---|
| 1122 | })
|
---|
| 1123 | .catch(callback);
|
---|
| 1124 | }
|
---|
| 1125 | );
|
---|
| 1126 | } else {
|
---|
| 1127 | // If html-webpack-plugin isn't used, process the first HTML asset as an optimize step
|
---|
| 1128 | tap(compilation, 'optimize-assets', PLUGIN_NAME, true, function (
|
---|
| 1129 | assets,
|
---|
| 1130 | callback
|
---|
| 1131 | ) {
|
---|
| 1132 | var htmlAssetName;
|
---|
| 1133 |
|
---|
| 1134 | for (var name in assets) {
|
---|
| 1135 | if (name.match(/\.html$/)) {
|
---|
| 1136 | htmlAssetName = name;
|
---|
| 1137 | break;
|
---|
| 1138 | }
|
---|
| 1139 | }
|
---|
| 1140 |
|
---|
| 1141 | if (!htmlAssetName) {
|
---|
| 1142 | return callback(Error('Could not find HTML asset.'));
|
---|
| 1143 | }
|
---|
| 1144 |
|
---|
| 1145 | var html = assets[htmlAssetName].source();
|
---|
| 1146 | if (!html) return callback(Error('Empty HTML asset.'));
|
---|
| 1147 |
|
---|
| 1148 | _this
|
---|
| 1149 | .process(compiler, compilation, String(html))
|
---|
| 1150 | .then(function (html) {
|
---|
| 1151 | assets[htmlAssetName] = new sources.RawSource(html);
|
---|
| 1152 | callback();
|
---|
| 1153 | })
|
---|
| 1154 | .catch(callback);
|
---|
| 1155 | });
|
---|
| 1156 | }
|
---|
| 1157 | });
|
---|
| 1158 | };
|
---|
| 1159 | /**
|
---|
| 1160 | * Read the contents of a file from Webpack's input filesystem
|
---|
| 1161 | */
|
---|
| 1162 |
|
---|
| 1163 | _proto.readFile = function readFile(compilation, filename) {
|
---|
| 1164 | var fs = this.fs || compilation.outputFileSystem;
|
---|
| 1165 | return new Promise(function (resolve, reject) {
|
---|
| 1166 | var callback = function callback(err, data) {
|
---|
| 1167 | if (err) reject(err);
|
---|
| 1168 | else resolve(data);
|
---|
| 1169 | };
|
---|
| 1170 |
|
---|
| 1171 | if (fs && fs.readFile) {
|
---|
| 1172 | fs.readFile(filename, callback);
|
---|
| 1173 | } else {
|
---|
| 1174 | require('fs').readFile(filename, 'utf8', callback);
|
---|
| 1175 | }
|
---|
| 1176 | });
|
---|
| 1177 | };
|
---|
| 1178 | /**
|
---|
| 1179 | * Apply critical CSS processing to html-webpack-plugin
|
---|
| 1180 | */
|
---|
| 1181 |
|
---|
| 1182 | _proto.process = function process(compiler, compilation, html) {
|
---|
| 1183 | try {
|
---|
| 1184 | var _temp5 = function _temp5() {
|
---|
| 1185 | // go through all the style tags in the document and reduce them to only critical CSS
|
---|
| 1186 | var styles = [].slice.call(document.querySelectorAll('style'));
|
---|
| 1187 | return Promise.resolve(
|
---|
| 1188 | Promise.all(
|
---|
| 1189 | styles.map(function (style) {
|
---|
| 1190 | return _this3.processStyle(style, document);
|
---|
| 1191 | })
|
---|
| 1192 | )
|
---|
| 1193 | ).then(function () {
|
---|
| 1194 | function _temp2() {
|
---|
| 1195 | // serialize the document back to HTML and we're done
|
---|
| 1196 | return serializeDocument(document);
|
---|
| 1197 | }
|
---|
| 1198 |
|
---|
| 1199 | var _temp = (function () {
|
---|
| 1200 | if (
|
---|
| 1201 | _this3.options.mergeStylesheets !== false &&
|
---|
| 1202 | styles.length !== 0
|
---|
| 1203 | ) {
|
---|
| 1204 | return Promise.resolve(
|
---|
| 1205 | _this3.mergeStylesheets(document)
|
---|
| 1206 | ).then(function () {});
|
---|
| 1207 | }
|
---|
| 1208 | })();
|
---|
| 1209 |
|
---|
| 1210 | return _temp && _temp.then ? _temp.then(_temp2) : _temp2(_temp);
|
---|
| 1211 | });
|
---|
| 1212 | };
|
---|
| 1213 |
|
---|
| 1214 | var _this3 = this;
|
---|
| 1215 |
|
---|
| 1216 | var outputPath = compiler.options.output.path;
|
---|
| 1217 | var publicPath = compiler.options.output.publicPath; // Parse the generated HTML in a DOM we can mutate
|
---|
| 1218 |
|
---|
| 1219 | var document = createDocument(html);
|
---|
| 1220 |
|
---|
| 1221 | if (_this3.options.additionalStylesheets) {
|
---|
| 1222 | var styleSheetsIncluded = [];
|
---|
| 1223 | (_this3.options.additionalStylesheets || []).forEach(function (
|
---|
| 1224 | cssFile
|
---|
| 1225 | ) {
|
---|
| 1226 | if (styleSheetsIncluded.includes(cssFile)) {
|
---|
| 1227 | return;
|
---|
| 1228 | }
|
---|
| 1229 |
|
---|
| 1230 | styleSheetsIncluded.push(cssFile);
|
---|
| 1231 | var webpackCssAssets = Object.keys(compilation.assets).filter(
|
---|
| 1232 | function (file) {
|
---|
| 1233 | return minimatch(file, cssFile);
|
---|
| 1234 | }
|
---|
| 1235 | );
|
---|
| 1236 | webpackCssAssets.map(function (asset) {
|
---|
| 1237 | var tag = document.createElement('style');
|
---|
| 1238 | tag.innerHTML = compilation.assets[asset].source();
|
---|
| 1239 | document.head.appendChild(tag);
|
---|
| 1240 | });
|
---|
| 1241 | });
|
---|
| 1242 | } // `external:false` skips processing of external sheets
|
---|
| 1243 |
|
---|
| 1244 | var _temp6 = (function () {
|
---|
| 1245 | if (_this3.options.external !== false) {
|
---|
| 1246 | var externalSheets = [].slice.call(
|
---|
| 1247 | document.querySelectorAll('link[rel="stylesheet"]')
|
---|
| 1248 | );
|
---|
| 1249 | return Promise.resolve(
|
---|
| 1250 | Promise.all(
|
---|
| 1251 | externalSheets.map(function (link) {
|
---|
| 1252 | return _this3.embedLinkedStylesheet(
|
---|
| 1253 | link,
|
---|
| 1254 | compilation,
|
---|
| 1255 | outputPath,
|
---|
| 1256 | publicPath
|
---|
| 1257 | );
|
---|
| 1258 | })
|
---|
| 1259 | )
|
---|
| 1260 | ).then(function () {});
|
---|
| 1261 | }
|
---|
| 1262 | })();
|
---|
| 1263 |
|
---|
| 1264 | return Promise.resolve(
|
---|
| 1265 | _temp6 && _temp6.then ? _temp6.then(_temp5) : _temp5(_temp6)
|
---|
| 1266 | );
|
---|
| 1267 | } catch (e) {
|
---|
| 1268 | return Promise.reject(e);
|
---|
| 1269 | }
|
---|
| 1270 | };
|
---|
| 1271 |
|
---|
| 1272 | _proto.mergeStylesheets = function mergeStylesheets(document) {
|
---|
| 1273 | try {
|
---|
| 1274 | var _temp9 = function _temp9() {
|
---|
| 1275 | setNodeText(first, sheet);
|
---|
| 1276 | };
|
---|
| 1277 |
|
---|
| 1278 | var _this5 = this;
|
---|
| 1279 |
|
---|
| 1280 | var styles = [].slice.call(document.querySelectorAll('style'));
|
---|
| 1281 |
|
---|
| 1282 | if (styles.length === 0) {
|
---|
| 1283 | _this5.logger.warn(
|
---|
| 1284 | 'Merging inline stylesheets into a single <style> tag skipped, no inline stylesheets to merge'
|
---|
| 1285 | );
|
---|
| 1286 |
|
---|
| 1287 | return Promise.resolve();
|
---|
| 1288 | }
|
---|
| 1289 |
|
---|
| 1290 | var first = styles[0];
|
---|
| 1291 | var sheet = first.textContent;
|
---|
| 1292 |
|
---|
| 1293 | for (var i = 1; i < styles.length; i++) {
|
---|
| 1294 | var node = styles[i];
|
---|
| 1295 | sheet += node.textContent;
|
---|
| 1296 | node.remove();
|
---|
| 1297 | }
|
---|
| 1298 |
|
---|
| 1299 | var _temp10 = (function () {
|
---|
| 1300 | if (_this5.options.compress !== false) {
|
---|
| 1301 | var before = sheet;
|
---|
| 1302 | var processor = postcss([cssnano()]);
|
---|
| 1303 | return Promise.resolve(
|
---|
| 1304 | processor.process(before, {
|
---|
| 1305 | from: undefined,
|
---|
| 1306 | })
|
---|
| 1307 | ).then(function (result) {
|
---|
| 1308 | // @todo sourcemap support (elsewhere first)
|
---|
| 1309 | sheet = result.css;
|
---|
| 1310 | });
|
---|
| 1311 | }
|
---|
| 1312 | })();
|
---|
| 1313 |
|
---|
| 1314 | return Promise.resolve(
|
---|
| 1315 | _temp10 && _temp10.then ? _temp10.then(_temp9) : _temp9(_temp10)
|
---|
| 1316 | );
|
---|
| 1317 | } catch (e) {
|
---|
| 1318 | return Promise.reject(e);
|
---|
| 1319 | }
|
---|
| 1320 | };
|
---|
| 1321 | /**
|
---|
| 1322 | * Inline the target stylesheet referred to by a <link rel="stylesheet"> (assuming it passes `options.filter`)
|
---|
| 1323 | */
|
---|
| 1324 |
|
---|
| 1325 | _proto.embedLinkedStylesheet = function embedLinkedStylesheet(
|
---|
| 1326 | link,
|
---|
| 1327 | compilation,
|
---|
| 1328 | outputPath,
|
---|
| 1329 | publicPath
|
---|
| 1330 | ) {
|
---|
| 1331 | try {
|
---|
| 1332 | var _temp14 = function _temp14(_result2) {
|
---|
| 1333 | if (_exit2) return _result2;
|
---|
| 1334 | // CSS loader is only injected for the first sheet, then this becomes an empty string
|
---|
| 1335 | var cssLoaderPreamble =
|
---|
| 1336 | "function $loadcss(u,m,l){(l=document.createElement('link')).rel='stylesheet';l.href=u;document.head.appendChild(l)}";
|
---|
| 1337 | var lazy = preloadMode === 'js-lazy';
|
---|
| 1338 |
|
---|
| 1339 | if (lazy) {
|
---|
| 1340 | cssLoaderPreamble = cssLoaderPreamble.replace(
|
---|
| 1341 | 'l.href',
|
---|
| 1342 | "l.media='print';l.onload=function(){l.media=m};l.href"
|
---|
| 1343 | );
|
---|
| 1344 | } // the reduced critical CSS gets injected into a new <style> tag
|
---|
| 1345 |
|
---|
| 1346 | var style = document.createElement('style');
|
---|
| 1347 | style.appendChild(document.createTextNode(sheet));
|
---|
| 1348 | link.parentNode.insertBefore(style, link);
|
---|
| 1349 |
|
---|
| 1350 | if (
|
---|
| 1351 | _this7.options.inlineThreshold &&
|
---|
| 1352 | sheet.length < _this7.options.inlineThreshold
|
---|
| 1353 | ) {
|
---|
| 1354 | style.$$reduce = false;
|
---|
| 1355 |
|
---|
| 1356 | _this7.logger.info(
|
---|
| 1357 | '\x1B[32mInlined all of ' +
|
---|
| 1358 | href +
|
---|
| 1359 | ' (' +
|
---|
| 1360 | sheet.length +
|
---|
| 1361 | ' was below the threshold of ' +
|
---|
| 1362 | _this7.options.inlineThreshold +
|
---|
| 1363 | ')\x1B[39m'
|
---|
| 1364 | );
|
---|
| 1365 |
|
---|
| 1366 | if (asset) {
|
---|
| 1367 | delete compilation.assets[relativePath];
|
---|
| 1368 | } else {
|
---|
| 1369 | _this7.logger.warn(
|
---|
| 1370 | ' > ' +
|
---|
| 1371 | href +
|
---|
| 1372 | ' was not found in assets. the resource may still be emitted but will be unreferenced.'
|
---|
| 1373 | );
|
---|
| 1374 | }
|
---|
| 1375 |
|
---|
| 1376 | link.parentNode.removeChild(link);
|
---|
| 1377 | return;
|
---|
| 1378 | } // drop references to webpack asset locations onto the tag, used for later reporting and in-place asset updates
|
---|
| 1379 |
|
---|
| 1380 | style.$$name = href;
|
---|
| 1381 | style.$$asset = asset;
|
---|
| 1382 | style.$$assetName = relativePath;
|
---|
| 1383 | style.$$assets = compilation.assets;
|
---|
| 1384 | style.$$links = [link]; // Allow disabling any mutation of the stylesheet link:
|
---|
| 1385 |
|
---|
| 1386 | if (preloadMode === false) return;
|
---|
| 1387 | var noscriptFallback = false;
|
---|
| 1388 |
|
---|
| 1389 | if (preloadMode === 'body') {
|
---|
| 1390 | document.body.appendChild(link);
|
---|
| 1391 | } else {
|
---|
| 1392 | link.setAttribute('rel', 'preload');
|
---|
| 1393 | link.setAttribute('as', 'style');
|
---|
| 1394 |
|
---|
| 1395 | if (preloadMode === 'js' || preloadMode === 'js-lazy') {
|
---|
| 1396 | var script = document.createElement('script');
|
---|
| 1397 | var js =
|
---|
| 1398 | cssLoaderPreamble +
|
---|
| 1399 | '$loadcss(' +
|
---|
| 1400 | JSON.stringify(href) +
|
---|
| 1401 | (lazy ? ',' + JSON.stringify(media || 'all') : '') +
|
---|
| 1402 | ')';
|
---|
| 1403 | script.appendChild(document.createTextNode(js));
|
---|
| 1404 | link.parentNode.insertBefore(script, link.nextSibling);
|
---|
| 1405 | style.$$links.push(script);
|
---|
| 1406 | cssLoaderPreamble = '';
|
---|
| 1407 | noscriptFallback = true;
|
---|
| 1408 | } else if (preloadMode === 'media') {
|
---|
| 1409 | // @see https://github.com/filamentgroup/loadCSS/blob/af1106cfe0bf70147e22185afa7ead96c01dec48/src/loadCSS.js#L26
|
---|
| 1410 | link.setAttribute('rel', 'stylesheet');
|
---|
| 1411 | link.removeAttribute('as');
|
---|
| 1412 | link.setAttribute('media', 'print');
|
---|
| 1413 | link.setAttribute(
|
---|
| 1414 | 'onload',
|
---|
| 1415 | "this.media='" + (media || 'all') + "'"
|
---|
| 1416 | );
|
---|
| 1417 | noscriptFallback = true;
|
---|
| 1418 | } else if (preloadMode === 'swap') {
|
---|
| 1419 | link.setAttribute('onload', "this.rel='stylesheet'");
|
---|
| 1420 | noscriptFallback = true;
|
---|
| 1421 | } else {
|
---|
| 1422 | var bodyLink = document.createElement('link');
|
---|
| 1423 | bodyLink.setAttribute('rel', 'stylesheet');
|
---|
| 1424 | if (media) bodyLink.setAttribute('media', media);
|
---|
| 1425 | bodyLink.setAttribute('href', href);
|
---|
| 1426 | document.body.appendChild(bodyLink);
|
---|
| 1427 | style.$$links.push(bodyLink);
|
---|
| 1428 | }
|
---|
| 1429 | }
|
---|
| 1430 |
|
---|
| 1431 | if (_this7.options.noscriptFallback !== false && noscriptFallback) {
|
---|
| 1432 | var noscript = document.createElement('noscript');
|
---|
| 1433 | var noscriptLink = document.createElement('link');
|
---|
| 1434 | noscriptLink.setAttribute('rel', 'stylesheet');
|
---|
| 1435 | noscriptLink.setAttribute('href', href);
|
---|
| 1436 | if (media) noscriptLink.setAttribute('media', media);
|
---|
| 1437 | noscript.appendChild(noscriptLink);
|
---|
| 1438 | link.parentNode.insertBefore(noscript, link.nextSibling);
|
---|
| 1439 | style.$$links.push(noscript);
|
---|
| 1440 | }
|
---|
| 1441 | };
|
---|
| 1442 |
|
---|
| 1443 | var _exit2;
|
---|
| 1444 |
|
---|
| 1445 | var _this7 = this;
|
---|
| 1446 |
|
---|
| 1447 | var href = link.getAttribute('href');
|
---|
| 1448 | var media = link.getAttribute('media');
|
---|
| 1449 | var document = link.ownerDocument;
|
---|
| 1450 | var preloadMode = _this7.options.preload; // skip filtered resources, or network resources if no filter is provided
|
---|
| 1451 |
|
---|
| 1452 | if (
|
---|
| 1453 | _this7.urlFilter
|
---|
| 1454 | ? _this7.urlFilter(href)
|
---|
| 1455 | : href.match(/^(https?:)?\/\//)
|
---|
| 1456 | ) {
|
---|
| 1457 | return Promise.resolve();
|
---|
| 1458 | } // path on disk (with output.publicPath removed)
|
---|
| 1459 |
|
---|
| 1460 | var normalizedPath = href.replace(/^\//, '');
|
---|
| 1461 | var pathPrefix = (publicPath || '').replace(/(^\/|\/$)/g, '') + '/';
|
---|
| 1462 |
|
---|
| 1463 | if (normalizedPath.indexOf(pathPrefix) === 0) {
|
---|
| 1464 | normalizedPath = normalizedPath
|
---|
| 1465 | .substring(pathPrefix.length)
|
---|
| 1466 | .replace(/^\//, '');
|
---|
| 1467 | }
|
---|
| 1468 |
|
---|
| 1469 | var filename = path.resolve(outputPath, normalizedPath); // try to find a matching asset by filename in webpack's output (not yet written to disk)
|
---|
| 1470 |
|
---|
| 1471 | var relativePath = path
|
---|
| 1472 | .relative(outputPath, filename)
|
---|
| 1473 | .replace(/^\.\//, '');
|
---|
| 1474 | var asset = compilation.assets[relativePath]; // Attempt to read from assets, falling back to a disk read
|
---|
| 1475 |
|
---|
| 1476 | var sheet = asset && asset.source();
|
---|
| 1477 |
|
---|
| 1478 | var _temp15 = (function () {
|
---|
| 1479 | if (!sheet) {
|
---|
| 1480 | var _temp16 = _catch$1(
|
---|
| 1481 | function () {
|
---|
| 1482 | return Promise.resolve(
|
---|
| 1483 | _this7.readFile(compilation, filename)
|
---|
| 1484 | ).then(function (_this6$readFile) {
|
---|
| 1485 | sheet = _this6$readFile;
|
---|
| 1486 |
|
---|
| 1487 | _this7.logger.warn(
|
---|
| 1488 | 'Stylesheet "' +
|
---|
| 1489 | relativePath +
|
---|
| 1490 | '" not found in assets, but a file was located on disk.' +
|
---|
| 1491 | (_this7.options.pruneSource
|
---|
| 1492 | ? ' This means pruneSource will not be applied.'
|
---|
| 1493 | : '')
|
---|
| 1494 | );
|
---|
| 1495 | });
|
---|
| 1496 | },
|
---|
| 1497 | function () {
|
---|
| 1498 | _this7.logger.warn(
|
---|
| 1499 | 'Unable to locate stylesheet: ' + relativePath
|
---|
| 1500 | );
|
---|
| 1501 |
|
---|
| 1502 | _exit2 = 1;
|
---|
| 1503 | }
|
---|
| 1504 | );
|
---|
| 1505 |
|
---|
| 1506 | if (_temp16 && _temp16.then) return _temp16.then(function () {});
|
---|
| 1507 | }
|
---|
| 1508 | })();
|
---|
| 1509 |
|
---|
| 1510 | return Promise.resolve(
|
---|
| 1511 | _temp15 && _temp15.then ? _temp15.then(_temp14) : _temp14(_temp15)
|
---|
| 1512 | );
|
---|
| 1513 | } catch (e) {
|
---|
| 1514 | return Promise.reject(e);
|
---|
| 1515 | }
|
---|
| 1516 | };
|
---|
| 1517 | /**
|
---|
| 1518 | * Parse the stylesheet within a <style> element, then reduce it to contain only rules used by the document.
|
---|
| 1519 | */
|
---|
| 1520 |
|
---|
| 1521 | _proto.processStyle = function processStyle(style) {
|
---|
| 1522 | try {
|
---|
| 1523 | var _this9 = this;
|
---|
| 1524 |
|
---|
| 1525 | if (style.$$reduce === false) return Promise.resolve();
|
---|
| 1526 | var name = style.$$name ? style.$$name.replace(/^\//, '') : 'inline CSS';
|
---|
| 1527 | var options = _this9.options;
|
---|
| 1528 | var document = style.ownerDocument;
|
---|
| 1529 | var head = document.querySelector('head');
|
---|
| 1530 | var keyframesMode = options.keyframes || 'critical'; // we also accept a boolean value for options.keyframes
|
---|
| 1531 |
|
---|
| 1532 | if (keyframesMode === true) keyframesMode = 'all';
|
---|
| 1533 | if (keyframesMode === false) keyframesMode = 'none'; // basically `.textContent`
|
---|
| 1534 |
|
---|
| 1535 | var sheet =
|
---|
| 1536 | style.childNodes.length > 0 &&
|
---|
| 1537 | [].map
|
---|
| 1538 | .call(style.childNodes, function (node) {
|
---|
| 1539 | return node.nodeValue;
|
---|
| 1540 | })
|
---|
| 1541 | .join('\n'); // store a reference to the previous serialized stylesheet for reporting stats
|
---|
| 1542 |
|
---|
| 1543 | var before = sheet; // Skip empty stylesheets
|
---|
| 1544 |
|
---|
| 1545 | if (!sheet) return Promise.resolve();
|
---|
| 1546 | var ast = parseStylesheet(sheet);
|
---|
| 1547 | var astInverse = options.pruneSource ? parseStylesheet(sheet) : null; // a string to search for font names (very loose)
|
---|
| 1548 |
|
---|
| 1549 | var criticalFonts = '';
|
---|
| 1550 | var failedSelectors = [];
|
---|
| 1551 | var criticalKeyframeNames = []; // Walk all CSS rules, marking unused rules with `.$$remove=true` for removal in the second pass.
|
---|
| 1552 | // This first pass is also used to collect font and keyframe usage used in the second pass.
|
---|
| 1553 |
|
---|
| 1554 | walkStyleRules(
|
---|
| 1555 | ast,
|
---|
| 1556 | markOnly(function (rule) {
|
---|
| 1557 | if (rule.type === 'rule') {
|
---|
| 1558 | // Filter the selector list down to only those match
|
---|
| 1559 | rule.filterSelectors(function (sel) {
|
---|
| 1560 | // Strip pseudo-elements and pseudo-classes, since we only care that their associated elements exist.
|
---|
| 1561 | // This means any selector for a pseudo-element or having a pseudo-class will be inlined if the rest of the selector matches.
|
---|
| 1562 | if (sel !== ':root') {
|
---|
| 1563 | sel = sel
|
---|
| 1564 | .replace(/(?:>\s*)?::?[a-z-]+\s*(\{|$)/gi, '$1')
|
---|
| 1565 | .trim();
|
---|
| 1566 | }
|
---|
| 1567 |
|
---|
| 1568 | if (!sel) return false;
|
---|
| 1569 |
|
---|
| 1570 | try {
|
---|
| 1571 | return document.querySelector(sel) != null;
|
---|
| 1572 | } catch (e) {
|
---|
| 1573 | failedSelectors.push(sel + ' -> ' + e.message);
|
---|
| 1574 | return false;
|
---|
| 1575 | }
|
---|
| 1576 | }); // If there are no matched selectors, remove the rule:
|
---|
| 1577 |
|
---|
| 1578 | if (rule.selectors.length === 0) {
|
---|
| 1579 | return false;
|
---|
| 1580 | }
|
---|
| 1581 |
|
---|
| 1582 | if (rule.declarations) {
|
---|
| 1583 | for (var i = 0; i < rule.declarations.length; i++) {
|
---|
| 1584 | var decl = rule.declarations[i]; // detect used fonts
|
---|
| 1585 |
|
---|
| 1586 | if (
|
---|
| 1587 | decl.property &&
|
---|
| 1588 | decl.property.match(/\bfont(-family)?\b/i)
|
---|
| 1589 | ) {
|
---|
| 1590 | criticalFonts += ' ' + decl.value;
|
---|
| 1591 | } // detect used keyframes
|
---|
| 1592 |
|
---|
| 1593 | if (
|
---|
| 1594 | decl.property === 'animation' ||
|
---|
| 1595 | decl.property === 'animation-name'
|
---|
| 1596 | ) {
|
---|
| 1597 | // @todo: parse animation declarations and extract only the name. for now we'll do a lazy match.
|
---|
| 1598 | var names = decl.value.split(/\s+/);
|
---|
| 1599 |
|
---|
| 1600 | for (var j = 0; j < names.length; j++) {
|
---|
| 1601 | var _name = names[j].trim();
|
---|
| 1602 |
|
---|
| 1603 | if (_name) criticalKeyframeNames.push(_name);
|
---|
| 1604 | }
|
---|
| 1605 | }
|
---|
| 1606 | }
|
---|
| 1607 | }
|
---|
| 1608 | } // keep font rules, they're handled in the second pass:
|
---|
| 1609 |
|
---|
| 1610 | if (rule.type === 'font-face') return; // If there are no remaining rules, remove the whole rule:
|
---|
| 1611 |
|
---|
| 1612 | var rules =
|
---|
| 1613 | rule.rules &&
|
---|
| 1614 | rule.rules.filter(function (rule) {
|
---|
| 1615 | return !rule.$$remove;
|
---|
| 1616 | });
|
---|
| 1617 | return !rules || rules.length !== 0;
|
---|
| 1618 | })
|
---|
| 1619 | );
|
---|
| 1620 |
|
---|
| 1621 | if (failedSelectors.length !== 0) {
|
---|
| 1622 | _this9.logger.warn(
|
---|
| 1623 | failedSelectors.length +
|
---|
| 1624 | ' rules skipped due to selector errors:\n ' +
|
---|
| 1625 | failedSelectors.join('\n ')
|
---|
| 1626 | );
|
---|
| 1627 | }
|
---|
| 1628 |
|
---|
| 1629 | var shouldPreloadFonts =
|
---|
| 1630 | options.fonts === true || options.preloadFonts === true;
|
---|
| 1631 | var shouldInlineFonts =
|
---|
| 1632 | options.fonts !== false && options.inlineFonts === true;
|
---|
| 1633 | var preloadedFonts = []; // Second pass, using data picked up from the first
|
---|
| 1634 |
|
---|
| 1635 | walkStyleRulesWithReverseMirror(ast, astInverse, function (rule) {
|
---|
| 1636 | // remove any rules marked in the first pass
|
---|
| 1637 | if (rule.$$remove === true) return false;
|
---|
| 1638 | applyMarkedSelectors(rule); // prune @keyframes rules
|
---|
| 1639 |
|
---|
| 1640 | if (rule.type === 'keyframes') {
|
---|
| 1641 | if (keyframesMode === 'none') return false;
|
---|
| 1642 | if (keyframesMode === 'all') return true;
|
---|
| 1643 | return criticalKeyframeNames.indexOf(rule.name) !== -1;
|
---|
| 1644 | } // prune @font-face rules
|
---|
| 1645 |
|
---|
| 1646 | if (rule.type === 'font-face') {
|
---|
| 1647 | var family, src;
|
---|
| 1648 |
|
---|
| 1649 | for (var i = 0; i < rule.declarations.length; i++) {
|
---|
| 1650 | var decl = rule.declarations[i];
|
---|
| 1651 |
|
---|
| 1652 | if (decl.property === 'src') {
|
---|
| 1653 | // @todo parse this properly and generate multiple preloads with type="font/woff2" etc
|
---|
| 1654 | src = (decl.value.match(/url\s*\(\s*(['"]?)(.+?)\1\s*\)/) ||
|
---|
| 1655 | [])[2];
|
---|
| 1656 | } else if (decl.property === 'font-family') {
|
---|
| 1657 | family = decl.value;
|
---|
| 1658 | }
|
---|
| 1659 | }
|
---|
| 1660 |
|
---|
| 1661 | if (src && shouldPreloadFonts && preloadedFonts.indexOf(src) === -1) {
|
---|
| 1662 | preloadedFonts.push(src);
|
---|
| 1663 | var preload = document.createElement('link');
|
---|
| 1664 | preload.setAttribute('rel', 'preload');
|
---|
| 1665 | preload.setAttribute('as', 'font');
|
---|
| 1666 | preload.setAttribute('crossorigin', 'anonymous');
|
---|
| 1667 | preload.setAttribute('href', src.trim());
|
---|
| 1668 | head.appendChild(preload);
|
---|
| 1669 | } // if we're missing info, if the font is unused, or if critical font inlining is disabled, remove the rule:
|
---|
| 1670 |
|
---|
| 1671 | if (
|
---|
| 1672 | !family ||
|
---|
| 1673 | !src ||
|
---|
| 1674 | criticalFonts.indexOf(family) === -1 ||
|
---|
| 1675 | !shouldInlineFonts
|
---|
| 1676 | ) {
|
---|
| 1677 | return false;
|
---|
| 1678 | }
|
---|
| 1679 | }
|
---|
| 1680 | });
|
---|
| 1681 | sheet = serializeStylesheet(ast, {
|
---|
| 1682 | compress: _this9.options.compress !== false,
|
---|
| 1683 | }).trim(); // If all rules were removed, get rid of the style element entirely
|
---|
| 1684 |
|
---|
| 1685 | if (sheet.trim().length === 0) {
|
---|
| 1686 | if (style.parentNode) {
|
---|
| 1687 | style.parentNode.removeChild(style);
|
---|
| 1688 | }
|
---|
| 1689 |
|
---|
| 1690 | return Promise.resolve();
|
---|
| 1691 | }
|
---|
| 1692 |
|
---|
| 1693 | var afterText = '';
|
---|
| 1694 |
|
---|
| 1695 | if (options.pruneSource) {
|
---|
| 1696 | var sheetInverse = serializeStylesheet(astInverse, {
|
---|
| 1697 | compress: _this9.options.compress !== false,
|
---|
| 1698 | });
|
---|
| 1699 | var asset = style.$$asset;
|
---|
| 1700 |
|
---|
| 1701 | if (asset) {
|
---|
| 1702 | // if external stylesheet would be below minimum size, just inline everything
|
---|
| 1703 | var minSize = _this9.options.minimumExternalSize;
|
---|
| 1704 |
|
---|
| 1705 | if (minSize && sheetInverse.length < minSize) {
|
---|
| 1706 | _this9.logger.info(
|
---|
| 1707 | '\x1B[32mInlined all of ' +
|
---|
| 1708 | name +
|
---|
| 1709 | ' (non-critical external stylesheet would have been ' +
|
---|
| 1710 | sheetInverse.length +
|
---|
| 1711 | 'b, which was below the threshold of ' +
|
---|
| 1712 | minSize +
|
---|
| 1713 | ')\x1B[39m'
|
---|
| 1714 | );
|
---|
| 1715 |
|
---|
| 1716 | setNodeText(style, before); // remove any associated external resources/loaders:
|
---|
| 1717 |
|
---|
| 1718 | if (style.$$links) {
|
---|
| 1719 | for (
|
---|
| 1720 | var _iterator = _createForOfIteratorHelperLoose(style.$$links),
|
---|
| 1721 | _step;
|
---|
| 1722 | !(_step = _iterator()).done;
|
---|
| 1723 |
|
---|
| 1724 | ) {
|
---|
| 1725 | var link = _step.value;
|
---|
| 1726 | var parent = link.parentNode;
|
---|
| 1727 | if (parent) parent.removeChild(link);
|
---|
| 1728 | }
|
---|
| 1729 | } // delete the webpack asset:
|
---|
| 1730 |
|
---|
| 1731 | delete style.$$assets[style.$$assetName];
|
---|
| 1732 | return Promise.resolve();
|
---|
| 1733 | }
|
---|
| 1734 |
|
---|
| 1735 | var _percent = (sheetInverse.length / before.length) * 100;
|
---|
| 1736 |
|
---|
| 1737 | afterText =
|
---|
| 1738 | ', reducing non-inlined size ' +
|
---|
| 1739 | (_percent | 0) +
|
---|
| 1740 | '% to ' +
|
---|
| 1741 | prettyBytes(sheetInverse.length);
|
---|
| 1742 | style.$$assets[
|
---|
| 1743 | style.$$assetName
|
---|
| 1744 | ] = new sources.LineToLineMappedSource(
|
---|
| 1745 | sheetInverse,
|
---|
| 1746 | style.$$assetName,
|
---|
| 1747 | before
|
---|
| 1748 | );
|
---|
| 1749 | } else {
|
---|
| 1750 | _this9.logger.warn(
|
---|
| 1751 | 'pruneSource is enabled, but a style (' +
|
---|
| 1752 | name +
|
---|
| 1753 | ') has no corresponding Webpack asset.'
|
---|
| 1754 | );
|
---|
| 1755 | }
|
---|
| 1756 | } // replace the inline stylesheet with its critical'd counterpart
|
---|
| 1757 |
|
---|
| 1758 | setNodeText(style, sheet); // output stats
|
---|
| 1759 |
|
---|
| 1760 | var percent = ((sheet.length / before.length) * 100) | 0;
|
---|
| 1761 |
|
---|
| 1762 | _this9.logger.info(
|
---|
| 1763 | '\x1B[32mInlined ' +
|
---|
| 1764 | prettyBytes(sheet.length) +
|
---|
| 1765 | ' (' +
|
---|
| 1766 | percent +
|
---|
| 1767 | '% of original ' +
|
---|
| 1768 | prettyBytes(before.length) +
|
---|
| 1769 | ') of ' +
|
---|
| 1770 | name +
|
---|
| 1771 | afterText +
|
---|
| 1772 | '.\x1B[39m'
|
---|
| 1773 | );
|
---|
| 1774 |
|
---|
| 1775 | return Promise.resolve();
|
---|
| 1776 | } catch (e) {
|
---|
| 1777 | return Promise.reject(e);
|
---|
| 1778 | }
|
---|
| 1779 | };
|
---|
| 1780 |
|
---|
| 1781 | return Critters;
|
---|
| 1782 | })();
|
---|
| 1783 |
|
---|
| 1784 | exports.core = Critters;
|
---|
| 1785 | exports.default = Critters$1;
|
---|
| 1786 | // # sourceMappingURL=critters.js.map
|
---|