Skip to content

Commit

Permalink
Editor: Introduce get_block_asset_url Utility Function.
Browse files Browse the repository at this point in the history
This commit introduces a valuable utility function, get_block_asset_url, designed to simplify the retrieval of block asset URLs, such as those for CSS and JavaScript files. This utility eliminates redundancy in both register_block_script_handle and register_block_style_handle. Additionally, `get_block_asset_url` incorporates an early exit mechanism to optimize performance.

This update includes comprehensive unit tests, covering various scenarios, including asset registration from core (wp-includes), themes, child themes, plugins, and mu-plugins.

Props spacedmonkey, joemcgill, flixos90, gziolo.
Fixes #58525.

git-svn-id: https://develop.svn.wordpress.org/trunk@56683 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
spacedmonkey committed Sep 25, 2023
1 parent 54c4de1 commit a68650e
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 71 deletions.
125 changes: 54 additions & 71 deletions src/wp-includes/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,54 @@ function generate_block_asset_handle( $block_name, $field_name, $index = 0 ) {
return $asset_handle;
}

/**
* Gets the URL to a block asset.
*
* @since 6.4.0
*
* @param string $path A normalized path to a block asset.
* @return string|false The URL to the block asset or false on failure.
*/
function get_block_asset_url( $path ) {
if ( empty( $path ) ) {
return false;
}

// Path needs to be normalized to work in Windows env.
static $wpinc_path_norm = '';
if ( ! $wpinc_path_norm ) {
$wpinc_path_norm = wp_normalize_path( realpath( ABSPATH . WPINC ) );
}

if ( str_starts_with( $path, $wpinc_path_norm ) ) {
return includes_url( str_replace( $wpinc_path_norm, '', $path ) );
}

static $template_paths_norm = array();

$template = get_template();
if ( ! isset( $template_paths_norm[ $template ] ) ) {
$template_paths_norm[ $template ] = wp_normalize_path( get_template_directory() );
}

if ( str_starts_with( $path, trailingslashit( $template_paths_norm[ $template ] ) ) ) {
return get_theme_file_uri( str_replace( $template_paths_norm[ $template ], '', $path ) );
}

if ( is_child_theme() ) {
$stylesheet = get_stylesheet();
if ( ! isset( $template_paths_norm[ $stylesheet ] ) ) {
$template_paths_norm[ $stylesheet ] = wp_normalize_path( get_stylesheet_directory() );
}

if ( str_starts_with( $path, trailingslashit( $template_paths_norm[ $stylesheet ] ) ) ) {
return get_theme_file_uri( str_replace( $template_paths_norm[ $stylesheet ], '', $path ) );
}
}

return plugins_url( basename( $path ), $path );
}

/**
* Finds a script handle for the selected block metadata field. It detects
* when a path to file was provided and finds a corresponding asset file
Expand Down Expand Up @@ -107,7 +155,8 @@ function register_block_script_handle( $metadata, $field_name, $index = 0 ) {
return $script_handle;
}

$script_asset_raw_path = dirname( $metadata['file'] ) . '/' . substr_replace( $script_path, '.asset.php', - strlen( '.js' ) );
$path = dirname( $metadata['file'] );
$script_asset_raw_path = $path . '/' . substr_replace( $script_path, '.asset.php', - strlen( '.js' ) );
$script_handle = generate_block_asset_handle( $metadata['name'], $field_name, $index );
$script_asset_path = wp_normalize_path(
realpath( $script_asset_raw_path )
Expand All @@ -128,44 +177,8 @@ function register_block_script_handle( $metadata, $field_name, $index = 0 ) {
return false;
}

// Path needs to be normalized to work in Windows env.
static $wpinc_path_norm = '';
if ( ! $wpinc_path_norm ) {
$wpinc_path_norm = wp_normalize_path( realpath( ABSPATH . WPINC ) );
}

// Cache $template_path_norm and $stylesheet_path_norm to avoid unnecessary additional calls.
static $template_path_norm = '';
static $stylesheet_path_norm = '';
if ( ! $template_path_norm || ! $stylesheet_path_norm ) {
$template_path_norm = wp_normalize_path( get_template_directory() );
$stylesheet_path_norm = wp_normalize_path( get_stylesheet_directory() );
}

$script_path_norm = wp_normalize_path( realpath( dirname( $metadata['file'] ) . '/' . $script_path ) );

$is_core_block = isset( $metadata['file'] ) && str_starts_with( $metadata['file'], $wpinc_path_norm );

/*
* Determine if the block script was registered in a theme, by checking if the script path starts with either
* the parent (template) or child (stylesheet) directory path.
*/
$is_parent_theme_block = str_starts_with( $script_path_norm, trailingslashit( $template_path_norm ) );
$is_child_theme_block = str_starts_with( $script_path_norm, trailingslashit( $stylesheet_path_norm ) );
$is_theme_block = ( $is_parent_theme_block || $is_child_theme_block );

