Skip to content

wp-build: strip unminified bundles from production builds#48729

Open
dhasilva wants to merge 2 commits into
trunkfrom
update/wp-build-polyfills-strip-unminified
Open

wp-build: strip unminified bundles from production builds#48729
dhasilva wants to merge 2 commits into
trunkfrom
update/wp-build-polyfills-strip-unminified

Conversation

@dhasilva
Copy link
Copy Markdown
Contributor

@dhasilva dhasilva commented May 12, 2026

Fixes # N/A

Proposed changes

  • New shared bin strip-unminified-prod in @automattic/jetpack-wp-build-polyfills. It deletes paired .js/.css siblings of .min.js/.min.css (plus any paired .js.map) under build/{routes,scripts,modules,styles}/**, and rewrites the generated routes.php / scripts.php / modules.php / styles.php loaders so the SCRIPT_DEBUG ternary collapses to the minified asset. The PHP rewrite keeps the asset reachable even when a deploy target sets SCRIPT_DEBUG=true, so loaders never 404.
  • Wired into the build-production pipeline of every wp-build consumer: newsletter, podcast, forms, backup, scan, videopress. The non-production build script and pnpm watch are unchanged — local developers still have both bundles available for SCRIPT_DEBUG debugging.
  • Videopress also got its composer.json build-production normalised to delegate to pnpm run build-production, matching the other five packages.
  • The script is idempotent (re-runs are silent no-ops) and includes a defensive guard: if @wordpress/build ever changes the shape of the SCRIPT_DEBUG ternary (in either the $extension or the $suffix form), the script throws instead of silently shipping a loader that 404s under SCRIPT_DEBUG. Logs a one-line skip notice if build/ is missing entirely.
  • Node --test coverage for the bin (8 tests): fixture-based deletion and PHP rewrite assertions for all four loader shapes, idempotency, and the safety-net throw for both unrecognised ternary forms.

Caveat about SCRIPT_DEBUG-on-production debugging: with this PR, a deploy target running with SCRIPT_DEBUG=true gets the minified bundle instead of the unminified one. Devtools sourcemaps still resolve names, but in-page stack traces and view-source are no longer readable. This is the small tradeoff in exchange for cutting ~10 MiB of dead bytes from every production mirror.

Measured savings (each branch built end-to-end via composer run-script build-production per package; du -sb on build/):

Package Trunk This branch Delta
newsletter 8.46 MiB 4.54 MiB −3.92 MiB
podcast 3.90 MiB 1.16 MiB −2.73 MiB
forms 5.51 MiB 1.80 MiB −3.71 MiB
backup 0.45 MiB 0.44 MiB −0.00 MiB (no real wp-build route yet)
scan 2.25 MiB 0.75 MiB −1.50 MiB
videopress 2.99 MiB 1.57 MiB −1.42 MiB
Total 23.55 MiB 10.27 MiB −13.28 MiB (−56.4%)

Premium-analytics was intentionally left out of this PR.

Related product discussion/links

This is the "Recommendation A" follow-up to the internal production-size investigation. Companion PR #48627 already addresses the cross-plugin jetpack-newsletter duplication (Recommendation B in the same report).

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

No. Build-only change; no runtime behaviour changes for end users.

Testing instructions

The bug this PR fixes is only visible at production-build time, so the tests are build-side. The end-to-end fix is verified by the size-delta table above; you can repeat it locally for any wp-build package:

  1. Check out trunk, then run for one of the wp-build packages:
    cd projects/packages/newsletter   # or podcast / forms / scan / videopress / backup
    composer run-script build-production
    du -sh build/
    Note the size and confirm build/routes/*/content.js and build/routes/*/route.js are present.
  2. Switch to this branch, pnpm install (the new bin is a workspace symlink), and re-run the same commands. Confirm:
    • build/ is noticeably smaller (see table above for the expected delta).
    • Unminified .js/.css siblings under build/{routes,scripts,modules,styles}/** are gone; their .min.js/.min.css counterparts remain.
    • build/routes.php, build/scripts.php, build/modules.php now use $extension = '.min.js'; instead of the SCRIPT_DEBUG ? '.js' : '.min.js' ternary.
    • build/styles.php now uses $suffix = '.min'; instead of SCRIPT_DEBUG ? '' : '.min'.
  3. Verify that running composer run-script build-production a second time without clean is a silent no-op (strip-unminified-prod is idempotent).
  4. Run the unit tests for the bin:
    cd projects/packages/wp-build-polyfills
    node --test 'tests/js/**/*.test.js'
    All 17 tests should pass (8 new + 9 existing).
  5. Smoke-test one of the modernised dashboards in wp-admin (e.g. Newsletter subscribers, VideoPress overview, Scan, Forms responses) with SCRIPT_DEBUG both false and true. Both should load and render correctly — under SCRIPT_DEBUG=true the loader now falls back to the minified bundle (no 404s).
  6. (Optional) Local dev sanity: pnpm --filter @automattic/jetpack-newsletter run watch should still produce both content.js and content.min.js and not invoke the strip step. The strip step only runs in build-production.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 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 update/wp-build-polyfills-strip-unminified branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack update/wp-build-polyfills-strip-unminified
bin/jetpack-downloader test jetpack-mu-wpcom-plugin update/wp-build-polyfills-strip-unminified

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 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: June 2, 2026
    • Code freeze: June 1, 2026

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


Videopress plugin:

No scheduled milestone found for this plugin.

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

@dhasilva dhasilva force-pushed the update/wp-build-polyfills-strip-unminified branch from cdc0fe3 to 3b0b77c Compare May 12, 2026 17:06
@github-actions github-actions Bot added [Plugin] Jetpack Issues about the Jetpack plugin. https://wordpress.org/plugins/jetpack/ [Plugin] VideoPress A standalone plugin to add high-quality VideoPress videos to your site. labels May 12, 2026
@dhasilva dhasilva force-pushed the update/wp-build-polyfills-strip-unminified branch from 3b0b77c to 68c650f Compare May 12, 2026 17:09
@dhasilva dhasilva added [Status] Needs Review This PR is ready for review. and removed [Status] In Progress labels May 12, 2026
@dhasilva dhasilva requested a review from a team May 12, 2026 17:13
@jp-launch-control
Copy link
Copy Markdown

jp-launch-control Bot commented May 12, 2026

Code Coverage Summary

This PR did not change code coverage!

That could be good or bad, depending on the situation. Everything covered before, and still is? Great! Nothing was covered before? Not so great. 🤷

Full summary · PHP report · JS report

@dhasilva dhasilva requested a review from tbradsha May 12, 2026 17:37
@tbradsha tbradsha requested a review from a team May 12, 2026 17:44
@CGastrell
Copy link
Copy Markdown
Contributor

Took this for a careful read — really like the shape of it. The defensive UNMATCHED_SUSPICIOUS_PATTERN throw is exactly the right kind of paranoia for a script that mutates generated PHP; future-you (or future-me, when wp-build drifts) will thank present-you for the clear error message that names the file to update. Idempotency is genuine too — traced both passes and the second run is a true no-op, not just a "happens to work" no-op. And the regexes hold up: I ran them mentally against the real routes.php / scripts.php / modules.php in newsletter, forms, videopress, scan, and backup, including the multi-line modules.php ternary, and they match cleanly every time.

One thing worth a second look before merge, and a couple of smaller nits:

