Package Exports
- @wordpress/build
- @wordpress/build/package.json
Readme
@wordpress/build
Build tool for WordPress plugins.
Description
@wordpress/build is an opinionated build system designed for WordPress plugins. It provides:
- Transpilation: Converts TypeScript/JSX source code to both CommonJS (
build/) and ESM (build-module/) formats using esbuild - Style Compilation: Processes SCSS files and CSS modules, generating LTR and RTL versions
- Bundling: Creates browser-ready bundles for WordPress scripts and modules
- PHP Generation: Automatically generates PHP registration files for scripts, modules, and styles
- Watch Mode: Incremental rebuilds during development
Installation
npm install @wordpress/build --save-devUsage
Production Build
wp-buildor via npm script:
{
"scripts": {
"build": "wp-build"
}
}Development Mode (Watch)
wp-build --watchor via npm script:
{
"scripts": {
"dev": "wp-build --watch"
}
}Package Configuration
Configure your package.json with the following optional fields:
wpScript
Set to true to bundle the package as a WordPress script/module:
{
"wpScript": true
}wpScriptModuleExports
Define script module entry points:
{
"wpScriptModuleExports": {
"./interactivity": "./build-module/interactivity/index.js"
}
}wpScriptDefaultExport
Handle default export wrapping:
{
"wpScriptDefaultExport": true
}wpScriptExtraDependencies
Additional script dependencies:
{
"wpScriptExtraDependencies": ["wp-polyfill"]
}wpStyleEntryPoints
Custom SCSS entry point patterns:
{
"wpStyleEntryPoints": {
"style": "src/style.scss"
}
}wpCopyFiles
Files to copy with optional PHP transformations:
{
"wpCopyFiles": [
{
"from": "src/index.php",
"to": "build/index.php",
"transform": "php"
}
]
}Root Configuration
Configure your root package.json with a wpPlugin object to control global namespace and externalization behavior:
wpPlugin.scriptGlobal
The global variable name for your packages (e.g., "wp", "myPlugin"). Set to false to disable global exposure:
{
"wpPlugin": {
"scriptGlobal": "myPlugin"
}
}wpPlugin.packageNamespace
The package scope to match for global exposure (without @ prefix). Only packages matching @{packageNamespace}/* will expose globals:
{
"wpPlugin": {
"scriptGlobal": "myPlugin",
"packageNamespace": "my-plugin"
}
}wpPlugin.handlePrefix
The prefix used for WordPress script handles in .asset.php files (e.g., wp-data, my-plugin-editor). Defaults to packageNamespace:
{
"wpPlugin": {
"scriptGlobal": "myPlugin",
"packageNamespace": "my-plugin",
"handlePrefix": "mp"
}
}With this configuration:
@my-plugin/editor→window.myPlugin.editorwith handlemp-editor@my-plugin/data→window.myPlugin.datawith handlemp-data
wpPlugin.externalNamespaces
Additional package namespaces to externalize (consume as externals, not expose). Each namespace must be an object with global and optional handlePrefix:
{
"wpPlugin": {
"externalNamespaces": {
"woo": {
"global": "woo",
"handlePrefix": "woocommerce"
},
"acme": {
"global": "acme",
"handlePrefix": "acme-plugin"
}
}
}
}This allows your packages to consume third-party dependencies as externals:
import { Cart } from '@woo/cart'→window.woo.cartwith handlewoocommerce-cartimport { Button } from '@acme/ui'→window.acme.uiwith handleacme-plugin-ui- Dependencies are tracked in
.asset.phpfiles
If handlePrefix is omitted, it defaults to the namespace key (e.g., "woo" → woo-cart).
wpPlugin.pages (Experimental)
Define admin pages that support routes. Each page gets generated PHP functions for route registration and can be extended by other plugins:
{
"wpPlugin": {
"pages": ["my-admin-page"]
}
}This generates two page modes:
build/pages/my-admin-page/page.php- Full-page mode (takes over entire admin screen with custom sidebar)build/pages/my-admin-page/page-wp-admin.php- WP-Admin mode (integrates within standard wp-admin interface)build/pages.php- Loader for all pages
Each mode provides route/menu registration functions and a render callback. Routes are automatically registered for both modes.
Registering a menu item for WP-Admin mode:
// Build URL with initial route via 'p' query parameter
$url = admin_url( 'admin.php?page=my-admin-page-wp-admin&p=' . urlencode( '/my/route' ) );
add_menu_page( 'Title', 'Menu', 'capability', $url, '', 'icon', 20 );Registering a menu item for full-page mode:
add_menu_page( 'Title', 'Menu', 'capability', 'my-admin-page', 'my_admin_page_render_page', 'icon', 20 );Example: WordPress Core (Gutenberg)
{
"wpPlugin": {
"scriptGlobal": "wp",
"packageNamespace": "wordpress"
}
}This configuration:
- Packages like
@wordpress/dataexposewindow.wp.data - Packages like
@wordpress/block-editorexposewindow.wp.blockEditor - All packages can consume
@wordpress/*as externals
Example: Third-Party Plugin
{
"wpPlugin": {
"scriptGlobal": "acme",
"packageNamespace": "acme"
}
}This configuration:
- Packages like
@acme/editorexposewindow.acme.editor - Packages like
@acme/dataexposewindow.acme.data - All packages can still consume
@wordpress/*→window.wp.* - All packages can still consume vendors (react, lodash) →
window.React,window.lodash
Behavior
- Packages with
wpScript: truematching the namespace: Bundled with global exposure - Packages with
wpScript: truenot matching the namespace: Bundled without global exposure - Dependencies:
@wordpress/*packages are always externalized towp.*globals - Vendors: React, lodash, jQuery, moment are always externalized to their standard globals
- Asset files:
.asset.phpfiles are always generated for WordPress dependency management
Output Structure
The built tool generates several files in the build/ directory, but the primary output is the PHP registration file.
Make sure to include the generated PHP file in your plugin file.
require_once plugin_dir_path( __FILE__ ) . 'build/index.php';Routes (Experimental)
Routes provide a file-based routing system for WordPress admin pages. Each route must be associated with a page defined in wpPlugin.pages (see above). Create a routes/ directory at your repository root with subdirectories for each route.
Structure
routes/
home/
package.json # Route configuration
stage.tsx # Main content component
inspector.tsx # Optional sidebar component
route.tsx # Optional lifecycle hooks (beforeLoad, loader)Route Configuration
In routes/{route-name}/package.json:
{
"route": {
"path": "/",
"page": "my-admin-page"
}
}The page field must match one of the pages defined in wpPlugin.pages in your root package.json. This tells the build system which page this route belongs to. It can also map to an existing page registered by another plugin.
Components
stage.tsx - Main content (required):
export const stage = () => <div>Content</div>;inspector.tsx - Sidebar content (optional):
export const inspector = () => <div>Inspector</div>;route.tsx - Lifecycle hooks (optional):
export const route = {
beforeLoad: ({ params, search }) => {
// Pre-navigation validation, auth checks
},
loader: ({ params, search }) => {
// Data preloading
}
};Build Output
The build system generates:
build/routes/{route-name}/content.js- Bundled stage/inspector componentsbuild/routes/{route-name}/route.js- Bundled lifecycle hooks (if present)build/routes/index.php- Route registry databuild/routes.php- Route registration logic
The boot package in Gutenberg will automatically use these routes and make them available.
Contributing to this package
This is an individual package that's part of the Gutenberg project. The project is organized as a monorepo. It's made up of multiple self-contained software packages, each with a specific purpose.
The packages in this monorepo are published to npm and used by WordPress as well as other software projects.
To find out more about contributing to this package or Gutenberg as a whole, please read the project's main contributor guide.
License
GPL-2.0-or-later © The WordPress Contributors