Skip to content

Conversation

@youknowriad
Copy link
Contributor

@youknowriad youknowriad commented Jan 12, 2026

Trac ticket

https://core.trac.wordpress.org/ticket/64393

Summary

This PR improves the Gutenberg build integration to address several issues reported in the ticket. The changes simplify the developer workflow and restore a flow similar to how package dependencies worked before the Gutenberg checkout-and-build approach was introduced.

Key Improvements

  1. Automatic rebuild on ref change: Adds a new gutenberg:sync script that follows the same pattern as install-changed (which WordPress already uses for npm dependencies). It stores a hash of the built ref in .gutenberg-hash and only rebuilds when the ref changes. This means when gutenberg.ref in package.json changes (e.g., after git pull), the next npm run build will automatically detect this and rebuild Gutenberg.

  2. Full integration on npm install: Updates postinstall to run the complete checkout + build + copy flow. Running npm install now produces a fully working development environment with Gutenberg assets in src/.

  3. Clean Gutenberg checkout: Restores Gutenberg's package.json after the build completes using git checkout, keeping the Gutenberg directory clean and avoiding uncommitted changes.

  4. Simplified scripts: Removes the gutenberg:integrate npm script and gutenberg-integrate grunt task since their functionality is now handled by gutenberg:sync.

New Flow

npm install
  → gutenberg:sync (checks .gutenberg-hash, runs checkout+build if ref changed)
  → gutenberg:copy --dev (copies to src/)

npm run build / build:dev / dev
  → gutenberg-sync (checks hash, rebuilds only if ref changed)
  → gutenberg-copy (copies to build/ or src/)

This is very similar to how things worked before with package dependencies - npm install handles everything, and subsequent builds are fast because they skip the already-built Gutenberg unless the ref has changed.

Test instructions

  1. Clone the branch and run npm install
  2. Verify Gutenberg is checked out, built, and copied to src/
  3. Run npm run build - should skip Gutenberg rebuild ("already synced")
  4. Change gutenberg.ref in package.json to a different commit
  5. Run npm run build - should detect the change and rebuild Gutenberg
  6. Delete src/wp-includes/build/routes.php and verify WordPress doesn't fatal error

Files changed

  • .gitignore - Added .gutenberg-hash
  • Gruntfile.js - Added gutenberg-sync task, removed gutenberg-integrate task, updated build tasks
  • package.json - Updated postinstall, added gutenberg:sync, removed gutenberg:integrate
  • src/wp-admin/font-library.php - Updated error message to reference npm install
  • src/wp-settings.php - Added conditional file_exists() checks for build PHP files
  • tools/gutenberg/build-gutenberg.js - Added restorePackageJson() after build
  • tools/gutenberg/sync-gutenberg.js - New script for hash-based rebuild detection

🤖 Generated with Claude Code

@github-actions
Copy link

github-actions bot commented Jan 12, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props youknowriad, ellatrix, mcsf, dmsnell, ntsekouras, jorgefilipecosta, tobiasbg.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@youknowriad youknowriad force-pushed the youknowriad/gutenberg-build-improvements branch 3 times, most recently from abd87f6 to 2703d40 Compare January 12, 2026 11:33
* @since 5.5.0
*/
function register_core_block_types_from_metadata() {
if ( ! file_exists( BLOCKS_PATH . 'require-static-blocks.php' ) ) {
Copy link
Member

Choose a reason for hiding this comment

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

What is this file? I'm not seeing it in the Gutenberg repo? Is it something dynamically built?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's built to avoid loading the block.json file from JSON, I think for performance reasons.

@youknowriad youknowriad force-pushed the youknowriad/gutenberg-build-improvements branch from 2703d40 to ecb0f18 Compare January 12, 2026 11:43
@youknowriad
Copy link
Contributor Author

@TobiasBg has kindly accepted to help me test this PR properly and the unit tests in TablePress are now running without any changes.

@youknowriad youknowriad force-pushed the youknowriad/gutenberg-build-improvements branch from ecb0f18 to e8a0aa5 Compare January 12, 2026 14:35
// If the block parser isn't available, skip block attribute filtering.
if ( ! class_exists( 'WP_Block_Parser' ) ) {
return $content;
}
Copy link
Member

Choose a reason for hiding this comment

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

!!!!!

do we really want to disable security protections by default? I’m thinking about cases where something changes and we forget to come back to this spot, because nobody knows it’s there, and suddenly no security mitigations are applied because the happy-path code continues looking right

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If there's no block parsing and no blocks, it means there's no dynamic behavior that creates security issues from blocks, which means this change is the right thing to do in this case.

Worth also nothing that the degraded mode of WordPress loading already exists a long time ago, you can't load WordPress unless you run the build command and this is not something introduced with my changes at all.

Copy link

Choose a reason for hiding this comment

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

If there's no block parsing and no blocks, it means there's no dynamic behavior that creates security issues from blocks, which means this change is the right thing to do in this case.

This assumes that the parser class will be missing if and only if we are in a "no block parsing and no blocks" situation, which I'm not sure is right. What if something in the build process fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This assumes that the parser class will be missing if and only if we are in a "no block parsing and no blocks" situation, which I'm not sure is right.

This is correct though, there doesn't seem any other case for me where the parser is not available outside npm install didn't run. Also regardless of whether there are blocks or not, if the parser is not available, there's no way dynamic blocks run.

Copy link
Member

Choose a reason for hiding this comment

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

we now have WP_Block_Processor which can produce parsed blocks and we have blocks serialized in JSON in post objects in the database/PHP files, don’t we? and doesn’t render_block() run fine without the parser? and if someone adds a filter to the parser class, won’t do_blocks() run fine without this missing class?

Copy link
Contributor Author

@youknowriad youknowriad Jan 14, 2026

Choose a reason for hiding this comment

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

Let's take a step back and contextualize what we're talking about here:

  • No WordPress install in the wild will be without parser class and blocks, so no issue in this case (99.99% of the cases).
  • WordPress doesn't work unbuilt (there's a message that is shown when you try to navigate to it)
  • Unbuilt Wordpress-develop users will also always have the parser class and blocks, unless npm install didn't run, no issues here.
  • If npm install don't run at all, there will be no parser class and no blocks, which means no issues here either.
  • If for some reason npm install breaks in the middle, the copy of the "parser" happens before the copy of the "blocks" in the script, which means the chance of blocks existing and parser not existing are inexistent here.

So basically, the only way for a potential issue to exist here is the following:

  • Someone is loading WordPress by loading php files directly in a script.
  • That person somehow managed to have blocks in, either by including blocks manually or else
  • That person somehow managed to add its own parser class
  • I guess that means the person recreated Gutenberg basically.

I think the chances for this to happen are basically zero.

@TobiasBg
Copy link

I've tested various iterations of this PR throughout the day and thanks to @youknowriad's adjustments was able to get my plugin's unit tests running again.

So, for the common scenario of running unit tests on wordpress-develop, the additional file_exists/class_exists checks are helpful. (Having to run npm commands between checkout and running tests would, in contrast, be ugly, due to the heavily increased built time, storage, etc.)

@youknowriad
Copy link
Contributor Author

due to the heavily increased built time, storage, etc.

Just want to note that the built time is actually faster than before.

@TobiasBg
Copy link

due to the heavily increased built time, storage, etc.
Just want to note that the built time is actually faster than before.

Oh, this was meant as "increased in comparison to not having to run a build step before" (for plugin unit tests, and similar). :-)

