{"id":271395,"date":"2018-05-24T07:22:09","date_gmt":"2018-05-24T14:22:09","guid":{"rendered":"http:\/\/css-tricks.com\/?p=271395"},"modified":"2019-09-17T10:42:39","modified_gmt":"2019-09-17T17:42:39","slug":"learning-gutenberg-6-setting-up-a-custom-webpack-config","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/learning-gutenberg-6-setting-up-a-custom-webpack-config\/","title":{"rendered":"Learning Gutenberg: Setting up a Custom webpack Config"},"content":{"rendered":"
Gutenberg introduces the modern JavaScript stack into the WordPress ecosystem, which means some new tooling should be learned. Although tools like create-guten-block<\/a> are incredibly useful, it\u2019s also handy to know what\u2019s going on under the hood.<\/p>\n <\/p>\n Let\u2019s jump in!<\/p>\n Webpack takes the small, modular aspects of your front-end codebase and smooshes them down into one efficient file. It\u2019s highly extendable and configurable and works as the beating heart of some of the most popular products and projects on the web. It\u2019s very much a JavaScript tool, although it can be used for pretty much whatever you want. For this tutorial, it\u2019s sole focus is JavaScript though.<\/p>\n What we\u2019re going to get webpack doing is watch for our changes on some custom block files and compile them with Babel to generate JavaScript files that can be read by most browsers. It\u2019ll also merge any dependencies that we import.<\/p>\n Hopefully you still have a local WordPress instance running from our primer in Part 2, but if not, you\u2019ll need to have one installed to continue with what we\u2019re about to do. In that install, navigate to Then, inside Don\u2019t forget the opening PHP tag, but you can leave the closing one off since we\u2019ll be adding more to this file soon enough.<\/p>\n WordPress looks for these comments to register a plugin in the same way it looks for comments at the top of style.css in a theme. This is an abbreviated version of what you\u2019ll find at the top of other plugins\u2019 main files. If you were planning to release it on the WordPress plugin repository, you\u2019d want to add a description and version number as well as license and author information. <\/p>\n The first thing we\u2019re going to do is initialize npm<\/a>. Run the following at the root<\/strong> of your plugin folder ( This will ask you a few questions about your project and ultimately generate you a Next, let\u2019s install webpack:<\/p>\n You might have noticed that we\u2019re installing webpack locally to our project. This is a good practice to get into with crucial packages that are prone to large, breaking changes. It also means you and your team are all singing off the same song sheet.<\/p>\n Then run this:<\/p>\n Then these Sass and CSS dependencies:<\/p>\n Now, NPX to allow us to use our local dependencies instead of any global ones:<\/p>\n Lastly, run this: <\/p>\n That will install the webpack CLI<\/a> for you.<\/p>\n Now that we have this installed, we should create our config file. Still in the root of your plugin, create a file called With this webpack file, we\u2019re going to be working with traditional ES5 code for maximum compatibility, because it runs with Node JS. You can use ES6 and Babel, but we\u2019ll keep things as simple as possible for this tutorial. <\/p>\n Alight, let\u2019s add some constants and imports. Add the following, right at the top of your The As you can see, we\u2019re requiring some dependencies. We know that webpack is needed, so we\u2019ll skip that. Let\u2019s instead focus our attention on Let\u2019s add some config now. Add the following after the What we\u2019ve done there is instantiate two instances of OK, after that last bit of code, add the following:<\/p>\n Here we\u2019ve got two arrays of plugins. Our Next up, add this SCSS config object:<\/p>\n This object is going to tell our webpack instance how to behave when it comes across Last up, the meat and taters of the config: <\/p>\n That\u2019s our entire config, so let\u2019s break it down.<\/p>\n The script starts with Next, let\u2019s look at some of these following properties:<\/p>\n Next is Lastly is our plugins section. Here, we pass our array of plugin instances. In our project, we have plugins that minify code, remove duplicate code and one that reduces the length of commonly used IDs. This is all to make sure our production code is optimized.<\/p>\n For reference, this is how your full config file should look: <\/p>\n Back in Drop this code below the opening comment in As the above comments indicate, we first register our script with WordPress using the handle Our final function, If you are familiar with theme development, you\u2019ve probably used Babel turns our ES6 code into better-supported ES5 code, so we need to install some dependencies. In the root of your plugin ( Update April 2019:<\/strong> We’ve had a lot of updates to this over the last many months because of the big change in Babel from 6 to 7. Thanks to Nina Regli, Rick Hughs, and Bryan Chong.<.p><\/p>\n Nina had luck with:<\/p>\n Rick had luck with:<\/p>\n Bryan had luck with:<\/p>\n Bryan also renamed `.babelrc` to `babel.config.js` and configured it like this:<\/p>\n That big ol\u2019 npm install adds all the Babel dependencies. Now we can add our While still in your theme folder, add the following file: Now open it up and paste the following: <\/p>\n Update April 2019:<\/strong> With Babel 7, you probably need So, what we\u2019ve got there are two things: <\/p>\n In the plugins, you\u2019ll notice that there\u2019s a React JSX transformer. That\u2019s sorting out our JSX and turning it into proper JavaScript, but what we\u2019re doing is telling it to generate WordPress elements, rather than React elements, which JSX is more commonly associated with.<\/p>\n The last thing we\u2019re going to do is generate some stub files and test that our webpack and WordPress setup is all good. <\/p>\n Go into your plugin directory and create a folder called Inside the Now that you’ve created those files, open up OK, so now we\u2019ve generated the minimum amount of things, let\u2019s run webpack. Open up your terminal and move into your current plugin folder\u2014then we can run the following, which will fire-up webpack:<\/p>\n Pretty dang straightforward, huh? If you go ahead and look in your A lot of setup has been done, but all of our ducks are in a row. We\u2019ve set up webpack, Babel and WordPress to all work as a team to build out or custom Gutenberg block (and future blocks). Hopefully now you feel more comfortable working with webpack and feel like you could dive in and make customizations to fit your projects.<\/p>\n Gutenberg introduces the modern JavaScript stack into the WordPress ecosystem, which means some new tooling should be learned. Although tools like create-guten-block are incredibly useful, it\u2019s also handy to know what\u2019s going on under the hood.<\/p>\n","protected":false},"author":251168,"featured_media":271396,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"c2c_always_allow_admin_comments":false,"footnotes":"","jetpack_publicize_message":"To develop for Gutenberg, we'll need to setup webpack. That's next in our ongoing series.","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":[]},"categories":[4],"tags":[1390,264],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/05\/gutenberg-6.jpg?fit=1200%2C600&ssl=1","jetpack-related-posts":[],"featured_media_src_url":"https:\/\/i0.wp.com\/css-tricks.com\/wp-content\/uploads\/2018\/05\/gutenberg-6.jpg?fit=1024%2C512&ssl=1","_links":{"self":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/271395"}],"collection":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/users\/251168"}],"replies":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/comments?post=271395"}],"version-history":[{"count":12,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/271395\/revisions"}],"predecessor-version":[{"id":296084,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/posts\/271395\/revisions\/296084"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media\/271396"}],"wp:attachment":[{"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/media?parent=271395"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/categories?post=271395"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/css-tricks.com\/wp-json\/wp\/v2\/tags?post=271395"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}Article Series:<\/h4>\n
\n
Getting started<\/h3>\n
Setting up a plugin<\/h3>\n
wp-content\/plugins<\/code> and create a fresh directory called
card-block<\/code> (spoiler alert: we\u2019re going to make a card block… who doesn\u2019t like cards?).<\/p>\n
card-block<\/code>, create a file called
card-block.php<\/code>. This will be the equivalent to
plugin.php<\/code> from create-guten-block. Next, drop in this chunk of comments to tell WordPress to acknowledge this directory as a plugin and display it in the Plugins page of the Dashboard:<\/p>\n
<?php\r\n \/*\r\n Plugin Name: Card Block\r\n *\/<\/code><\/pre>\n
Getting started with webpack<\/h3>\n
wp-content\/plugins\/card-block<\/code>):<\/p>\n
npm init<\/code><\/pre>\n
package.json<\/code> file, which lists dependencies and stores core information about your project. <\/p>\n
npm install webpack --save-dev<\/code><\/pre>\n
npm install extract-text-webpack-plugin@next --save-dev<\/code><\/pre>\n
npm install node-sass sass-loader css-loader --save-dev\r\n\r\n# J Parenti wrote in to say that in Babel 7, it's different. You may need to:\r\nnpm uninstall --save-dev sass-loader\r\nnpm install --save-dev sass-loader@7.1.0<\/code><\/pre>\n
npm install npx -g<\/code><\/pre>\n
npm install webpack-cli --save-dev<\/code><\/pre>\n
webpack.config.js<\/code> and open that file so we can get coding.<\/p>\n
webpack.config.js<\/code> file:<\/p>\n
var ExtractText = require('extract-text-webpack-plugin');\r\nvar debug = process.env.NODE_ENV !== 'production';\r\nvar webpack = require('webpack');<\/code><\/pre>\n
debug<\/code> var is declaring whether or not we\u2019re in
debug<\/code> mode. This is our default mode, but it can be overridden by prepending our webpack commands with
NODE_ENV=production<\/code>. The
debug<\/code> boolean flag will determine whether webpack generates sourcemaps and minifies the code.<\/p>\n
ExtractText<\/code>. Essentially,
ExtractText<\/code> enables us to pull in files other than JavaScript into the mix. We\u2019re going to need this for our Sass files. By default, webpack assumes everything is JavaScript, so
ExtractText<\/code> kind of *translates* the other types of files.<\/p>\n
webpack<\/code> definition:<\/p>\n
var extractEditorSCSS = new ExtractText({\r\n filename: '.\/blocks.editor.build.css'\r\n});\r\n\r\nvar extractBlockSCSS = new ExtractText({\r\n filename: '.\/blocks.style.build.css'\r\n});<\/code><\/pre>\n
ExtractText<\/code> by passing a config object. All that we\u2019ve set is the output of our two block stylesheets. We\u2019ll come to these in the next series, but right now all you need to know is that this is the first step in getting our Sass compiling.<\/p>\n
var plugins = [ extractEditorSCSS, extractBlockSCSS ];<\/code><\/pre>\n
ExtractText<\/code> instances live in the core plugins set and we\u2019ve got a couple of optimization plugins that are only smooshed into the core plugins set if we\u2019re not in
debug<\/code> mode. We\u2019re running that logic right at the end.<\/p>\n
var scssConfig = {\r\n use: [\r\n {\r\n loader: 'css-loader'\r\n },\r\n {\r\n loader: 'sass-loader',\r\n options: {\r\n outputStyle: 'compressed'\r\n }\r\n }\r\n ]\r\n};<\/code><\/pre>\n
scss<\/code> files. We\u2019ve got it in a config object to keep things as DRY<\/a> as possible.<\/p>\n
module.exports = {\r\n context: __dirname,\r\n devtool: debug ? 'inline-sourcemap' : null,\r\n mode: debug ? 'development' : 'production',\r\n entry: '.\/blocks\/src\/blocks.js',\r\n output: {\r\n path: __dirname + '\/blocks\/dist\/',\r\n filename: 'blocks.build.js'\r\n },\r\n module: {\r\n rules: [\r\n {\r\n test: \/\\.js$\/,\r\n exclude: \/node_modules\/,\r\n use: [\r\n {\r\n loader: 'babel-loader'\r\n }\r\n ]\r\n },\r\n {\r\n test: \/editor\\.scss$\/,\r\n exclude: \/node_modules\/,\r\n use: extractEditorSCSS.extract(scssConfig)\r\n },\r\n {\r\n test: \/style\\.scss$\/,\r\n exclude: \/node_modules\/,\r\n use: extractBlockSCSS.extract(scssConfig)\r\n }\r\n ]\r\n },\r\n plugins: plugins\r\n};<\/code><\/pre>\n
module.exports<\/code> which is essentially saying, “when you import or require me, this is what you\u2019re getting.”<\/em> We can determine what we expose to whatever imports our code, which means we could run code above
module.exports<\/code> that do some heavy calculations, for example.<\/p>\n
\n
context<\/code> is our base where paths will resolve from. We\u2019ve passed
__dirname<\/code>, which is the current working directory.<\/li>\n
devtool<\/code> is where we define what sort of sourcemap<\/a> we may or may not want. If we\u2019re not in
debug<\/code> mode, we pass
null<\/code> with a ternary operator.<\/li>\n
entry<\/code> is where we tell webpack to start its journey of pack-ery. In our instance, this is the path to our
blocks.js<\/code> file.<\/li>\n
output<\/code> is what it says on the tin. We\u2019re passing an object that defines the output path and the filename that we want to call it.<\/li>\n<\/ul>\n
module<\/code>, which we\u2019ll get into a touch more detail. The
module<\/code> section can contain multiple rules. In our instance, the only
rule<\/code> we have is looking for JavaScript and SCSS files. It\u2019s doing this by searching with a regular expression that\u2019s defined by the
test<\/code> property. The end-goal of the rule is to find the right sort of files and pass them into a loader, which is
babel-loader<\/code> in our case. As we learned in a previous tutorial in this series, Babel is what converts our modern ES6 code into more supported ES5 code. <\/p>\n
var ExtractText = require('extract-text-webpack-plugin');\r\nvar debug = process.env.NODE_ENV !== 'production';\r\nvar webpack = require('webpack');\r\n\r\nvar extractEditorSCSS = new ExtractText({\r\n filename: '.\/blocks.editor.build.css'\r\n});\r\n\r\nvar extractBlockSCSS = new ExtractText({\r\n filename: '.\/blocks.style.build.css'\r\n});\r\n\r\nvar plugins = [extractEditorSCSS, extractBlockSCSS];\r\n\r\nvar scssConfig = {\r\n use: [\r\n {\r\n loader: 'css-loader'\r\n },\r\n {\r\n loader: 'sass-loader',\r\n options: {\r\n outputStyle: 'compressed'\r\n }\r\n }\r\n ]\r\n};\r\n\r\nmodule.exports = {\r\n context: __dirname,\r\n devtool: debug ? 'inline-sourcemap' : null,\r\n mode: debug ? 'development' : 'production',\r\n entry: '.\/blocks\/src\/blocks.js',\r\n output: {\r\n path: __dirname + '\/blocks\/dist\/',\r\n filename: 'blocks.build.js'\r\n },\r\n module: {\r\n rules: [\r\n {\r\n test: \/\\.js$\/,\r\n exclude: \/node_modules\/,\r\n use: [\r\n {\r\n loader: 'babel-loader'\r\n }\r\n ]\r\n },\r\n {\r\n test: \/editor\\.scss$\/,\r\n exclude: \/node_modules\/,\r\n use: extractEditorSCSS.extract(scssConfig)\r\n },\r\n {\r\n test: \/style\\.scss$\/,\r\n exclude: \/node_modules\/,\r\n use: extractBlockSCSS.extract(scssConfig)\r\n }\r\n ]\r\n },\r\n plugins: plugins\r\n};<\/code><\/pre>\n
Registering our block<\/h3>\n
card-block.php<\/code>, our main task now is to enqueue the JavaScript and CSS files we will be building with webpack. In a theme, we would do this with call
wp_enqueue_script<\/code> and
wp_enqueue_style<\/code> inside an action added to
wp_enqueue_scripts<\/code>. We essentially do the same thing here, except instead we enqueue the scripts and styles with a function specific to blocks.<\/p>\n
card-block.php<\/code>:<\/p>\n
function my_register_gutenberg_card_block() {\r\n\r\n \/\/ Register our block script with WordPress\r\n wp_register_script(\r\n 'gutenberg-card-block',\r\n plugins_url('\/blocks\/dist\/blocks.build.js', __FILE__),\r\n array('wp-blocks', 'wp-element', 'wp-editor')\r\n );\r\n\r\n \/\/ Register our block's base CSS \r\n wp_register_style(\r\n 'gutenberg-card-block-style',\r\n plugins_url( '\/blocks\/dist\/blocks.style.build.css', __FILE__ ),\r\n array( 'wp-blocks' )\r\n );\r\n \r\n \/\/ Register our block's editor-specific CSS\r\n if( is_admin() ) :\r\n wp_register_style(\r\n 'gutenberg-card-block-edit-style',\r\n plugins_url('\/blocks\/dist\/blocks.editor.build.css', __FILE__),\r\n array( 'wp-edit-blocks' )\r\n );\r\n endif;\r\n \r\n \/\/ Enqueue the script in the editor\r\n register_block_type('card-block\/main', array(\r\n 'editor_script' => 'gutenberg-card-block',\r\n 'editor_style' => 'gutenberg-card-block-edit-style',\r\n 'style' => 'gutenberg-card-block-edit-style'\r\n ));\r\n}\r\n\r\nadd_action('init', 'my_register_gutenberg_card_block');<\/code><\/pre>\n
gutenberg-card-block<\/code> with two dependencies:
wp-blocks<\/code> and
wp-elements<\/code>. This function only registers<\/em> a script, it does not enqueue it. We do something similar for out edit and main stylesheets.<\/p>\n
register_block_type<\/code>, does the enqueuing for us. It also gives the block a name,
card-block\/main<\/code>, which identifies this block as the
main<\/code> block within the namespace
card-block<\/code>, then identifies the script and styles we just registered as the main editor script, editor stylesheet, and primary stylesheet for the block.<\/p>\n
get_template_directory()<\/code> to handle file paths in hooks like the ones above. For plugin development, we use the function
plugins_url()<\/code> which does pretty much the same thing, except instead of concatenating a path like this:
get_template_directory() . '\/script.js'<\/code>,
plugins_url()<\/code> accepts a string path as a parameter and does the concatenation for us. The second parameter,
_ FILE _<\/code>, is one of PHP\u2019s magic constants<\/a> that equates to the full file path of the current file.<\/p>\n
Getting Babel running<\/h3>\n
wp-content\/plugins\/card-block<\/code>), run the following: <\/p>\n
npm install babel-core babel-loader babel-plugin-add-module-exports babel-plugin-transform-react-jsx babel-preset-env --save-dev<\/code><\/pre>\n
npm install --save-dev @babel\/core @babel\/preset-env<\/code><\/p>\n
npm install @babel\/core babel-loader babel-plugin-add-module-exports babel-plugin-transform-react-jsx @babel\/preset-env --save-dev<\/code><\/p>\n
npm install --save-dev @babel\/preset-react babel-preset-minify @babel\/core @babel\/cli @babel\/preset-en<\/code><\/p>\n
module.exports = function (api) {\r\n return {\r\n presets: [\r\n [\r\n \"@babel\/preset-react\",\r\n {\r\n \"pragma\": \"wp.element.createElement\"\r\n }\r\n ],\r\n \"minify\",\r\n \"@babel\/env\"\r\n ]\r\n };\r\n}<\/code><\/pre>\n<\/div>\n
.babelrc<\/code> file which stores some settings for us. It prevents us from having to repeat them over and over in the command line.<\/p>\n
.babelrc<\/code>.<\/p>\n
{\r\n \"presets\": [\"env\"],\r\n \"plugins\": [\r\n [\"transform-react-jsx\", {\r\n \"pragma\": \"wp.element.createElement\"\r\n }]\r\n ]\r\n}<\/code><\/pre>\n
\"presets\": [\"@babel\/preset-env\"],<\/code><\/p>\n
\"presets\": [\"env\"]<\/code> is basically magic. It automatically determines which ES features to use to generate your ES5 code. We used to have to add different presets for all the different ES versions (e.g. ES2015), but it\u2019s been simplified.<\/p>\n
Generate stub files<\/h3>\n
blocks<\/code> and, within that, create two folders: one called
src<\/code> and one called
dist<\/code>.<\/p>\n
src<\/code> folder, create the following files. We\u2019ve added the paths, too, so you put them in the right place:<\/p>\n
\n
blocks.js<\/code><\/li>\n
common.scss<\/code><\/li>\n
block\/block.js<\/code><\/li>\n
block\/editor.scss<\/code><\/li>\n
block\/style.scss<\/code><\/li>\n<\/ul>\n
blocks.js<\/code> and add this one-liner and save:<\/p>\n
import '.\/block\/block';<\/code><\/pre>\n
npx webpack<\/code><\/pre>\n
dist<\/code> folder, you should see some compiled goodies in there! <\/p>\n
Wrapping up<\/h3>\n
\nArticle Series:<\/h4>\n
\n