styles.php is in the same family but isn't covered. @wordpress/build also emits a build/styles.php with the SCRIPT_DEBUG-driven shape $suffix = SCRIPT_DEBUG ? '' : '.min'; (e.g. projects/packages/newsletter/build/styles.php). The bin doesn't patch this loader, and the suspicious-pattern guard is keyed to the .js ternary so it won't catch the suffix-style drift either. Today it's dormant — every package's build/styles/registry.php returns an empty array so nothing's enqueued — but the moment someone lands a wpStyles entry, a SCRIPT_DEBUG=true deploy would request a non-min CSS that the bin already stripped (well, would strip if it walked build/styles/ — which it also doesn't today). Two clean options:

  1. Extend the bin to walk build/styles/ and rewrite the styles.php suffix the same way.
  2. Just widen UNMATCHED_SUSPICIOUS_PATTERN to also catch the suffix shape so an empty-→-populated transition trips the safety net loudly instead of 404'ing.

Either works — (2) is a smaller diff if you want to defer the styles handling to a follow-up.

Tests would buy a lot of confidence here. tests/js/validate-boot-asset.test.js is the obvious template — a fixture that synthesizes a tiny build/ with one route, runs the bin, asserts the deletions and the PHP rewrite, then re-runs and asserts no-op. Since the safety properties hinge entirely on the two regexes matching real wp-build output, a test would catch template drift one PR earlier than the build itself does. Not a blocker, but cheap insurance.

Tiny nits, take or leave:

  • The "strict win" line in the body is almost true — but anyone debugging a production issue via SCRIPT_DEBUG=true today gets the unminified bundle, which makes in-page stack traces and view-source readable. After this they get the minified bundle in both modes; sourcemaps still resolve names in devtools, but the readability tradeoff exists. Worth a one-line acknowledgement so future-readers aren't surprised.
  • When build/ is absent entirely the bin exits silently. A one-liner like [strip-unminified-prod] no build/ at <cwd>, skipping would make accidental misconfigurations observable. Minor.

One question: projects/packages/premium-analytics also has build/{routes,scripts,modules}.php files in the tree but isn't in the list of consumers this PR wires up. Same generator? If yes, the savings table understates the total and it's worth a follow-up to add it.

Nothing here is a blocker — the styles.php thing is the only one I'd genuinely want addressed before merge, and even that has a one-line fix path (widen the guard). Really nice work.

Each wp-build consumer now runs a new strip-unminified-prod step after
wp-build, removing the *.js / *.css siblings of *.min.js / *.min.css
under build/{routes,scripts,modules}/** and collapsing the SCRIPT_DEBUG
ternary in the generated routes.php / scripts.php / modules.php loaders
to '.min.js'. Cuts ~10 MiB of dead bytes from jetpack-production while
keeping local pnpm watch / non-production builds untouched.

Wired into: newsletter, podcast, forms, backup, scan, videopress.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dhasilva dhasilva force-pushed the update/wp-build-polyfills-strip-unminified branch from 68c650f to bbd5136 Compare May 12, 2026 21:34
@dhasilva
Copy link
Copy Markdown
Contributor Author

dhasilva commented May 12, 2026

On premium-analytics: that one was a deliberate decision because it does not use the polyfills package and is a small package itself. It would require adding the polyfills package as a dependency just for this.

About changes made to address the comment, I'll let Claudio do the talk:


Generated by Claude:

Thanks for the careful read — addressed the styles.php gap and the smaller nits in bbd51363b8:

  • styles.php is now covered. Added styles to the target subdir walk and added a third pattern for the suffix-shape ternary ($suffix = SCRIPT_DEBUG ? '' : '.min'$suffix = '.min'). The safety net UNMATCHED_SUSPICIOUS_PATTERNS now contains both the .js/.min.js and ''/.min shapes, so an empty-→-populated transition for build/styles/ will trip loudly instead of 404'ing.
  • Tests. Refactored the bin into a thin wrapper + strip-unminified-prod-lib.js and added node --test coverage in tests/js/strip-unminified-prod.test.js. 8 cases: fixture-based deletion + PHP rewrite for all four loader shapes, idempotency, safety-net throws for both unrecognised ternary forms, and a focused patchPhpSource() unit test. Followed the validate-boot-asset.test.js template like you suggested.
  • build/ absent message. [strip-unminified-prod] no build/ at <cwd>, skipping. now prints when there's no build directory.
  • PR body: added a one-line "Caveat about SCRIPT_DEBUG-on-production debugging" acknowledging the in-page stack trace / view-source readability tradeoff — sourcemaps still resolve in devtools, but the unminified-in-page case is gone. Good call.

…od tests

Replaces the synthesised PHP fixtures with a tiny wp-build-compatible
package that actually runs through @wordpress/build at test time. The
before hook invokes wp-build, the test asserts deletions / PHP rewrites
against the real generated output, and the after hook cleans up. If a
future wp-build release shifts the SCRIPT_DEBUG ternary shape or renames
a loader, the polyfills test suite now fails one PR earlier than any
consumer's build-production would.

The fixture covers all four strip targets — one route (paired
.js/.min.js under build/routes/) and one wpScript sub-package with a
build-style/style.css (paired .js/.min.js under build/scripts/ plus
paired .css/.min.css under build/styles/).

@wordpress/build and its browserslist/babel runtime deps move into
polyfills' devDependencies. tests/** is already production-exclude in
.gitattributes, and the new wp-build output paths are gitignored, so
nothing about this commit ships to any production mirror.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dhasilva dhasilva force-pushed the update/wp-build-polyfills-strip-unminified branch from ddec2c3 to d7230a8 Compare May 12, 2026 23:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants