Managing Javascript dependencies for custom Gutenberg blocks is tricky because most of the core packages such as components
or core-data
are made available by WordPress core in the window.wp
global variable. But what happens if you want to use these packages outside of the post editor screen or when they change between versions of WordPress?
Below we’ll look at the available tooling and workflows for generating script bundles that either include or exclude references to the JS packages included with the version of WordPress running your block plugin.
It assumes that your source code is written in modern JS which is passed through a build step that transpiles it into something that is understood by the majority of the browsers, and helps with keeping the list of core JS package dependencies updated when enqueuing the script.
1. Define Dependencies
Define all @wordpress/...
packages used by your blocks as regular NPM dependencies to your project. So npm install @wordpress/...
for every package that you need.
2. Use Dependencies
Pull in the installed dependencies using the standard import { something } from "@wordpress/..."
syntax in your block source code. This will also ensure that your code editor is able to autocomplete exports and APIs provided by these packages.
3. Collect Dependencies
Use the wp-scripts build
command to generate index.assets.php
which returns an array of all @wordpress/...
dependencies (known to be bundled with the WordPress core) found in your block Javascript code. It looks something like this:
Behind the scenes it uses the @wordpress/dependency-extraction-webpack-plugin
package to discover and collect all known dependencies bundled with the WordPress core in your source code during the Javascript transpilation step. For example, it assumes that packages such as lodash
, moment
and those starting with @wordpress/...
are included in WordPress core and can be pulled in using known script handles.
Now, when calling wp_enqueue_script()
for your script bundle build/index.js
you can use the generated index.assets.php
file to define the necessary WP core dependencies and your script revision:
Each of the defined bundle entry points will generate a matching *.assets.php
file next to the generated *.js
file. By default it looks for src/index.js
and generates both build/index.js
and build/index.assets.php
.
To define custom entry points, pass a list of entry files to the build command along with the destination directory:
which will generate dist/one.js
with a matching dist/one.assets.php
and dist/two.js
with a matching dist/two.assets.php
.
Fun fact: here is the pull request that introduced this feature.
4. Transpile and Bundle
The same wp-scripts build
step also transpiles your modern source JS into browser compatible JS according to the browser compatibility rules defined in the @wordpress/scripts
Babel configuration along with all the browser polyfills.
By default it replaces all known @wordpress/...
dependencies with references to global window.wp.*
objects using the @wordpress/dependency-extraction-webpack-plugin
package mentioned previously.
To keep all dependencies in the output bundle and prevent them from being rewritten with references to the global window.wp.*
references pass the --webpack-no-externals
flag to the wp-scripts build
command or set the WP_NO_EXTERNALS
environment variable to 1
or true
.
This allows you to generate bundles that include the exact versions of packages defined in the package.json
of your project instead of relying on the ones shipped with the version of WordPress that is running your block plugin. Here is a sample package.json
config that does that:
which produces the following output:
npm run build
createsbuild/example.js
and a matchingbuild/example.assets.php
whilenpm run build-with-externals
creates onlybuild-with-externals/example-with-externals.js
and no*.assets.php
file because all of the dependencies are included in the bundle!
Conclusion
The @wordpress/scripts
tooling provides a lot of functionality for working with WordPress-specific and external dependencies in a reliable and consistent way while giving developers the option to configure the generated JS to their project needs.