Building a JavaScript-driven UI for AutomateWoo – Part 1 – Setting up @wordpress/scripts and webpack

In this series of posts, I’m going to share my experience building JavaScript-driven UIs for AutomateWoo using components from WooCommerce Admin and Gutenberg. The aim for these posts is to be of practical use for other WooCommerce extension developers, and they will include step by step instructions based on what we’ve done for AutomateWoo.

To provide a little background, AutomateWoo is a five-year-old WooCommerce extension with a UI built primarily with PHP. I think it’s also worth mentioning that I come from a primarily WordPress/PHP background and have had a fairly small amount of past JavaScript experience.

I have to admit that a couple of years ago when I first heard that the WooCommerce admin interface was going to become JavaScript-based, I was a bit worried. Rebuilding a large extension like AutomateWoo seemed like a huge task and that’s still true. However, I now see the benefits are completely worth it and the paradigm shift is vital for the future of WooCommerce!

So let’s get started! In this post I’ll cover:

  • Setting up @wordpress/scripts
  • Customizing the default webpack config
  • Bundling SASS and CSS
  • Linting JavaScript files
  • Loading the new JavaScript and CSS files

Setting up @wordpress/scripts

With AutomateWoo we have been using Gulp to minify JS files and perform various tasks like generating translation files and building plugin ZIP files. We still use Gulp for several things but have chosen to use the @wordpress/scripts package to handle our newer JS files. @wordpress/scripts comes with a collection of useful tools for JavaScript WordPress development. For example, I wanted to use the cool new ES6 and JSX syntaxes which @wordpress/scripts compiles automatically to browser-compatible JavaScript.

  1. Install npm
  2. In your plugin directory run npm init to create a package.json file
  3. Run npm install @wordpress/scripts --save-dev
  4. Open package.json and update the scripts property to:
{
    "scripts": {
        "build": "wp-scripts build",
        "start": "wp-scripts start",
    }
}
  1. Create src/index.js
  2. Run npm run build

Now you should have a build/ directory containing a compiled index.js file and an index.asset.php file. You can also run npm run start which will build, and then watch, the files in src/ for changes.

Customizing the default webpack config

The build and start scripts use webpack behind the scenes and @wordpress/scripts has a useful default webpack config that can be customized.

For AutomateWoo, I didn’t want the src/ and build/ directories in the plugin’s root directory. To change them create a webpack.config.js file in your plugin directory with the following:

// Load the default @wordpress/scripts config object
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
// Use the defaultConfig but replace the entry and output properties
module.exports = {
    ...defaultConfig,
    entry: {
        index: './assets/src/index.js',
    },
    output: {
        filename: '[name].js',
        path: __dirname + '/assets/build',
    },
};

Now move src/index.js to assets/src/index.js and after running npm run build the built files will be added to the assets/build/ directory.

Bundling SASS and CSS with webpack

@wordpress/scripts can bundle .scss and .css files out of the box making it possible to import styles from JavaScript files. This makes it easy to keep a component’s JavaScript and CSS files in the same directory which I’ve found to be a nice pattern. For example:

./assets/src/components/
|── button/
   │── index.js
   |── style.scss
// assets/src/components/button/index.js
import './style.scss'
const button = ( props ) => {
    // ...
};
export default button;

Linting JavaScript files

Linting is another great feature of @wordpress/scripts and helps enforce code style guidelines for your plugin’s JavaScript files. @wordpress/scripts uses ESLint under the hood and comes with a preconfigured set of recommended rules for WordPress development. For WooCommerce development I’d recommend using the @woocommerce/eslint-plugin package which extends the WordPress defaults and adds some helpful additional rules.

  1. Open package.json and add a new entry to the scripts property:
{
    "scripts": {
        "lint:js": "wp-scripts lint-js assets/src",
    }
}

2. Run npm install @woocommerce/eslint-plugin --save-dev

3. Create a .eslintrc.js file in your plugin directory with the following:

module.exports = {
    extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
};

4. Now running npm run lint:js will check the files in assets/src/ for code style issues.

If an ESLint integration is available with your IDE, I’d recommend enabling it as I found the warnings generated when running npm run lint:js could often be hard to understand.