That the build time for when a build is needed/desired is faster is a good side effect, of course!

@youknowriad youknowriad force-pushed the youknowriad/gutenberg-build-improvements branch from b5dd400 to 1df9f51 Compare January 12, 2026 23:06
@WordPress WordPress deleted a comment from gclapps0612-cmd Jan 14, 2026
@youknowriad
Copy link
Contributor Author

With the update of the build tool, we also now have prefixed functions, removed the lingering package.json files.

@youknowriad
Copy link
Contributor Author

@WordPress/gutenberg-core I'd appreciate a review of this PR. This unblocks folks doing unit tests without building and address all the remaining feedback on the build change. Would be good to land this one.

@youknowriad youknowriad force-pushed the youknowriad/gutenberg-build-improvements branch from daddd6b to d1ca44e Compare January 15, 2026 16:06
@youknowriad youknowriad force-pushed the youknowriad/gutenberg-build-improvements branch 2 times, most recently from 6742d34 to b4ed4f4 Compare January 15, 2026 17:46
youknowriad and others added 7 commits January 15, 2026 18:47
This commit improves the Gutenberg build integration in several ways:

1. Adds a new `gutenberg:sync` script that follows the same pattern as
   `install-changed` - storing a hash of the built ref in `.gutenberg-hash`
   and only rebuilding when the ref changes. This ensures that when the
   `gutenberg.ref` in package.json changes (e.g., after `git pull`), the
   next build will automatically rebuild Gutenberg.

2. Updates `postinstall` to run the full checkout + build + copy flow,
   so `npm install` produces a working development environment.

3. Adds conditional `file_exists()` checks around the PHP files from
   `wp-includes/build/` so WordPress doesn't fatal error if these files
   don't exist yet.

4. Restores Gutenberg's `package.json` after the build completes using
   `git checkout`, keeping the Gutenberg checkout clean.

5. Removes the `gutenberg:integrate` script and `gutenberg-integrate`
   grunt task since their functionality is now handled by `gutenberg:sync`.

This simplifies the developer workflow and restores a similar flow to
how package dependencies worked before - where `npm install` handles
everything and subsequent builds are fast because they skip the already-
built Gutenberg.

See #64393.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Miguel Fonseca <150562+mcsf@users.noreply.github.com>
The source map files contain path references to the original Gutenberg
source files which don't exist in WordPress Core. Since the .js files
already have their sourceMappingURL comments stripped, these .map files
serve no purpose.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The edit-post.js file comes from Gutenberg and may not always be present.
jQuery is a more fundamental dependency that's always copied during the
build process via Grunt's copy:vendor task.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Files ending with .js.map don't match .endsWith('.js'), so the explicit
exclusion check is unnecessary.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
More semantically correct since we're checking for a directory.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@youknowriad youknowriad force-pushed the youknowriad/gutenberg-build-improvements branch from b4ed4f4 to e12aece Compare January 15, 2026 17:47
Copy link
Member

@jorgefilipecosta jorgefilipecosta left a comment

Choose a reason for hiding this comment

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

Tried the testing steps, tried to test this PR on playground, tried with and without Gutenberg plugin, in all the cases things worked. Did not found any issue with the code, and I agree and support the direction here, and I think it will greatly simplify gutenberg and core syncs in the future. Also opens the door to more automations on that are, e.g: I can see one for WordPress core abilities in the future.
So I think this is ready for merging getting a wider testing way before the 7.0 beta.

Create the destination directory once before copying files rather than
on each iteration.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants