Skip to content

Add wp-build-polyfills package for WP < 7.0 compatibility#47367

Draft
dhasilva wants to merge 9 commits intotrunkfrom
fix/polyfill-wp-build-dependencies
Draft

Add wp-build-polyfills package for WP < 7.0 compatibility#47367
dhasilva wants to merge 9 commits intotrunkfrom
fix/polyfill-wp-build-dependencies

Conversation

@dhasilva
Copy link
Contributor

@dhasilva dhasilva commented Feb 26, 2026

Proposed changes:

  • Create a new wp-build-polyfills package that bundles WordPress Core packages (@wordpress/private-apis, @wordpress/theme, @wordpress/boot, @wordpress/route, @wordpress/a11y) as polyfills for WordPress < 7.0
  • The package includes a build script (build-polyfills) using esbuild that produces both classic scripts (IIFE) and script modules (ESM) with proper externals handling matching wp-build's strategy
  • A PHP class WP_Build_Polyfills conditionally registers the polyfill scripts/modules only when they are not already provided by Core or Gutenberg
  • Integrate the new package into the Forms package as a dependency — polyfills are built before wp-build runs and registered in the Forms dashboard

Other information:

  • Have you written new tests for your changes, if applicable?
  • Have you checked the E2E test CI results, and verified that your changes do not break them?
  • Have you tested your changes on WordPress.com, if applicable (if so, you'll see a generated comment below with a script to run)?

Jetpack product discussion

Does this pull request change what data or activity we track or use?

No.

Testing instructions:

  • Check out this branch and run pnpm install then pnpm jetpack build packages/forms
  • Verify that projects/packages/forms/build/polyfills/ is generated with both scripts/ and modules/ subdirectories containing bundled polyfills and .asset.php files
  • Activate the Forms package on a WordPress < 7.0 site and navigate to the Forms dashboard — verify the polyfill scripts are registered and the dashboard loads without errors
  • On a WordPress 7.0+ site, verify the polyfills are not registered (Core provides the packages natively)

Changelog

  • Generate changelog entries for this PR (using AI).

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!


Jetpack plugin:

The Jetpack plugin has different release cadences depending on the platform:

  • WordPress.com Simple releases happen as soon as you deploy your changes after merging this PR (PCYsg-Jjm-p2).
  • WoA releases happen weekly.
  • Releases to self-hosted sites happen monthly:
    • Scheduled release: March 3, 2026
    • Code freeze: March 3, 2026

If you have any questions about the release process, please ask in the #jetpack-releases channel on Slack.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack or WordPress.com Site Helper), and enable the fix/polyfill-wp-build-dependencies branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack fix/polyfill-wp-build-dependencies
bin/jetpack-downloader test jetpack-mu-wpcom-plugin fix/polyfill-wp-build-dependencies

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2

@dhasilva dhasilva self-assigned this Feb 26, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new wp-build-polyfills package to provide WordPress Core packages (@wordpress/private-apis, @wordpress/theme, @wordpress/boot, @wordpress/route, @wordpress/a11y) as polyfills for WordPress versions prior to 7.0. The package includes a custom esbuild-based build script that bundles these packages as both classic scripts (IIFE format) and script modules (ESM format), with externals handling that matches wp-build's strategy. The polyfills are conditionally registered only when not already provided by Core or Gutenberg, and are integrated into the Forms package as a build dependency.

Changes:

  • Created new wp-build-polyfills package with build script, PHP registration class, and package configuration
  • Integrated polyfills into Forms package by adding dependency and calling registration during dashboard initialization
  • Updated build scripts to generate polyfills before running wp-build

Reviewed changes

Copilot reviewed 9 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
projects/packages/wp-build-polyfills/src/class-wp-build-polyfills.php PHP class for conditional polyfill registration
projects/packages/wp-build-polyfills/package.json Package configuration with devDependencies and build script
projects/packages/wp-build-polyfills/composer.json Composer configuration for PHP package
projects/packages/wp-build-polyfills/bin/build-polyfills.mjs Custom esbuild script to bundle polyfills with proper externals
projects/packages/wp-build-polyfills/.gitignore Ignore node_modules
projects/packages/wp-build-polyfills/changelog/fix-polyfill-wp-build-dependencies Changelog entry for new package
projects/packages/forms/src/dashboard/class-dashboard.php Integration of polyfill registration in Forms dashboard
projects/packages/forms/package.json Added build-polyfills script and dependency
projects/packages/forms/composer.json Added wp-build-polyfills as PHP dependency
projects/packages/forms/changelog/fix-polyfill-wp-build-dependencies Changelog entry for Forms integration
pnpm-lock.yaml Lockfile updates for new dependencies
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)