With PHPStorm (my IDE) I simply had to tick a box in settings to enable ESLint support.

Loading the new JavaScript and CSS files

Now it’s time to load the new JavaScript and CSS files from the build/ directory in the WordPress admin area. We can use the familiar wp_enqueue_script() and wp_enqueue_style() functions for this. However, there is one special thing about @wordpress/scripts that’s worth noting. Remember the index.asset.php file from earlier? This file is generated each time your JavaScript is built and returns an array containing:

  • dependencies: an array of all JavaScript dependencies e.g. [ wp-components ]
  • version: a generated string that changes for each build

This makes our loader slightly more complex but means we will never need to manually add dependencies. Here’s an example loader for our new JavaScript-driven WooCommerce plugin:

<?php
namespace DanBitzer/MyPlugin/Admin;
use DanBitzer/MyPlugin/PluginInfo;
/**
 * Class AssetLoader.
 *
 * Loads JavaScript and CSS files.
 */
class AssetLoader {
    /**
     * @var PluginInfo
     */
    protected $plugin_info;
    /**
     * AssetLoader constructor.
     *
     * @param PluginInfo $plugin_info
     */
    public function __construct( PluginInfo $plugin_info ) {
        $this->plugin_info = $plugin_info;
        add_action( 'admin_enqueue_scripts', [ $this, 'handle_admin_enqueue_scripts_action' ] );
    }
    /**
     * Register asset loader hooks.
     */
    public function handle_admin_enqueue_scripts_action() {
        // Load assets if on an WooCommerce Admin page
        if ( function_exists( 'wc_admin_is_registered_page' ) && wc_admin_is_registered_page() ) {
            $this->load_js_and_css();
        }
    }
    /**
     * Load JS and CSS assets.
     */
    protected function load_js_and_css() {
        $handle            = 'dan-bitzer-my-plugin';
        $build_path        = '/assets/build';
        $script_asset_path = $this->plugin_info->get_path() . $build_path . '/index.asset.php';
        $script_info       = file_exists( $script_asset_path )
            ? include $script_asset_path
            : [ 'dependencies' => [], 'version' => $this->plugin_info::VERSION ];
        wp_register_script(
            $handle,
            $this->plugin_info->get_url() . $build_path . '/index.js',
            $script_info['dependencies'],
            $script_info['version'],
            true
        );
        wp_register_style(
            $handle,
            $this->plugin_info->get_url() . $build_path . '/index.css',
            // Add the WooCommerce Admin styles as a dependency
            [ 'wc-admin-app' ],
            $this->plugin_info::VERSION
        );
        wp_enqueue_script( $handle );
        wp_enqueue_style( $handle );
    }
}

Check out the full example plugin in part one to see how it all fits together.

That’s all, Thanks for reading!


4 responses to “Building a JavaScript-driven UI for AutomateWoo – Part 1 – Setting up @wordpress/scripts and webpack”

  1. Excellent post Dan. Thank you for sharing! Could you also shed some more light on:

    What benefits you saw of moving to JS from PHP.
    And why the paradigm shift is vital for the future of WooCommerce.

    1. Daniel Bitzer Avatar
      Daniel Bitzer

      Thanks Pokhi!

      Could you also shed some more light on. What benefits you saw of moving to JS from PHP. And why the paradigm shift is vital for the future of WooCommerce.

      Sure thing! I think users (store owners especially) expect it to be fast and easy to manage their store. If WooCommerce is too hard to use people will probably move to another platform. I think a modern JS framework, like React, is currently the best way to build a great UI/UX on ther web.

      1. I’m starting to use React, not so much for UX but I’m keen on using the concept of “state”. For the Woo Admin React interfaces I’ve used so far, one gripe is load time – they are noticeably slower than the PHP pages in WP Admin. Whilst the pages with overviews in PHP load swiftly (orders, products etc), the reports & the customers pages are slow. So for UX I’m unsure if this is really an improvement. Is this just teething problems, or do we need to get used to a slower admin?

  2. Nice work. Curious for part two…!

Leave a Reply

Your email address will not be published. Required fields are marked *