Needs additional guidance around feeding `getBundle` through a minifier
If you run getBundle output through a minifier before the transform gets to it, it will get removed!
Specifically this quick tip: https://www.11ty.dev/docs/quicktips/inline-css/#capture-and-minify
<!-- capture the CSS content as a Nunjucks variable -->
{% set css %}
{% include "sample.css" %}
{% getBundle "css" %}
{% endset %}
<!-- feed it through our cssmin filter to minify -->
<style>
{{ css | cssmin | safe }}
</style>
Workaround is to move the bundle outside of the css minification pipeline (and optionally use a bundle transform to minify instead):
<!-- capture the CSS content as a Nunjucks variable -->
{% set css %}
{% include "sample.css" %}
{% endset %}
<!-- feed it through our cssmin filter to minify -->
<style>
{{ css | cssmin | safe }}
</style>
<style>{% getBundle "css" %}</style>
@zachleat I chose to add minification directly in my own template format (I support Sass, not CSS):
- https://github.com/nhoizey/pack11ty/blob/63ea72d55d41236fb965d6371caa87e5aeabf384/eleventy.config.js#L73-L78
(I'm currently trying to move this code out of the project config file, into a plugin, so this should no longer be in the main branch soon.)
The doco explains how to do it: Modify the bundle output. This is also more performant during the build.
Here's an example of what I'm doing in my site: .eleventy.js
const bundlerPlugin = require("@11ty/eleventy-plugin-bundle");
const postcss = require('postcss');
const postcssNesting = require('postcss-nesting');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const UglifyJS = require("uglify-js");
module.exports = function (eleventyConfig) {
eleventyConfig.addPlugin(bundlerPlugin, {
transforms: [
async function (code) {
// this.type returns the bundle name.
if (this.type === 'css') {
// Same as Eleventy transforms, this.page is available here.
let result = await postcss([
// postcssNesting,
autoprefixer,
cssnano
]).process(code, { from: this.page.inputPath, to: null });
return result.css;
}
if (this.type === 'js') {
let minified = UglifyJS.minify(code);
if (minified.error) {
console.log("UglifyJS error: ", minified.error);
return code;
}
return minified.code;
}
return code;
}
]
});
}
head.njk
<head>
[...]
{% css %}
{% include "assets/css/inline.css" %}
{% endcss %}
<!-- Inlined critical styles -->
<style>{% getBundle "css" %}</style>
<!-- Deferred non-critical styles -->
<link rel="stylesheet" href="{% getBundleFileUrl 'css', 'defer' %}" media="print" onload="this.media='all'">
<noscript>
<link rel="stylesheet" href="{% getBundleFileUrl 'css', 'defer' %}">
</noscript>
<!-- Inlined js bundle -->
<script>{% getBundle "js" %}</script>
</head>
I tried replicating the example in the bottom of the readme but it did not work, ultimately, I used the solution by elgandoz and it's working fine.
As an example, here's the code that did not work:
cfg.addBundle("css", {
transforms: [
(/** @type {string} */ input) => (new CleanCSS()).minify(input).styles
]
});
cfg.addBundle("js", {
transforms: [
async function(/** @type {string} */ input) {
try {
const result = await minifyJs(input);
return result.code;
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
return input;
}
}
]
});
Have I messed up somewhere? I tried with non-arrow functions but still no success.
The working code:
cfg.addPlugin(pluginBundle, {
transforms: [
async function(content) {
// this.type returns the bundle name.
if (this.type === "css") {
return (new CleanCSS).minify(content).styles;
}
if (this.type === "js") {
try {
const minified = await minifyJs(content);
return minified.code;
}
catch (e) {
// eslint-disable-next-line no-console
console.error(e);
return content;
}
}
return content;
}
]
});