How to include WordPress plugin assets with Webpack?

sage9
plugin
webpack

#1

So I’m just getting used to Webpack, and I got stuck at including plugin assets, specifically when it comes to images.

Let’s say there’s a WordPress plugin with assets like:

assets/images/image.png
assets/styles/styles.scss
assets/scripts/scripts.js

Including the styles and scripts is pretty simple, I could just use @import and import to include them, and they’d be compiled into my CSS ans JS files just fine. However when assets like images are being referenced, things will break, presumably because the relative path changes from the plugin styles directory to the themes styles directory, so one ends up with errors like:

error in ./resources/assets/styles/plugins.scss

Module build failed: ModuleNotFoundError: Module not found: Error: Can’t resolve ‘…/images/image.png’ in ‘…\resources\assets\styles’

I’m wondering if there’s some kind of built-in way of handling this?

Regards,
Timo


#2

Here’s how I did this:

In theme-name/resources/assets/config.json I added the following:

    "vendor": [
      "../../../../plugins/plugin-name/js/script1",
      "../../../../plugins/plugin-name/js/script2.js",
      "../../../../plugins/plugin-name/js/script3.js",
    ],

And in theme-name/app/setup.php I modified the following add_action:

/**
 * Theme assets
 */
add_action('wp_enqueue_scripts', function () {
    wp_enqueue_style('sage/main.css', asset_path('styles/main.css'), false, null);
    wp_enqueue_script('sage/main.js', asset_path('scripts/main.js'), ['jquery'], null, true);

    // Add Vendor scripts
    wp_enqueue_script('sage/vendor.js', asset_path('scripts/vendor.js'), ['jquery'], null, true);
}, 100);

Does this help get you going?


#3

That lead me to a solution, thanks!

Not sure if it’s the “correct” way of doing things, but it works and it looks half-way elegant… Including JS files that way worked out of the box (I guess that would change once it would use modules and stuff), but they aren’t the problem for now, but SCSS + images.

Trying to include SCSS files that way will cause an error because the loaders doesn’t include the path where these files live:

error in …/assets/styles/styles.scss

Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type.
| .vc_row_layouts,
| .vc_layout-panel-switcher {
| .vc-composer-icon {

@ multi …/assets/styles/styles.scss

So I’ve modified the include paths in webpack.config.js for the /\.scss$/ and /\.(ttf|eot|woff2?|png|jpe?g|gif|svg|ico)$/ rules:

include: [
	config.paths.assets,
	path.resolve(config.paths.root, "../../plugins")
],

Now the files are being compiled, the images are being copied, and the URLs are being rewritten, so that’s nice. The resulting directory structure in the dist folder however wasn’t overly pretty:

dist/_/_/plugins/plugin-name/assets/images/image.png

so I ended up creating a separate rule for the images instead:

{
  test: /\.(ttf|eot|woff2?|png|jpe?g|gif|svg|ico)$/,
  include: path.resolve(config.paths.root, "../../plugins"),
  loader: 'url',
  options: {
    limit: 4096,
    outputPath: 'vendor/plugins/',
    name: `[path]${assetsFilenames}.[ext]`,
    context: path.resolve(config.paths.root, "../../plugins"),
  },
},

Using a context that matches the include path results in a structure like:

dist/vendor/plugins/plugin-name/assets/images/image.png

Regards,
Timo


#4

I’m wondering if anyone has tried to build a system with webpack-merge where each plugin has their own webpack config that can be included from the theme of the site.

Something like this would make sense in a world where we want to build functionality in plugins but control the frontend world from the theme’s perspective. But this type of world would probably get “muddy” very quickly…at least that’s my first concern, but it’s intriguing none-the-less.