Skip to content

Commit

Permalink
jetpack plugin: Add support for WordPress 6.7+'s block metadata colle…
Browse files Browse the repository at this point in the history
…ction registration (#40064)

* WIP: Add wp_register_block_metadata_collection to the jetpack plugin

* changelog

* Ignore WP sniffs for a build script

Co-authored-by: Brad Jorsch <[email protected]>

* Add new built file to gitignore to avoid committing and gitattributes to ensure it exists in built packages

* fix some issues

* fix directory constants, add comments

* move register_block_metadata_collection

* build_block_manifest: More error checking and comments

* add unit test

* add phan suppress comment

* try to fix phan suppression

* better changelog comment

* change to _inc/blocks when registering

* Update projects/plugins/jetpack/changelog/update-wp_register_block_metadata_collection

Co-authored-by: Brad Jorsch <[email protected]>

* Update projects/plugins/jetpack/tools/build-block-manifest.php

Co-authored-by: Brad Jorsch <[email protected]>

* Update projects/plugins/jetpack/tools/build-block-manifest.php

Co-authored-by: Brad Jorsch <[email protected]>

* move to build-extensions step

* fix comment

* move to _inc path

---------

Co-authored-by: Brandon Kraft <[email protected]>
Co-authored-by: Brad Jorsch <[email protected]>
  • Loading branch information
3 people authored Nov 11, 2024
1 parent 3de8166 commit 7e694b3
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

Use wp_register_block_metadata_collection() on WordPress 6.7+ to improve block registration performance by reducing filesystem operations. (See https://core.trac.wordpress.org/changeset/59132)
27 changes: 27 additions & 0 deletions projects/plugins/jetpack/class.jetpack-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,33 @@ public static function site_supports_next_default_size() {
// Final fallback.
return false;
}

/**
* Register block metadata collection for Jetpack blocks.
* This allows for more efficient block metadata loading by avoiding
* individual block.json file reads at runtime.
*
* Uses wp_register_block_metadata_collection() if available (WordPress 6.7+)
* and if the manifest file exists. The manifest file is auto-generated
* during the build process.
*
* Runs on plugins_loaded to ensure registration happens before individual
* blocks register themselves on init.
*
* @static
* @since $$next-version$$
* @return void
*/
public static function register_block_metadata_collection() {
$meta_file_path = JETPACK__PLUGIN_DIR . '_inc/blocks/blocks-manifest.php';
if ( function_exists( 'wp_register_block_metadata_collection' ) && file_exists( $meta_file_path ) ) {
// @phan-suppress-next-line PhanUndeclaredFunction -- New in WP 6.7. We're checking if it exists first.
wp_register_block_metadata_collection(
JETPACK__PLUGIN_DIR . '_inc/blocks/',
$meta_file_path
);
}
}
}

if ( ( new Host() )->is_woa_site() ) {
Expand Down
1 change: 1 addition & 0 deletions projects/plugins/jetpack/class.jetpack.php
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,7 @@ private function __construct() {
require_once JETPACK__PLUGIN_DIR . 'class.jetpack-gutenberg.php';
add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_independent_blocks' ) );
add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'load_block_editor_extensions' ), 9 );
add_action( 'plugins_loaded', array( 'Jetpack_Gutenberg', 'register_block_metadata_collection' ) );
/**
* We've switched from enqueue_block_editor_assets to enqueue_block_assets in WP-Admin because the assets with the former are loaded on the main site-editor.php.
*
Expand Down
2 changes: 1 addition & 1 deletion projects/plugins/jetpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"build-asset-cdn-json": "php tools/build-asset-cdn-json.php",
"build-client": "concurrently --names js,css,module-headings 'webpack --config ./tools/webpack.config.js' 'webpack --config ./tools/webpack.config.css.js' 'php tools/build-module-headings-translations.php'",
"build-concurrently": "pnpm run clean && concurrently 'pnpm:compile-ts' 'pnpm:build-client' 'pnpm:build-extensions' 'pnpm:build-widget-visibility' && pnpm run build-asset-cdn-json",
"build-extensions": "webpack --config ./tools/webpack.config.extensions.js && tools/check-block-assets.php",
"build-extensions": "webpack --config ./tools/webpack.config.extensions.js && tools/check-block-assets.php && php tools/build-block-manifest.php",
"build-production": "pnpm run clean && pnpm run build-production-client && pnpm run build-production-extensions && pnpm run build-production-widget-visibility && pnpm run build-asset-cdn-json",
"build-production-concurrently": "pnpm run clean && concurrently 'pnpm:compile-ts-production' 'pnpm:build-production-client' 'pnpm:build-production-extensions' 'pnpm:build-production-widget-visibility' && pnpm run build-asset-cdn-json",
"build-production-client": "NODE_ENV=production BABEL_ENV=production pnpm run build-client && pnpm exec validate-es ./_inc/build/",
Expand Down
160 changes: 160 additions & 0 deletions projects/plugins/jetpack/tests/php/test_block_manifest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<?php
/**
* Test file for Jetpack Block Manifest functionality.
*
* @package Jetpack
*/

require_once dirname( __DIR__, 2 ) . '/tools/build-block-manifest.php';

/**
* Test case for block manifest generation.
*/
class WP_Test_Block_Manifest extends WP_UnitTestCase {

/**
* Temporary test directory path.
*
* @var string
*/
private $test_dir;

/**
* Track files and directories we create.
*
* @var array
*/
private $cleanup_paths = array();

/**
* Set up the test environment.
*
* @return void
*/
public function set_up() {
parent::set_up();

// Create a temporary test directory.
$this->test_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/jetpack-block-manifest-test-' . uniqid();
mkdir( $this->test_dir, 0777, true );
$this->cleanup_paths[] = $this->test_dir;
}

/**
* Clean up after ourselves.
*
* @return void
*/
public function tear_down() {
// Remove files first, then directories in reverse order.
foreach ( array_reverse( $this->cleanup_paths ) as $path ) {
if ( is_file( $path ) ) {
unlink( $path );
} elseif ( is_dir( $path ) ) {
rmdir( $path );
}
}
parent::tear_down();
}

/**
* Test that the script fails properly when directory doesn't exist.
*
* @return void
*/
public function test_nonexistent_directory() {
$this->expectException( Exception::class );
$this->expectExceptionMessage( 'Input directory does not exist: /path/does/not/exist' );
build_block_manifest( '/path/does/not/exist' );
}

/**
* Test that the script handles empty directories correctly.
*
* @return void
*/
public function test_empty_directory() {
$this->expectException( Exception::class );
$this->expectExceptionMessage( "No block.json files found in: {$this->test_dir}" );
build_block_manifest( $this->test_dir );
}

/**
* Test invalid JSON handling.
*
* @return void
*/
public function test_invalid_json() {
// Create test directory and file.
$block_dir = $this->test_dir . '/invalid-block';
mkdir( $block_dir );
$this->cleanup_paths[] = $block_dir;

$json_file = $block_dir . '/block.json';
file_put_contents( $json_file, '{invalid json}' );
$this->cleanup_paths[] = $json_file;

$this->expectException( Exception::class );
$this->expectExceptionMessage( 'No valid block.json files were processed' );
build_block_manifest( $this->test_dir );
}

/**
* Test successful manifest generation.
*
* @return void
*/
public function test_successful_manifest_generation() {
// Create test directories and files.
$block1_dir = $this->test_dir . '/block1';
mkdir( $block1_dir );
$this->cleanup_paths[] = $block1_dir;

$block1_json = $block1_dir . '/block.json';
file_put_contents(
$block1_json,
wp_json_encode(
array(
'name' => 'test/block1',
'title' => 'Test Block 1',
)
)
);
$this->cleanup_paths[] = $block1_json;

$block2_dir = $this->test_dir . '/block2';
mkdir( $block2_dir );
$this->cleanup_paths[] = $block2_dir;

$block2_json = $block2_dir . '/block.json';
file_put_contents(
$block2_json,
wp_json_encode(
array(
'name' => 'test/block2',
'title' => 'Test Block 2',
)
)
);
$this->cleanup_paths[] = $block2_json;

// Generate manifest.
$result = build_block_manifest( $this->test_dir );
$this->assertTrue( $result );

// Track manifest file for cleanup.
$manifest_path = $this->test_dir . '/blocks-manifest.php';
$this->cleanup_paths[] = $manifest_path;

// Verify the manifest was created and contains expected data.
$this->assertFileExists( $manifest_path );

// Include and check manifest content.
$manifest = include $manifest_path;
$this->assertIsArray( $manifest );
$this->assertArrayHasKey( 'block1', $manifest );
$this->assertArrayHasKey( 'block2', $manifest );
$this->assertEquals( 'Test Block 1', $manifest['block1']['title'] );
$this->assertEquals( 'Test Block 2', $manifest['block2']['title'] );
}
}
93 changes: 93 additions & 0 deletions projects/plugins/jetpack/tools/build-block-manifest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php
/**
* Build script to generate a PHP manifest file containing block metadata from block.json files.
* This reduces filesystem reads and JSON parsing at runtime.
*
* @package automattic/jetpack
*/

// phpcs:disable WordPress.WP.AlternativeFunctions, WordPress.PHP.DevelopmentFunctions.error_log_var_export

/**
* Generates a manifest file containing block metadata from block.json files.
*
* Scans the given directory for block.json files, combines their metadata
* into a single PHP file that returns an array. This improves performance
* by avoiding filesystem reads and JSON parsing at runtime.
*
* @param string $base_path Optional. Base directory to scan for block.json files.
* Defaults to Jetpack blocks directory.
* @return bool True on success, throws Exception with error message on failure.
* @throws Exception If directory doesn't exist, no files found, or write fails.
*/
function build_block_manifest( $base_path = null ) {
if ( null === $base_path ) {
$base_path = __DIR__ . '/../_inc/blocks';
}

if ( ! file_exists( $base_path ) ) {
throw new Exception( "Input directory does not exist: {$base_path}", 1 );
}

$blocks = array();
$files = glob( $base_path . '/*/block.json' );

if ( empty( $files ) ) {
throw new Exception( "No block.json files found in: {$base_path}", 1 );
}

foreach ( $files as $file ) {
if ( ! file_exists( $file ) ) {
// Log warning but continue processing other files.
fwrite( STDERR, "\033[33mWarning:\033[0m Skipping missing file: {$file}\n" );
continue;
}

$json_content = file_get_contents( $file );
if ( false === $json_content ) {
fwrite( STDERR, "\033[33mWarning:\033[0m Could not read file: {$file}\n" );
continue;
}

$block_data = json_decode( $json_content, true );
if ( null === $block_data ) {
fwrite( STDERR, "\033[33mWarning:\033[0m Invalid JSON in file: {$file}\n" );
continue;
}

$dir_name = basename( dirname( $file ) );
$blocks[ $dir_name ] = $block_data;
}

if ( empty( $blocks ) ) {
throw new Exception( 'No valid block.json files were processed', 1 );
}

$output_path = $base_path . '/blocks-manifest.php';
$content = sprintf(
"<?php\n" .
"/**\n" .
" * Generated block metadata manifest.\n" .
" * @generated This file is generated. Do not modify it manually.\n" .
" */\n\n" .
"return %s;\n",
var_export( $blocks, true )
);

$file_put_result = file_put_contents( $output_path, $content );
if ( false === $file_put_result ) {
throw new Exception( "Failed to write manifest file: {$output_path}", 1 );
}

echo '✅ Generated block manifest at ' . $output_path . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo 'Found ' . count( $blocks ) . " blocks\n";

return true;
}

// Only run the function if this script is being executed directly
if ( defined( 'ABSPATH' ) && defined( 'DOING_TESTS' ) ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedIf
// Do nothing - we're in a test environment
} else {
build_block_manifest();
}

0 comments on commit 7e694b3

Please sign in to comment.