projects/packages/wp-build-polyfills/src/class-wp-build-polyfills.php:26

  • The documentation for the $base_file parameter should clarify that it expects a reference to a PHP file within the plugin (typically __FILE__), not a constructed path. This would help prevent the misuse seen in the Forms integration. Consider updating the parameter description to: "File path for plugins_url() computation. Should be FILE or a reference to a PHP file in the plugin."
	 * @param string $base_file File path for plugins_url() computation.

projects/packages/forms/changelog/fix-polyfill-wp-build-dependencies:2

  • The changelog type is listed as "fixed" but this change is adding a new feature (polyfill support) rather than fixing an existing bug. Consider using "added" or "changed" as the type instead to more accurately reflect the nature of the change.
Type: fixed

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

$forms_root = dirname( __DIR__, 2 );
\Automattic\Jetpack\WP_Build_Polyfills\WP_Build_Polyfills::register(
$forms_root,
$forms_root . '/build/polyfills/modules/boot/index.min.js'
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second parameter to WP_Build_Polyfills::register() should be __FILE__ or a similar reference to the current PHP file, not a constructed path to a JavaScript file. The $base_file parameter is used with plugins_url() which expects a file path that's part of the plugin to compute the plugin's base URL. The constructed path $forms_root . '/build/polyfills/modules/boot/index.min.js' is incorrect and won't compute the correct URL.

According to the convention seen throughout the codebase, this should be __FILE__ so that plugins_url() can properly determine the plugin base directory and construct the correct URL.

Suggested change
$forms_root . '/build/polyfills/modules/boot/index.min.js'
__FILE__

Copilot uses AI. Check for mistakes.
@jp-launch-control
Copy link

jp-launch-control bot commented Feb 26, 2026

Code Coverage Summary

Coverage changed in 1 file.

File Coverage Δ% Δ Uncovered
projects/packages/forms/src/dashboard/class-dashboard.php 55/212 (25.94%) -0.76% 6 💔

Full summary · PHP report · JS report

If appropriate, add one of these labels to override the failing coverage check: Covered by non-unit tests Use to ignore the Code coverage requirement check when E2Es or other non-unit tests cover the code Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR I don't care about code coverage for this PR Use this label to ignore the check for insufficient code coveage.

@github-actions github-actions bot added the [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ label Feb 26, 2026
dhasilva and others added 2 commits February 26, 2026 21:16
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 27, 2026 00:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 17 changed files in this pull request and generated 2 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +39 to +45
add_action(
'wp_default_scripts',
function () use ( $polyfills_dir, $base_file ) {
self::register_modules( $polyfills_dir, $base_file );
},
20
);
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second add_action( 'wp_default_scripts', … ) callback is declared with no parameters, but wp_default_scripts passes the WP_Scripts instance as an argument. With the default accepted_args of 1, this will be invoked with 1 argument and can trigger an ArgumentCountError on PHP 8+. Fix by either accepting the $scripts parameter in the closure (even if unused) or by passing 0 as the accepted_args argument to add_action for this callback.

Copilot uses AI. Check for mistakes.
Comment on lines +126 to +129
wp_register_script_module(
$module_id,
plugins_url( 'build/polyfills/modules/' . $name . '/index.min.js', $base_file ),
$asset['module_dependencies'] ?? array(),
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

register_modules() passes only $asset['module_dependencies'] into wp_register_script_module(), but the generated asset file also contains a standard 'dependencies' list (e.g. classic script handles like wp-i18n, jquery, etc). Script modules in this codebase are enqueued with mixed dependency arrays (module IDs + classic script handles), so dropping 'dependencies' here can cause required classic scripts to not be loaded before the module executes. Consider merging $asset['dependencies'] and $asset['module_dependencies'] (or emitting a single dependencies array from the build step) and passing the combined list to wp_register_script_module().

Suggested change
wp_register_script_module(
$module_id,
plugins_url( 'build/polyfills/modules/' . $name . '/index.min.js', $base_file ),
$asset['module_dependencies'] ?? array(),
$dependencies = array_unique(
array_merge(
$asset['dependencies'] ?? array(),
$asset['module_dependencies'] ?? array()
)
);
wp_register_script_module(
$module_id,
plugins_url( 'build/polyfills/modules/' . $name . '/index.min.js', $base_file ),
$dependencies,

Copilot uses AI. Check for mistakes.
dhasilva and others added 2 commits February 26, 2026 21:57
The bin entry in package.json handles execution permissions via
npm/pnpm — no need for the git executable bit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bin/build-polyfills.mjs script is a Node.js dev build tool and
should not be shipped in the jetpack_vendor directory.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 27, 2026 01:35
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@dhasilva dhasilva added DO NOT MERGE don't merge it! labels Feb 27, 2026
@dhasilva dhasilva marked this pull request as draft February 27, 2026 02:13
@dhasilva dhasilva marked this pull request as draft February 27, 2026 02:13
@simison simison requested review from a team and youknowriad February 27, 2026 07:34
"build:dashboard": "webpack --config ./tools/webpack.config.dashboard.js",
"build:form-editor": "webpack --config ./tools/webpack.config.form-editor.js",
"build:wp-build": "wp-build",
"build:polyfills": "build-polyfills",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a good code pattern for the monorepo. jetpack build should already handle ordering of the builds to make sure the depended-on package is built before the dependent package.

Adding this here, and calling it from build:wp-build which is called from build which is called from the build-production and build-development scripts used by jetpack build, will result in the polyfill package getting built twice. Or it will once you set up the package correctly.

... Oh, you're building the scripts into this package, instead of having the wp-build-polyfills package contain the scripts as well as the PHP code to register them? That seems incredibly strange to me, why are you doing it that way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked that "it needs to to be a separate package, but the output must be used by Forms", so Claude came up with this solution. I'm testing a refactor to completely avoid this, but I rebased Gutenberg and now there is another bug there that needs to be tracked before I can verify Jetpack's side and update this PR. Will ping you again when possible.

import { build } from 'esbuild';

// Resolve packages from this package's own node_modules, not the consumer's.
const __dirname = path.dirname( fileURLToPath( import.meta.url ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently discovered that import.meta.dirname exists.

Suggested change
const __dirname = path.dirname( fileURLToPath( import.meta.url ) );
const __dirname = import.meta.dirname;


// Resolve packages from this package's own node_modules, not the consumer's.
const __dirname = path.dirname( fileURLToPath( import.meta.url ) );
const packageRoot = path.resolve( __dirname, '..' );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there and advantage to this over

Suggested change
const packageRoot = path.resolve( __dirname, '..' );
const packageRoot = path.dirname( __dirname );

?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wary that you seem to be trying to reinvent Webpack with this script. But you're missing a bunch of the stuff that our actual Webpack config does, like make sure i18n will still work with translate.wordpress.org.

} )
);

// Non-minified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a waste of space to build both a minified version with source maps and an unminified version (still with source maps).

Our normal setup does minified versus unminified based on whether it's run via jetpack build or jetpack build --production, and the latter is what gets published.

} )
);

// Non-minified
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

Comment on lines +31 to +45
add_action(
'wp_default_scripts',
function ( $scripts ) use ( $polyfills_dir, $base_file ) {
self::register_scripts( $scripts, $polyfills_dir, $base_file );
},
20
);

add_action(
'wp_default_scripts',
function () use ( $polyfills_dir, $base_file ) {
self::register_modules( $polyfills_dir, $base_file );
},
20
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these two separate functions for the same hook?

),
'wp-theme' => array(
'path' => 'theme',
'deps' => array( 'wp-element', 'wp-private-apis' ),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having deps here seems pointless. You don't register it at all if there's no .asset.php file, and that file should always contain dependencies.

};
} );

// Generate asset file on build end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, you're even reinventing this from scratch.

'wp-private-apis' => array(
'path' => 'private-apis',
'deps' => array(),
// Always replace: older Core versions ship private-apis with an
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we're running in a newer Core version, though?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

DO NOT MERGE don't merge it! [Feature] Contact Form [Package] Forms [Package] Wp Build Polyfills [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Status] Needs Review This PR is ready for review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants