{"success":true,"data":{"title":"Webpack and WordPress 💙","slug":"webpack-and-wordpress","excerpt":"A walkthrough of a Webpack boilerplate theme for WordPress, covering Hot Module Replacement for live browser updates without a full reload, Sass compilation and autoprefixing, ES6 transpilation via Babel, cache-busting manifest files, automatic favicon generation, and CSS and JavaScript minification — with separate development and production configurations throughout.","has_code_sample":true,"published_at":"2019-01-27T18:40:00.000Z","content":"\u003cp\u003eI’ve recently made the move from using \u003ca href=\"https://gruntjs.com/\"\u003eGrunt\u003c/a\u003e and \u003ca href=\"https://gulpjs.com/\"\u003eGulp\u003c/a\u003e to build my front end to using Webpack instead. It’s been a \u003ca href=\"https://webpack.js.org/concepts/\"\u003esteep learning curve\u003c/a\u003e as Webpack is a whole different beast. Getting started can be quite daunting, but once you’ve played with it a bit it all starts to make sense.\u003c/p\u003e\n\n\u003cp\u003eWebpack has given me access to a lot of useful tools, so I’ve put together a boilerplate WordPress theme which can be used as a starting point for new projects. I’ve \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate\"\u003ereleased it on GitHub\u003c/a\u003e, and here I’ll take you through the different parts.\u003c/p\u003e\n\n\u003ch2 id=\"features\"\u003eFeatures\u003c/h2\u003e\n\u003cp\u003eFirst up, for the initial version, I had a few goals in mind.\u003c/p\u003e\n\n\u003ch3 id=\"hot-module-replacement\"\u003eHot Module Replacement\u003c/h3\u003e\n\u003cp\u003eRight at the top of my list was Hot Module Replacement. This nifty little feature will appropriately update or refresh the browser as I work on files. For example, if I change some Sass it will update the page without the need for a full reload. That saves time. Cleverly, if I change something that can’t be updated like that the page will be refreshed. Neat!\u003c/p\u003e\n\n\u003ch3 id=\"webpack-sassy\"\u003eWebpack! Sassy!\u003c/h3\u003e\n\u003cp\u003eI like to write in Sass but browsers don’t work with it natively. It will need to be compiled into CSS.\u003c/p\u003e\n\n\u003cp\u003eIn the same vein, Webpack can help me with autoprefixing — after it’s compiled my Sass to CSS it can add the prefixes I’ll need automatically so I don’t have to.\u003c/p\u003e\n\n\u003ch3 id=\"tower-of-babel\"\u003eTower of Babel\u003c/h3\u003e\n\u003cp\u003e\u003ca href=\"http://es6-features.org\"\u003eES6\u003c/a\u003e (and its descendants) bring lots of nice new features to JavaScript. To me, it makes JavaScript feel like a more mature language. That’s great! But certain browsers (no prizes for guessing which) don’t support it. Thankfully \u003ca href=\"https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them\"\u003etranspiling\u003c/a\u003e saves us here. I can write in ES6 and have Webpack convert it to older JavaScript, using \u003ca href=\"https://babeljs.io\"\u003eBabel\u003c/a\u003e, making it compatible with older browsers.\u003c/p\u003e\n\n\u003ch3 id=\"bust-that-cache\"\u003eBust That Cache\u003c/h3\u003e\n\u003cp\u003eI spend a lot of time optimising for speed — a \u003ca href=\"https://blog.hubspot.com/marketing/page-load-time-conversion-rates\"\u003equick site matters\u003c/a\u003e. One of the most fundamental ways of helping a site load quickly is by leveraging browser caching so that a user’s browser saves a copy of site assets, like CSS. Doing so means the browser doesn’t have to download the cached files for every request. This is great until you make a change and the user is left with an out-of-date file.\u003c/p\u003e\n\n\u003cp\u003eTo work around that I’ll generate a manifest file that relates a file to its most recent version. I’ll go into it in detail later, but in a nutshell, you’ll have some JSON like this:\u003c/p\u003e\n\n\u003cdiv class=\"language-json highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"css/style.css\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"path/to/current/css/style.abc1234d.css\"\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eYou can use that to have your theme queue up the latest version of the file, which has a hash appended to its filename so the latest files are always downloaded.\u003c/p\u003e\n\n\u003ch3 id=\"automatically-generate-favicons\"\u003eAutomatically Generate Favicons\u003c/h3\u003e\n\u003cp\u003eYou need about a million favicons for a modern site. These are for the various browsers and devices that can access them. I don’t want to have to generate them all so I’ll have Webpack do the heavy lifting for me. It will take one source file, generate all the versions needed from that and then spit out the HTML needed to link to them. All I have to do is include it in my theme.\u003c/p\u003e\n\n\u003ch3 id=\"minify-all-the-things\"\u003eMinify All The Things\u003c/h3\u003e\n\u003cp\u003eAll the generated CSS and JavaScript should be minified to make it as small as can be. Whilst that’s happening we should generate \u003ca href=\"https://blog.teamtreehouse.com/introduction-source-maps\"\u003esourcemaps\u003c/a\u003e so the compiled versions of our sources can be debugged easier. Let’s get started.\u003c/p\u003e\n\n\u003ch2 id=\"structure\"\u003eStructure\u003c/h2\u003e\n\u003cp\u003eThe Webpack parts of the boilerplate are split up into a few files to keep things logically separate. I’ve linked to the files below as they were when I wrote this article. They may have been updated since, but I’ll keep commits helpful and everything commented so you can see what’s changed, and why.\u003c/p\u003e\n\n\u003cul\u003e\n  \u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/4bb168fa72c97bdfb4a8c4f6088162a96c4aa1e6/.env.sample\"\u003e.env(.sample)\u003c/a\u003e:\u003c/strong\u003e Used to configure the boilerplate.\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/a4efd93d2ed30ea5b6842fe9618fc4e85d8fd6b3/package.json\"\u003epackage.json\u003c/a\u003e:\u003c/strong\u003e Defines the packages needed, and the commands that can be run.\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/078ed331a076b09f5a2fb4a0b113aedf7d2c83fa/postcss.config.js\"\u003epostcss.config.js\u003c/a\u003e:\u003c/strong\u003e Configure PostCSS, which handles auto-prefixing for us.\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/7bae5211bf0c5a9dcc74d26ec4a07984f8c4919c/webpack.dev.js\"\u003ewebpack.dev.js\u003c/a\u003e:\u003c/strong\u003e Defines our development set-up.\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003e\u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/55c30bcbe007aa164fa84965dc6d3ebe06444d9d/webpack.prod.js\"\u003ewebpack.prod.js\u003c/a\u003e:\u003c/strong\u003e Defines our production set-up.\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003ch2 id=\"environment\"\u003eEnvironment\u003c/h2\u003e\n\u003cp\u003eWhen developing WordPress I use PHP’s inbuilt server to serve the website, and Webpack Dev Server to serve the assets. The development configuration assumes you do the same, so if you work differently you will need to fiddle with some settings. The command I’m running for the PHP server, which should be executed in the same directory as \u003ccode\u003ewp-config.php\u003c/code\u003e, is:\u003c/p\u003e\n\n\u003cdiv class=\"language-bash highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"nv\"\u003e$ \u003c/span\u003ephp \u003cspan class=\"nt\"\u003e-S\u003c/span\u003e localhost:8000\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003ch2 id=\"development\"\u003eDevelopment\u003c/h2\u003e\n\u003cp\u003eLet’s start with the development environment.\u003c/p\u003e\n\n\u003cp\u003eTo get Hot Module Replacement working I use Webpack Dev Server. The \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/7bae5211bf0c5a9dcc74d26ec4a07984f8c4919c/webpack.dev.js#L14\"\u003econfiguration looks like this\u003c/a\u003e:\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003econfigureDevServer\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003econtentBase\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003e__dirname\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003edist\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehost\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003elocalhost\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehot\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003einline\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eoverlay\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eport\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e8080\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eThe \u003ccode\u003ehost\u003c/code\u003e and \u003ccode\u003eport\u003c/code\u003e options specify some details about where we’ll be hosting from. In this instance that will be \u003ccode\u003ehttp://localhost:8080\u003c/code\u003e. Note the port number - this is important. The Webpack Dev Server must not conflict with the PHP server.\u003c/p\u003e\n\n\u003cp\u003eWe configure the \u003ccode\u003econtentBase\u003c/code\u003e that our assets will be served from. In this case, it’ll be the \u003ccode\u003edist\u003c/code\u003e folder. When combined with the host set-up we’ll find our assets at \u003ccode\u003ehttp://localhost:8080/dist/\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003ehot\u003c/code\u003e option is what enables the Hot Module Replacement feature and \u003ccode\u003einline\u003c/code\u003e lets us toggle \u003ca href=\"https://webpack.js.org/configuration/dev-server/#devserver-inline\"\u003ehow the HMR happens\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eFinally, \u003ccode\u003eoverlay\u003c/code\u003e enables an error overlay on the page when a mistake is made.\u003c/p\u003e\n\n\u003cp\u003eNow we can bring it all together. We pass the \u003ccode\u003edevServer\u003c/code\u003e option the function we just wrote to configure it and add the \u003ccode\u003ewebpack.HotModuleReplacementPlugin\u003c/code\u003e plugin to the export.\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"nx\"\u003emodule\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eexports\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003edevServer\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nf\"\u003econfigureDevServer\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e// ...\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eplugins\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nx\"\u003ewebpack\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nc\"\u003eHotModuleReplacementPlugin\u003c/span\u003e\u003cspan class=\"p\"\u003e()]\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eThe last bit to be aware of is the rules that match images; we don’t do anything fancy with them in development, so we just make sure they’re available with \u003ccode\u003efile-loader\u003c/code\u003e:\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nl\"\u003etest\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"sr\"\u003e/\u003c/span\u003e\u003cspan class=\"se\"\u003e\\.\u003c/span\u003e\u003cspan class=\"sr\"\u003esvg$/\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003euse\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nl\"\u003eloader\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003efile-loader\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nl\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e[name].[ext]\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"nx\"\u003eoutputPath\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003epath\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003e__dirname\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e/dist\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n      \u003cspan class=\"nx\"\u003epublicPath\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehttp://localhost:8080/\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eYou may need to add some additional matches for all the images you use. Here’s an example of how to do that:\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nl\"\u003etest\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"sr\"\u003e/\u003c/span\u003e\u003cspan class=\"se\"\u003e\\.(\u003c/span\u003e\u003cspan class=\"sr\"\u003esvg|png|jpe\u003c/span\u003e\u003cspan class=\"se\"\u003e?\u003c/span\u003e\u003cspan class=\"sr\"\u003eg\u003c/span\u003e\u003cspan class=\"se\"\u003e)\u003c/span\u003e\u003cspan class=\"sr\"\u003e$/\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e// ...\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003ch3 id=\"usage\"\u003eUsage\u003c/h3\u003e\n\u003cp\u003eTo start the dev server, hop onto Terminal and navigate to the theme. Once you’re there run:\u003c/p\u003e\n\n\u003cdiv class=\"language-bash highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"nv\"\u003e$ \u003c/span\u003enpm start\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003ch2 id=\"production\"\u003eProduction\u003c/h2\u003e\n\u003cp\u003eThe dev environment is pretty light, this is deliberate - we don’t want to wait five minutes every time we change one line of Sass. Production, however, has a lot more going on. Let’s dive in.\u003c/p\u003e\n\n\u003ch3 id=\"compiling-sass-and-processing-css\"\u003eCompiling Sass and Processing CSS\u003c/h3\u003e\n\u003cp\u003eFirst-up we tell Webpack \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/7bae5211bf0c5a9dcc74d26ec4a07984f8c4919c/webpack.prod.js#L154\"\u003ewhat to do with our Sass files\u003c/a\u003e. Remember that Webpack loaders get executed from bottom to top.\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nl\"\u003etest\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"sr\"\u003e/\u003c/span\u003e\u003cspan class=\"se\"\u003e\\.\u003c/span\u003e\u003cspan class=\"sr\"\u003es\u003c/span\u003e\u003cspan class=\"se\"\u003e[\u003c/span\u003e\u003cspan class=\"sr\"\u003ec|a\u003c/span\u003e\u003cspan class=\"se\"\u003e]\u003c/span\u003e\u003cspan class=\"sr\"\u003ess$/\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003euse\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003eloader\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eMiniCssExtractPlugin\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eloader\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ecss-loader\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003eloader\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003epostcss-loader\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esass-loader\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003e\u003ca href=\"https://github.com/webpack-contrib/sass-loader\"\u003esass-loader\u003c/a\u003e is what compiles our Sass to CSS. It uses \u003ca href=\"https://github.com/sass/node-sass\"\u003enode-sass\u003c/a\u003e to do this, which we \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/7bae5211bf0c5a9dcc74d26ec4a07984f8c4919c/package.json#L44\"\u003einclude in our project dependencies\u003c/a\u003e. \u003ca href=\"https://github.com/postcss/postcss-loader\"\u003epostcss-loader\u003c/a\u003e then runs PostCSS, which in this case adds prefixes to the CSS rules. \u003ca href=\"https://github.com/webpack-contrib/css-loader\"\u003ecss-loader\u003c/a\u003e handles any imports and \u003ca href=\"https://github.com/webpack-contrib/mini-css-extract-plugin\"\u003eMiniCssExtractPlugin\u003c/a\u003e writes everything to a file.\u003c/p\u003e\n\n\u003ch3 id=\"transpiling-es6-javascript-to-es5\"\u003eTranspiling ES6 JavaScript to ES5\u003c/h3\u003e\n\u003cp\u003eAs I mentioned before, Babel takes care of converting the modern JavaScript code to it’s older equivalent. It too is \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/55c30bcbe007aa164fa84965dc6d3ebe06444d9d/webpack.prod.js#L87\"\u003econfigured using a function\u003c/a\u003e that returns an object.\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003econfigureBabel\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ebrowserList\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eloader\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ebabel-loader\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"na\"\u003epresets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[[\u003c/span\u003e\n        \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e@babel/preset-env\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n          \u003cspan class=\"na\"\u003emodules\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n          \u003cspan class=\"na\"\u003euseBuiltIns\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eusage\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n          \u003cspan class=\"na\"\u003etargets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003ebrowsers\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003ebrowserList\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e]],\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eplugins\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[[\u003c/span\u003e\n        \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e@babel/plugin-transform-runtime\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eregenerator\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e]]\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eFirst up, the function takes a list of browsers to support. We’ll supply the list from the \u003ca href=\"https://github.com/browserslist/browserslist\"\u003ebrowserlist\u003c/a\u003e option in \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/a4efd93d2ed30ea5b6842fe9618fc4e85d8fd6b3/package.json#L14\"\u003epackage.json\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eMore on that in a minute.\u003c/p\u003e\n\n\u003cp\u003eWe use \u003ca href=\"https://github.com/babel/babel-loader\"\u003ebabel-loader\u003c/a\u003e to make Babel and Webpack play nice together, and pass it some options to define how it should work. Babel has a number of \u003ca href=\"https://babeljs.io/docs/en/presets\"\u003epresets\u003c/a\u003e that let us easily define what Babel should do without having to write lengthy configs and figure out what plugins we need. In WordPress Boilerplate I’m using \u003ca href=\"https://babeljs.io/docs/en/babel-preset-env\"\u003e@babel/preset-env\u003c/a\u003e which lets us pass an array of targets and then it works out what needs to happen.\u003c/p\u003e\n\n\u003cp\u003eLet’s look at the options and what they’re doing.\u003c/p\u003e\n\n\u003cul\u003e\n  \u003cli\u003e\u003ca href=\"https://babeljs.io/docs/en/babel-preset-env#modules\"\u003emodules: false\u003c/a\u003e tells the preset not to transform modules to different types.\u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"https://babeljs.io/docs/en/babel-preset-env#modules\"\u003euseBuiltIns: ‘usage’\u003c/a\u003e tells Babel to include polyfills as required. Basically, if you use a JavaScript \u003ccode\u003ePromise\u003c/code\u003e it will include the corresponding polyfill. If we don’t it won’t.\u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"https://babeljs.io/docs/en/babel-preset-env#targets\"\u003etargets: { browsers: browserList }\u003c/a\u003e is the really cool part, in my opinion. It takes a \u003ccode\u003ebrowserlist\u003c/code\u003e query and decides what needs to be done to the JavaScript source to meet the requirements that have been defined. It’s seriously clever.\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003cp\u003eFinally, we add a single plugin: \u003ca href=\"https://babeljs.io/docs/en/babel-plugin-transform-runtime\"\u003ebabel-transform-runtime\u003c/a\u003e, which reduces file size by ensuring polyfills are only included once. It also helps avoids conflicts on browsers that don’t need the included polyfills at all.\u003c/p\u003e\n\n\u003ch3 id=\"manifest-files\"\u003eManifest Files\u003c/h3\u003e\n\u003cp\u003eWebpack can automatically generate Manifest files of all our assets for us. Using these, we can automatically bust caches by appending a hash to the names of the files we generate. Here’s a short example of how that works. Take this \u003ccode\u003emanifest.json\u003c/code\u003e file:\u003c/p\u003e\n\n\u003cdiv class=\"language-json highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"css/site.css\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"/wp-content/themes/wordpress-boilerplate/dist/css/site.5482fa8e.css\"\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eWhen we enqueue our CSS (or JavaScript etc.) we can query this file and use the latest files. I’ve written a \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/e41da6c2e3c42d877e134eba861c87491c737c71/functions/manifest.php#L16\"\u003ehandy PHP function\u003c/a\u003e for this. You can \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/e41da6c2e3c42d877e134eba861c87491c737c71/functions/enqueue.php#L33\"\u003esee it in use in the enqueues\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eSo, every time we enqueue site.css the site.5482fa8e.css file will be used and browser caching can do its job. When the file changes the hash will change. This gets updated automatically in the manifest file and the new file gets loaded by the browser.\u003c/p\u003e\n\n\u003cp\u003eThe manifest itself has only a \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate/blob/2fb8074ffe7d02a82d77218261555d1da5977c6b/webpack.prod.js#L110\"\u003elittle configuration\u003c/a\u003e going on.\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003econfigureManifest\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eincludes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e/\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ewebapp.html\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003efile\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n      \u003cspan class=\"kd\"\u003elet\u003c/span\u003e \u003cspan class=\"nx\"\u003eextension\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esplit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e.\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n      \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003emap\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"nx\"\u003eextension\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"nx\"\u003eextension\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ejs\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n      \u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eextension\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e/\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n      \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003efile\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eThe \u003ccode\u003emap\u003c/code\u003e option takes a callback. Essentially, without the callback the JavaScript, JavaScript source map and CSS (but not the CSS map, annoyingly) will not have a directory in their key:\u003c/p\u003e\n\n\u003cdiv class=\"language-json highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"style.css\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"/dist/css/style.css\"\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eThe function will add prepend the \u003ccode\u003ecss/\u003c/code\u003e directory to the key in the example above. It’ll work without it, too, but I like to keep things consistent.\u003c/p\u003e\n\n\u003ch3 id=\"so-many-favicons\"\u003eSo Many Favicons\u003c/h3\u003e\n\u003cp\u003eI may have exaggerated a touch earlier when I said millions of favicons are needed. This will actually generate 45 from one source image. \u003ca href=\"https://github.com/brunocodutra/webapp-webpack-plugin\"\u003eWebAppWebpackPlugin\u003c/a\u003e generates the favicons needed and \u003ca href=\"https://webpack.js.org/plugins/html-webpack-plugin/\"\u003eHtmlWebpackPlugin\u003c/a\u003e generates the HTML we need to include them.\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003econfigureWebApp\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ecache\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003efavicons\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eappName\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eAPP_NAME\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003edeveloperName\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eDEVELOPER_NAME\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003edeveloperURL\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eDEVELOPER_URL\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"na\"\u003einject\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eforce\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003elogo\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eLOGO_PATH\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eWe’re populating the values from the \u003ccode\u003e.env\u003c/code\u003e file. Once \u003ccode\u003eWebAppWebpackPlugin\u003c/code\u003e has done its job, HtmlWebpackPlugin generates the HTML for us.\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003econfigureHtmlWebpack\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003etemplateContent\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e''\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003efilename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ewebapp.html\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003einject\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eWe tell the plugin to produce a file called \u003ccode\u003ewebapp.html\u003c/code\u003e, which will be placed in the \u003ccode\u003e/dist\u003c/code\u003e folder. We can use it in our theme like so:\u003c/p\u003e\n\n\u003cdiv class=\"language-php highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"o\"\u003e!\u003c/span\u003e \u003cspan class=\"no\"\u003eWP_DEBUG\u003c/span\u003e \u003cspan class=\"k\"\u003eand\u003c/span\u003e \u003cspan class=\"nb\"\u003efile_exists\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"k\"\u003e__DIR__\u003c/span\u003e \u003cspan class=\"mf\"\u003e.\u003c/span\u003e \u003cspan class=\"s1\"\u003e'/dist/webapp.html'\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eecho\u003c/span\u003e \u003cspan class=\"nb\"\u003efile_get_contents\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e \u003cspan class=\"k\"\u003e__DIR__\u003c/span\u003e \u003cspan class=\"mf\"\u003e.\u003c/span\u003e \u003cspan class=\"s1\"\u003e'/dist/webapp.html'\u003c/span\u003e \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003ch3 id=\"all-the-small-things\"\u003eAll The Small Things\u003c/h3\u003e\n\u003cp\u003eCSS minification is taken care of by \u003ca href=\"https://github.com/NMFR/optimize-css-assets-webpack-plugin\"\u003eOptimizeCSSAssetsPlugin\u003c/a\u003e, and JavaScript is worked on by \u003ca href=\"https://github.com/webpack-contrib/terser-webpack-plugin\"\u003eTerser\u003c/a\u003e.\u003c/p\u003e\n\n\u003cdiv class=\"language-javascript highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003econfigureJavaScriptOptimisation\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ecache\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eparallel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003esourceMap\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eWe have Terser use parallel processes to build the JavaScript to significantly reduce the build time required.\u003c/p\u003e\n\n\u003ch2 id=\"mopping-up\"\u003eMopping Up\u003c/h2\u003e\n\u003cp\u003eWell, that turned into a bit of a deep dive. I’ve found this setup to be a good starting point for my WordPress themes. There are a few bits I want to add, the most notable being \u003ca href=\"https://www.smashingmagazine.com/2018/10/smart-bundling-legacy-code-browsers/\"\u003emodern and legacy builds\u003c/a\u003e and \u003ca href=\"https://webpack.js.org/guides/code-splitting/\"\u003ecode splitting\u003c/a\u003e. I’ll get those added in the future and write another post about them then.\u003c/p\u003e\n\n\u003cp\u003eRemember — \u003ca href=\"https://github.com/by-robots/wordpress-boilerplate\"\u003echeck out the GitHub repo\u003c/a\u003e for the most up-to-date release.\u003c/p\u003e\n","tags":["JavaScript","PHP","Webpack","WordPress"]}}