automattic / jetpack-wp-build-polyfills
Polyfills for WordPress Core packages not available or incomplete in older WP versions
Package info
github.com/Automattic/jetpack-wp-build-polyfills
Type:jetpack-library
pkg:composer/automattic/jetpack-wp-build-polyfills
Requires
- php: >=7.4
Requires (Dev)
- automattic/phpunit-select-config: ^1.0.11
- yoast/phpunit-polyfills: ^4.0.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-18 20:26:25 UTC
README
Polyfills for WordPress Core packages not yet available or complete in older WordPress versions.
This package conditionally registers @wordpress/* packages as both classic scripts (IIFE) and script modules (ESM) when they are not already provided by Core or Gutenberg.
It is intended to be used while Jetpack supports WordPress versions whose bundled packages are missing or incomplete. Revisit this package once Jetpack's minimum supported WordPress version reaches 7.1.
Problem
Plugins built with @wordpress/build depend on @wordpress/* packages newer than the ones WordPress 7.0, Jetpack's minimum, bundles. WordPress 7.0 ships no wp-views script or @wordpress/widget-primitives module, its wp-private-apis allowlist rejects newer dashboard packages such as @wordpress/views and @wordpress/widget-dashboard, and its wp-rich-text locks only useRichText into privateApis.
This package provides those missing or updated packages so that plugins using @wordpress/build can work across Jetpack's supported WordPress versions.
What it polyfills
Classic scripts (IIFE)
| Handle | Source package | Force-replaced? |
|---|---|---|
wp-notices |
@wordpress/notices |
Yes on WP < 7.0 — missing component exports |
wp-private-apis |
@wordpress/private-apis |
Yes on WP < 7.1 unless Gutenberg >= 23.5.0 is active — incomplete allowlist |
wp-rich-text |
@wordpress/rich-text |
Yes on WP < 7.1 unless Gutenberg >= 23.6.0 is active — incomplete privateApis |
wp-theme |
@wordpress/theme |
No — only registered if absent |
wp-views |
@wordpress/views |
No — only registered if absent |
wp-rich-text, wp-theme and wp-views require wp-private-apis: requesting any of them implicitly requests wp-private-apis too — see WP_Build_Polyfills::SCRIPT_DEPENDENCIES for why.
Script modules (ESM)
| Module ID | Source package |
|---|---|
@wordpress/boot |
@wordpress/boot |
@wordpress/route |
@wordpress/route |
@wordpress/a11y |
@wordpress/a11y |
@wordpress/widget-primitives |
@wordpress/widget-primitives |
Script modules use "first-wins" semantics — if Core or Gutenberg already registered the module, the polyfill is silently ignored. The exception is @wordpress/widget-primitives, which replaces the copy an active Gutenberg older than 23.9.0 registers.
How it works
WP_Build_Polyfills::register()hooks intowp_default_scriptsat priority 20, after Core and Gutenberg (both at the default priority 10) have registered their scripts.- For each polyfill, it checks whether a built asset file exists (
build/scripts/*/index.asset.phporbuild/modules/*/index.asset.php). - For classic scripts, it checks whether the handle is already registered. Scripts marked for force replacement are deregistered and re-registered with the polyfill version when the WordPress version is below the script's threshold and active Gutenberg is not known to provide a compatible implementation. Non-force scripts are skipped if already registered.
- For script modules, it calls
wp_register_script_module(), which ignores duplicates, except that@wordpress/widget-primitivesfirst deregisters the copy an active Gutenberg older than 23.9.0 registers.
wp-private-apis has an additional Gutenberg-version guard because the dashboard packages require a private-apis allowlist that includes @wordpress/widget-dashboard. Gutenberg 23.4.0 and older do not include that allowlist entry; Gutenberg 23.5.0 is expected to be the first active-Gutenberg version that matches the current @next package build used here.
Usage
Call register() early in your plugin, specifying a consumer name and the polyfills you need:
use Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills; WP_Build_Polyfills::register( 'my-plugin', array( 'wp-notices', 'wp-private-apis', '@wordpress/boot', '@wordpress/route', ) );
Available handles are listed in WP_Build_Polyfills::SCRIPT_HANDLES and WP_Build_Polyfills::MODULE_IDS.
Multiple plugins can call register() — the hook is only added once, and all requested polyfills are merged. You can inspect which consumers requested which polyfills via WP_Build_Polyfills::get_consumers().
The version threshold for force-replacements can be overridden with a third parameter:
WP_Build_Polyfills::register( 'my-plugin', array( 'wp-notices' ), '7.1' );
Admin frame
WP_Build_Admin_Frame reconciles the @wordpress/boot single-page layout with the wp-admin frame. On WordPress 7.0+ the boot module that runs is Core's bundled copy, so the fixes are applied from PHP around the page, and WP_Build_Polyfills::register() arms them:
- Backdrop color.
@wordpress/admin-uionly knows Core's color schemes and paints a near-black backdrop for WordPress.com and third-party ones. A script onin_admin_headersamples the#adminmenubackbackground into--wp-build-admin-menu-background, which a stylesheet onadmin_headapplies. - First paint. The same stylesheet paints the backdrop and a stage-shaped panel on the empty app container, so the page does not flash white before boot mounts.
- Cross-document view transitions. Where Core enables them, a render-blocking deferred script on
admin_headholds a wp-build page's first render until its admin menu has parsed, and boot's surfaces are unnamed while the page leaves so they do not zoom or slide over the next one.
It lives here because this package is the one runtime every wp-build page already loads, and it is temporary until wp-build or boot ship the same behavior.
Boot module asset file
Packages that use @wordpress/build to generate pages get a hardcoded reference to build/modules/boot/index.min.asset.php in the generated page templates. This file provides the classic script dependencies and version hash needed to bootstrap the page.
When @wordpress/build stops bundling the boot module (as planned in upcoming Gutenberg changes), this asset file will no longer be generated. This package builds its own boot module asset file, and ships a bin script (provide-boot-asset-file) that copies it to the expected location in the consumer's build directory.
Consuming packages should add @automattic/jetpack-wp-build-polyfills as a devDependency and call the script after wp-build:
"build:boot-proxy": "provide-boot-asset-file"
Safety checks
Two checks run after webpack (see the build script) so version skew in the bundled @wordpress/*
set fails the build instead of silently blanking a dashboard at runtime (as in Jetpack 16.0):
validate-boot-asset.js— every dependency handle in the boot module's.asset.phpis a known Core or polyfill handle (catches a script silently dropped for an unregistered dependency).validate-export-contract.js— every symbol a consumer (@wordpress/boot, …) imports from a polyfilled classic-script provider (@wordpress/theme,@wordpress/notices,@wordpress/private-apis,@wordpress/views) exists in that provider's shipped public API (catches the 16.0wp.theme.ThemeProvider is undefinedcase). Run standalone withpnpm run check-contracts. The ESM module providers (route,a11y) are a follow-up. Regression-tested intests/js/validate-export-contract.test.js.
Development
# Build polyfills (development) pnpm run build # Build polyfills (production) pnpm run build-production # Run PHP tests composer run test-php