$script_uri = '';
if ( $is_core_block ) {
$script_uri = includes_url( str_replace( $wpinc_path_norm, '', $script_path_norm ) );
} elseif ( $is_theme_block ) {
// Get the script path deterministically based on whether or not it was registered in a parent or child theme.
$script_uri = $is_parent_theme_block
? get_theme_file_uri( str_replace( $template_path_norm, '', $script_path_norm ) )
: get_theme_file_uri( str_replace( $stylesheet_path_norm, '', $script_path_norm ) );
} else {
// Fallback to plugins_url().
$script_uri = plugins_url( $script_path, $metadata['file'] );
}
$script_path_norm = wp_normalize_path( realpath( $path . '/' . $script_path ) );
$script_uri = get_block_asset_url( $script_path_norm );

$script_args = array();
if ( 'viewScript' === $field_name ) {
Expand Down Expand Up @@ -255,37 +268,7 @@ function register_block_style_handle( $metadata, $field_name, $index = 0 ) {
}

$style_path_norm = wp_normalize_path( realpath( dirname( $metadata['file'] ) . '/' . $style_path ) );
$has_style_file = '' !== $style_path_norm;

if ( $has_style_file ) {
$style_uri = plugins_url( $style_path, $metadata['file'] );

// Cache $template_path_norm and $stylesheet_path_norm to avoid unnecessary additional calls.
static $template_path_norm = '';
static $stylesheet_path_norm = '';
if ( ! $template_path_norm || ! $stylesheet_path_norm ) {
$template_path_norm = wp_normalize_path( get_template_directory() );
$stylesheet_path_norm = wp_normalize_path( get_stylesheet_directory() );
}

// Determine if the block style was registered in a theme, by checking if the script path starts with either
// the parent (template) or child (stylesheet) directory path.
$is_parent_theme_block = str_starts_with( $style_path_norm, trailingslashit( $template_path_norm ) );
$is_child_theme_block = str_starts_with( $style_path_norm, trailingslashit( $stylesheet_path_norm ) );
$is_theme_block = ( $is_parent_theme_block || $is_child_theme_block );

if ( $is_core_block ) {
// All possible $style_path variants for core blocks are hard-coded above.
$style_uri = includes_url( 'blocks/' . str_replace( 'core/', '', $metadata['name'] ) . '/' . $style_path );
} elseif ( $is_theme_block ) {
// Get the script path deterministically based on whether or not it was registered in a parent or child theme.
$style_uri = $is_parent_theme_block
? get_theme_file_uri( str_replace( $template_path_norm, '', $style_path_norm ) )
: get_theme_file_uri( str_replace( $stylesheet_path_norm, '', $style_path_norm ) );
}
} else {
$style_uri = false;
}
$style_uri = get_block_asset_url( $style_path_norm );

$version = ! $is_core_block && isset( $metadata['version'] ) ? $metadata['version'] : false;
$result = wp_register_style(
Expand All @@ -298,7 +281,7 @@ function register_block_style_handle( $metadata, $field_name, $index = 0 ) {
return false;
}

if ( $has_style_file ) {
if ( $style_uri ) {
wp_style_add_data( $style_handle_name, 'path', $style_path_norm );

if ( $is_core_block ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"apiVersion": 2,
"title": "Example Theme Block",
"name": "block-theme/example-block",
"description": "Custom block registered from within a theme",
"editorScript": "file:./index.js",
"style": "file:./style.css"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Test CSS file - RTL version */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Test CSS file */
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

return array(
'dependencies' => array( 'wp-element', 'wp-blocks' ),
'version' => 'test',
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Test JavaScript file. */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Test CSS file - RTL version */
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Test CSS file */
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

return array(
'dependencies' => array( 'wp-element', 'wp-blocks' ),
'version' => 'test',
);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* Test JavaScript file. */
116 changes: 116 additions & 0 deletions tests/phpunit/tests/blocks/getBlockAssetUrl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php
/**
* Tests for block asset urls.
*
* @package WordPress
* @subpackage Blocks
*
* @since 6.4.0
*
* @group blocks
* @covers ::get_block_asset_url
*/
class Tests_Get_Block_Asset_Url extends WP_UnitTestCase {
/**
* Original theme directory.
*
* @var string[]
*/
private $orig_theme_dir;

public function set_up() {
global $wp_theme_directories;

parent::set_up();

// Sets up the `wp-content/themes/` directory to ensure consistency when running tests.
$this->orig_theme_dir = $wp_theme_directories;
$wp_theme_directories = array( WP_CONTENT_DIR . '/themes', realpath( DIR_TESTDATA . '/themedir1' ) );

wp_clean_themes_cache();
unset( $GLOBALS['wp_themes'] );
}

public function tear_down() {
global $wp_theme_directories;

$wp_theme_directories = $this->orig_theme_dir;

wp_clean_themes_cache();
unset( $GLOBALS['wp_themes'] );

parent::tear_down();
}

/**
* @ticket 58525
*/
public function test_core_block() {
$path = ABSPATH . WPINC . '/blocks/file/view.min.js';
$url = get_block_asset_url( $path );

$this->assertStringNotContainsString( ABSPATH . WPINC, 'The return block asset url should not contain include path.' );
$this->assertSame( includes_url( '/blocks/file/view.min.js' ), $url, 'The return block asset url should match includes url.' );
}

/**
* @ticket 58525
*/
public function test_parent_theme() {
switch_theme( 'block-theme' );

$path = wp_normalize_path( realpath( DIR_TESTDATA . '/themedir1/block-theme/blocks/example-block/view.js' ) );
$url = get_block_asset_url( $path );

$this->assertSame( get_template_directory_uri() . '/blocks/example-block/view.js', $url );

}

/**
* @ticket 58525
*/
public function test_child_theme() {
switch_theme( 'block-theme-child' );

$path = wp_normalize_path( realpath( DIR_TESTDATA . '/themedir1/block-theme-child/blocks/example-block/view.js' ) );
$url = get_block_asset_url( $path );

$this->assertSame( get_stylesheet_directory_uri() . '/blocks/example-block/view.js', $url );

}

/**
* @ticket 58525
*/
public function test_plugin() {
$path = WP_PLUGIN_DIR . '/test-plugin/blocks/example-block/view.js';
$url = get_block_asset_url( $path );

$this->assertStringNotContainsString( WP_PLUGIN_DIR, $url, 'The return block asset url should not contain plugin path.' );
$this->assertSame( plugins_url( 'view.js', $path ), $url, 'The return block asset url should match plugin url.' );
$this->assertStringStartsWith( WP_PLUGIN_URL, $url, 'The return block asset url should contain the url that support with the mu plugin url.' );

}

/**
* @ticket 58525
*/
public function test_muplugin() {
$path = WPMU_PLUGIN_DIR . '/test-plugin/example-block/view.js';
$url = get_block_asset_url( $path );

$this->assertStringNotContainsString( WPMU_PLUGIN_DIR, $url, 'The return block asset url should not contain plugin path.' );
$this->assertSame( plugins_url( 'view.js', $path ), $url, 'The return block asset url should match plugin url.' );
$this->assertStringStartsWith( WPMU_PLUGIN_URL, $url, 'The return block asset url should contain the url that support with the mu plugin url.' );
}

/**
* @ticket 58525
*/
public function test_empty() {
$url = get_block_asset_url( '' );

$this->assertFalse( $url );
}

}

0 comments on commit a68650e

Please sign in to comment.