Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Member Author

Choose a reason for hiding this comment

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

These changes solely relocate existing code, aside from the inline comments noting otherwise.

Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ private function get_core_block_types() {
$core_blocks = array_filter(
array_keys( WP_Block_Type_Registry::get_instance()->get_all_registered() ),
function ( $block_name ) {
return strpos( $block_name, 'core/' ) === 0;
return str_starts_with( $block_name, 'core/' );
Copy link
Member Author

Choose a reason for hiding this comment

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

This and similar changes merely change generic string utilities to more explicit ones to better communicate intent.

}
);

Expand Down Expand Up @@ -166,44 +166,80 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna
// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
global $wp_styles, $wp_scripts;

// Save current asset state
$current_wp_styles = $wp_styles;
$current_wp_scripts = $wp_scripts;

// Preserve allowed plugin scripts registered during the `init` action
$preserved_scripts = array();
$preserved_styles = array();
try {
// Preserve allowed plugin assets before reinitializing
$preserved = $this->preserve_allowed_plugin_assets();

foreach ( $current_wp_scripts->registered as $handle => $script ) {
if ( $this->is_allowed_plugin_handle( $handle ) && ! $this->is_core_or_gutenberg_asset( $script->src ) ) {
$preserved_scripts[ $handle ] = clone $script;
}
}
// Initialize fresh asset registries to control what gets loaded
$wp_styles = new WP_Styles();
$wp_scripts = new WP_Scripts();

foreach ( $current_wp_styles->registered as $handle => $style ) {
if ( $this->is_allowed_plugin_handle( $handle ) && ! $this->is_core_or_gutenberg_asset( $style->src ) ) {
$preserved_styles[ $handle ] = clone $style;
}
}
// Restore preserved plugin assets
$this->restore_preserved_assets( $preserved );

// Initialize new instances to control which assets are loaded
$wp_styles = new WP_Styles();
$wp_scripts = new WP_Scripts();
// Set up a block editor screen context to prevent errors when
// plugins/themes call get_current_screen() during asset enqueueing
$this->setup_block_editor_screen();

// Restore preserved plugin scripts
foreach ( $preserved_scripts as $handle => $script ) {
$wp_scripts->registered[ $handle ] = $script;
}
// Trigger wp_loaded action that plugins frequently use to enqueue assets.
// This must happen after screen setup and before we collect enqueued assets.
do_action( 'wp_loaded' );

foreach ( $preserved_styles as $handle => $style ) {
$wp_styles->registered[ $handle ] = $style;
}
// Enqueue all core WordPress editor assets
$this->enqueue_core_editor_assets();

// Trigger block editor asset actions with forced script/style loading
add_filter( 'should_load_block_editor_scripts_and_styles', '__return_true' );
do_action( 'enqueue_block_assets' );
do_action( 'enqueue_block_editor_assets' );
remove_filter( 'should_load_block_editor_scripts_and_styles', '__return_true' );

// Enqueue editor-specific assets for all registered block types
$this->enqueue_block_type_editor_assets();

// Remove disallowed plugin assets before generating output
$this->unregister_disallowed_plugin_assets();

// Set up a block editor screen context to prevent errors when
// plugins/themes call get_current_screen() during asset enqueueing
$this->setup_block_editor_screen();
// Capture HTML output with absolute URLs
$html = $this->with_absolute_urls(
function () {
return array(
'styles' => $this->capture_styles_output(),
'scripts' => $this->capture_scripts_output(),
);
}
);

// Trigger an action frequently used by plugins to enqueue assets.
do_action( 'wp_loaded' );
return rest_ensure_response(
array(
'allowed_block_types' => array_merge(
$this->get_core_block_types(),
self::ALLOWED_PLUGIN_BLOCKS
),
'scripts' => $html['scripts'],
'styles' => $html['styles'],
)
);

} finally {
// Always restore original asset state, even if an exception occurred
$wp_styles = $current_wp_styles;
$wp_scripts = $current_wp_scripts;
}
Comment on lines +228 to +232
Copy link
Member Author

Choose a reason for hiding this comment

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

This try/finally block as added to ensure global state is restored in the event of an error. It will likely have no impact, but felt worthwhile nonetheless.

}

/**
* Enqueues core WordPress editor assets.
*
* This includes polyfills, block styles, theme styles, and foundational
* post editor scripts and styles.
*/
private function enqueue_core_editor_assets() {
global $wp_styles;

// We generally do not need reset styles for the block editor. However, if
// it's a classic theme, margins will be added to every block, which is
Expand All @@ -230,15 +266,15 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna
// Enqueue foundational post editor assets.
wp_enqueue_script( 'wp-edit-post' );
wp_enqueue_style( 'wp-edit-post' );
}

// Ensure the block editor scripts and styles are enqueued.
add_filter( 'should_load_block_editor_scripts_and_styles', '__return_true' );
do_action( 'enqueue_block_assets' );
do_action( 'enqueue_block_editor_assets' );
remove_filter( 'should_load_block_editor_scripts_and_styles', '__return_true' );

// Additionally, enqueue `editorStyle` and `editorScript` assets for all
// blocks, which contains editor-only styling for blocks (editor content).
/**
* Enqueues editor-specific assets for all registered block types.
*
* This includes editor_style_handles and editor_script_handles for each
* block, which contains editor-only styling and scripts.
*/
private function enqueue_block_type_editor_assets() {
$block_registry = WP_Block_Type_Registry::get_instance();
foreach ( $block_registry->get_all_registered() as $block_type ) {
if ( isset( $block_type->editor_style_handles ) && is_array( $block_type->editor_style_handles ) ) {
Expand All @@ -252,20 +288,105 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna
}
}
}
}

/**
* Captures the HTML output of enqueued scripts.
*
* @return string The HTML output of all enqueued scripts.
*/
private function capture_scripts_output() {
ob_start();
wp_print_head_scripts();
wp_print_footer_scripts();
return ob_get_clean();
}

/**
* Preserves allowed plugin assets from the current asset registries.
*
* This method clones assets from allowed plugins that aren't core/Gutenberg
* assets, so they can be restored after reinitializing the asset registries.
*
* @return array Array with 'scripts' and 'styles' keys containing cloned assets.
*/
private function preserve_allowed_plugin_assets() {
global $wp_scripts, $wp_styles;

$preserved = array(
'scripts' => array(),
'styles' => array(),
);

foreach ( $wp_scripts->registered as $handle => $script ) {
if ( $this->is_allowed_plugin_handle( $handle ) && ! $this->is_core_or_gutenberg_asset( $script->src ) ) {
$preserved['scripts'][ $handle ] = clone $script;
}
}

foreach ( $wp_styles->registered as $handle => $style ) {
if ( $this->is_allowed_plugin_handle( $handle ) && ! $this->is_core_or_gutenberg_asset( $style->src ) ) {
$preserved['styles'][ $handle ] = clone $style;
}
}

return $preserved;
}

/**
* Restores previously preserved plugin assets to the asset registries.
*
* @param array $preserved Array with 'scripts' and 'styles' keys containing preserved assets.
*/
private function restore_preserved_assets( $preserved ) {
global $wp_scripts, $wp_styles;

foreach ( $preserved['scripts'] as $handle => $script ) {
$wp_scripts->registered[ $handle ] = $script;
}

foreach ( $preserved['styles'] as $handle => $style ) {
$wp_styles->registered[ $handle ] = $style;
}
}

/**
* Executes a callback with absolute URL filters temporarily enabled.
*
* This ensures that all asset URLs are converted to absolute URLs during
* the callback execution, then removes the filters afterward.
*
* @param callable $callback The function to execute with absolute URL filters.
* @return mixed The return value of the callback.
*/
private function with_absolute_urls( $callback ) {
add_filter( 'script_loader_src', array( $this, 'make_url_absolute' ), 10, 2 );
add_filter( 'style_loader_src', array( $this, 'make_url_absolute' ), 10, 2 );

$result = $callback();

remove_filter( 'script_loader_src', array( $this, 'make_url_absolute' ), 10 );
remove_filter( 'style_loader_src', array( $this, 'make_url_absolute' ), 10 );

return $result;
}

/**
* Captures the HTML output of enqueued styles with emoji handling.
*
* This temporarily removes the emoji styles action to prevent deprecation
* warnings, then restores it after capturing the output.
*
* @return string The HTML output of all enqueued styles.
*/
private function capture_styles_output() {
// Remove the deprecated `print_emoji_styles` handler. It avoids breaking
// style generation with a deprecation message.
$has_emoji_styles = has_action( 'wp_print_styles', 'print_emoji_styles' );
if ( $has_emoji_styles ) {
remove_action( 'wp_print_styles', 'print_emoji_styles' );
}

// Unregister disallowed plugin assets before proceeding with asset collection
$this->unregister_disallowed_plugin_assets();

add_filter( 'script_loader_src', array( $this, 'make_url_absolute' ), 10, 2 );
add_filter( 'style_loader_src', array( $this, 'make_url_absolute' ), 10, 2 );

ob_start();
wp_print_styles();
$styles = ob_get_clean();
Expand All @@ -274,27 +395,7 @@ public function get_items( $request ) { // phpcs:ignore VariableAnalysis.CodeAna
add_action( 'wp_print_styles', 'print_emoji_styles' );
}

ob_start();
wp_print_head_scripts();
wp_print_footer_scripts();
$scripts = ob_get_clean();

remove_filter( 'script_loader_src', array( $this, 'make_url_absolute' ), 10 );
remove_filter( 'style_loader_src', array( $this, 'make_url_absolute' ), 10 );

$wp_styles = $current_wp_styles;
$wp_scripts = $current_wp_scripts;

return rest_ensure_response(
array(
'allowed_block_types' => array_merge(
$this->get_core_block_types(),
self::ALLOWED_PLUGIN_BLOCKS
),
'scripts' => $scripts,
'styles' => $styles,
)
);
return $styles;
}

/**
Expand Down Expand Up @@ -379,10 +480,10 @@ private function is_core_or_gutenberg_asset( $src ) {

return empty( $src ) ||
$src[0] === '/' ||
strpos( $src, 'wp-includes/' ) !== false ||
strpos( $src, 'wp-admin/' ) !== false ||
strpos( $src, 'plugins/gutenberg/' ) !== false ||
strpos( $src, 'plugins/gutenberg-core/' ) !== false; // WPCOM-specific path
str_contains( $src, 'wp-includes/' ) ||
str_contains( $src, 'wp-admin/' ) ||
str_contains( $src, 'plugins/gutenberg/' ) ||
str_contains( $src, 'plugins/gutenberg-core/' ); // WPCOM-specific path
}

/**
Expand All @@ -407,7 +508,7 @@ private function is_allowed_plugin_handle( $handle ) {
}

foreach ( self::ALLOWED_PLUGIN_HANDLE_PREFIXES as $allowed_prefix ) {
if ( strpos( $handle, $allowed_prefix ) === 0 ) {
if ( str_starts_with( $handle, $allowed_prefix ) ) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

Editor assets endpoint: refactor to reduce complexity
Loading