diff --git a/config/html-webpack-inline-critical-css-plugin.js b/config/critters-webpack-plugin.js
similarity index 84%
rename from config/html-webpack-inline-critical-css-plugin.js
rename to config/critters-webpack-plugin.js
index 39270652..6aa631f1 100644
--- a/config/html-webpack-inline-critical-css-plugin.js
+++ b/config/critters-webpack-plugin.js
@@ -3,10 +3,11 @@ const path = require('path');
const parse5 = require('parse5');
const nwmatcher = require('nwmatcher');
const css = require('css');
+const prettyBytes = require('pretty-bytes');
const treeUtils = parse5.treeAdapters.htmlparser2;
-const PLUGIN_NAME = 'html-webpack-inline-critical-css-plugin';
+const PLUGIN_NAME = 'critters-webpack-plugin';
const PARSE5_OPTS = {
treeAdapter: treeUtils
@@ -25,6 +26,11 @@ const ElementExtensions = {
return this.tagName;
}
},
+ insertBefore: function (child, referenceNode) {
+ if (!referenceNode) return this.appendChild(child);
+ treeUtils.insertBefore(this, child, referenceNode);
+ return child;
+ },
appendChild: function (child) {
treeUtils.appendChild(this, child);
return child;
@@ -82,7 +88,13 @@ const DocumentExtensions = {
addEventListener: Object
};
-module.exports = class HtmlWebpackInlineCriticalCssPlugin {
+/** Critters: Webpack Plugin Edition!
+ * @class
+ * @param {Object} options
+ * @param {Boolean} [options.external=true] Fetch and inline critical styles from external stylesheets
+ * @param {Boolean} [options.async=true] If `false`, only already-inline stylesheets will be reduced to critical rules.
+ */
+module.exports = class CrittersWebpackPlugin {
constructor(options) {
this.options = options || {};
}
@@ -112,6 +124,7 @@ module.exports = class HtmlWebpackInlineCriticalCssPlugin {
const externalSheets = document.querySelectorAll('link[rel="stylesheet"]');
Promise.all(externalSheets.map(function(link) {
+ if (self.options.external===false) return;
const href = link.getAttribute('href');
if (href.match(/^(https?:)?\/\//)) return Promise.resolve();
const filename = path.resolve(outputPath, href.replace(/^\//, ''));
@@ -121,7 +134,12 @@ module.exports = class HtmlWebpackInlineCriticalCssPlugin {
const style = document.createElement('style');
style.$$name = href;
style.appendChild(document.createTextNode(sheet));
- link.parentNode.appendChild(style); // @TODO insertBefore
+ link.parentNode.insertBefore(style, link.nextSibling);
+ if (self.options.async) {
+ link.setAttribute('rel', 'preload');
+ link.setAttribute('as', 'style');
+ link.setAttribute('onload', "this.rel='stylesheet'");
+ }
});
}))
.then(function() {
@@ -199,8 +217,9 @@ module.exports = class HtmlWebpackInlineCriticalCssPlugin {
}
style.appendChild(document.createTextNode(sheet));
}
- const name = style.$$name;
- console.log('\u001b[32mInlined CSS Size' + (name ? (' ('+name+')') : '') + ': ' + (sheet.length / 1000).toPrecision(2) + 'kb (' + ((before.length - sheet.length) / before.length * 100 | 0) + '% of original ' + (before.length / 1000).toPrecision(2) + 'kb)\u001b[39m');
+ const name = style.$$name ? style.$$name.replace(/^\//, '') : 'inline CSS';
+ const percent = (before.length - sheet.length) / before.length * 100 | 0;
+ console.log('\u001b[32mCritters: inlined ' + prettyBytes(sheet.length) + ' (' + percent + '% of original ' + prettyBytes(before.length) + ') of ' + name + '.\u001b[39m');
});
}
};
diff --git a/webpack.config.js b/webpack.config.js
index 89df2021..d640dbf9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -9,7 +9,7 @@ const PreloadWebpackPlugin = require('preload-webpack-plugin');
const ReplacePlugin = require('webpack-plugin-replace');
const CopyPlugin = require('copy-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
-const HtmlInlineCssPlugin = require('./config/html-webpack-inline-critical-css-plugin');
+const CrittersPlugin = require('./config/critters-webpack-plugin');
const WatchTimestampsPlugin = require('./config/watch-timestamps-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
@@ -175,8 +175,11 @@ module.exports = function(_, env) {
// Inject for resources
isProd && new PreloadWebpackPlugin(),
- isProd && new HtmlInlineCssPlugin(),
+ isProd && new CrittersPlugin({
+ // convert critical'd to
+ async: true
+ }),
// Inline constants during build, so they can be folded by UglifyJS.
new webpack.DefinePlugin({