Dealing with WebPack is no picnic, and the problem only worsens the bigger or more complicated your project gets. Say you want to add SCSS compiling, or TypeScript, or SVG/imports imports in your CSS, or CSS modules — the list goes on — now you’ve not only got a ton of things you have to configure properly, you have a bunch of npm dependencies to manage.
What if I told you that @wordpress/scripts does all that FOR you?
And with no WebPack config (unless you want it, and perhaps in a future post, I can show you how to extend the built-in @wordpress/scripts WebPack).
Let me tell you how.
๐ฆ Step 1: Install @wordpress/scripts
In your plugin or theme directory:
npm init -y
npm install @wordpress/scripts --save-dev
๐ ๏ธ Step 2: Configure package.jso
n
Add custom scripts:
"scripts": {
"build": "wp-scripts build",
"watch": "wp-scripts start",
"watch:hot": "wp-scripts start --hot"
}
Optionally, you can specify specific JavaScript files to compile, or even a custom output path:
"scripts": {
"build": "wp-scripts build assets/src/one.js assets/src/two.js --output-path=assets/dist/",
"watch": "wp-scripts start assets/src/one.js assets/src/two.js --output-path=assets/dist/",
"watch:hot": "wp-scripts start assets/src/one.js assets/src/two.js --output-path=assets/dist/ --hot"
}
๐ Step 3: Set Up Your Project Structure
assets/
src/
index.js (or index.ts)
style.scss
build/
index.js
index.asset.php
style.css
Inside index.js
, import styles directly — this will make it so we don’t have to tell the wp-scripts handler to compile them individually:
import './style.scss';
โ๏ธ Step 4: Compile Assets
Build assets for production:
npm run build
For development (watch mode):
npm run watch
For hot module replacement (HMR) mode:
npm run watch:hot
This will output the following files:
build/index.js
build/index.asset.php
build/style.css
๐งฉ Step 5: Register and Enqueue in PHP
Notice the build/index.asset.php
? The reason that’s there is so we can auto-register it so we can enqueue it wherever we need it.
Here’s a generalized function that will handle auto-registration for you (NOTE: you should customize the my-plugin parts to fit your plugin/theme so it doesn’t get confused with other plugins or themes):
function register_assets() {
$asset_root = plugin_dir_path( __FILE__ ) . 'assets/build/';
$asset_uri = plugin_dir_url( __FILE__ ) . 'assets/build/';
$asset_files = glob( $asset_root . '*.asset.php' );
// Load runtime if present (used by webpack for chunking).
if ( true === is_readable( $asset_root . 'runtime.js' ) ) {
enqueue_script(
'my-plugin/runtime',
$asset_uri . 'runtime.js',
array(),
filemtime( $asset_root . 'runtime.js' )
);
}
foreach ( $asset_files as $file ) {
$script_meta = require $file;
$slug = basename( $file, '.asset.php' );
$handle = "my-plugin/{$slug}";
$js_path = $asset_root . "{$slug}.js";
$js_uri = $asset_uri . "{$slug}.js";
$css_path = $asset_root . "{$slug}.css";
$css_uri = $asset_uri . "{$slug}.css";
if ( true === is_readable( $css_path ) ) {
wp_register_style(
$handle,
$css_uri,
array_filter( $script_meta['dependencies'], 'wp_style_is' ),
$script_meta['version']
);
}
if ( true === is_readable( $js_path ) ) {
wp_register_script(
$handle,
$js_uri,
array_filter( $script_meta['dependencies'], 'wp_script_is' ),
$script_meta['version'],
array( 'in_footer' => true )
);
}
}
}
add_action( 'init', 'register_assets' );
And now you can easily enqueue the scripts where you need them, like this:
function enqueue_my_scripts() {
wp_enqueue_script( 'my-plugin/index' ); // NOTE: 'index' comes from index.asset.php.
wp_enqueue_style( 'my-plugin/index' );
}
add_action( 'wp_enqueue_scripts', 'enqueue_my_scripts' );
๐งผ Bonus: Keep It Clean
Add the following to .gitignore
so it does not get included in your source code (unless you want it to be!).
assets/build/
You can build before deployment or commit the built assets as needed.