Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple view scripts per block #36176

Merged
merged 18 commits into from
Apr 7, 2022
Merged
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
100 changes: 100 additions & 0 deletions lib/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -593,3 +593,103 @@ function gutenberg_multiple_block_styles( $metadata ) {
return $metadata;
}
add_filter( 'block_type_metadata', 'gutenberg_multiple_block_styles' );

if ( ! function_exists( 'wp_enqueue_block_view_script' ) ) {
/**
* Enqueue a frontend script for a specific block.
*
* Scripts enqueued using this function will only get printed
* when the block gets rendered on the frontend.
*
* @param string $block_name The block-name, including namespace.
* @param array $args An array of arguments [handle,src,deps,ver,media].
*
* @return void
*/
function wp_enqueue_block_view_script( $block_name, $args ) {
$args = wp_parse_args(
$args,
array(
'handle' => '',
'src' => '',
'deps' => array(),
'ver' => false,
'in_footer' => false,

// Additional arg to allow translations for the script's textdomain.
'textdomain' => '',
aristath marked this conversation as resolved.
Show resolved Hide resolved
)
);

/**
* Callback function to register and enqueue scripts.
*
* @param string $content When the callback is used for the render_block filter,
* the content needs to be returned so the function parameter
* is to ensure the content exists.
* @return string Block content.
*/
$callback = static function( $content, $block ) use ( $args, $block_name ) {

// Sanity check.
if ( empty( $block['blockName'] ) || $block_name !== $block['blockName'] ) {
return $content;
}

// Register the stylesheet.
if ( ! empty( $args['src'] ) ) {
wp_register_script( $args['handle'], $args['src'], $args['deps'], $args['ver'], $args['in_footer'] );
}

// Enqueue the stylesheet.
wp_enqueue_script( $args['handle'] );
aristath marked this conversation as resolved.
Show resolved Hide resolved

// If a textdomain is defined, use it to set the script translations.
if ( ! empty( $args['textdomain'] ) && in_array( 'wp-i18n', $args['deps'], true ) ) {
wp_set_script_translations( $args['handle'], $args['textdomain'] );
}

return $content;
};

/*
* The filter's callback here is an anonymous function because
* using a named function in this case is not possible.
*
* The function cannot be unhooked, however, users are still able
* to dequeue the script registered/enqueued by the callback
* which is why in this case, using an anonymous function
* was deemed acceptable.
*/
add_filter( 'render_block', $callback, 10, 2 );
}
}

/**
* Allow multiple view scripts per block.
*
* Filters the metadata provided for registering a block type.
*
* @param array $metadata Metadata for registering a block type.
*
* @return array
*/
function gutenberg_block_type_metadata_multiple_view_scripts( $metadata ) {

// Early return if viewScript is empty, or not an array.
if ( ! isset( $metadata['viewScript'] ) || ! is_array( $metadata['viewScript'] ) ) {
return $metadata;
}

// Register all viewScript items.
foreach ( $metadata['viewScript'] as $view_script ) {
$item_metadata = $metadata;
$item_metadata['viewScript'] = $view_script;
gutenberg_block_type_metadata_view_script( array(), $item_metadata );
}

// Proceed with the default behavior.
$metadata['viewScript'] = $metadata['viewScript'][0];
return $metadata;
}
add_filter( 'block_type_metadata', 'gutenberg_block_type_metadata_multiple_view_scripts' );
51 changes: 51 additions & 0 deletions lib/compat/wordpress-6.0/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,54 @@ function gutenberg_build_query_vars_from_query_block( $block, $page ) {
}
return $query;
}

/**
* Registers view scripts for core blocks if handling is missing in WordPress core.
*
* This is a temporary solution until the Gutenberg plugin sets
* the required WordPress version to 6.0.
*
* @param array $settings Array of determined settings for registering a block type.
* @param array $metadata Metadata provided for registering a block type.
*
* @return array Array of settings for registering a block type.
*/
function gutenberg_block_type_metadata_view_script( $settings, $metadata ) {
if (
! isset( $metadata['viewScript'] ) ||
! empty( $settings['view_script'] ) ||
! isset( $metadata['file'] ) ||
strpos( $metadata['file'], gutenberg_dir_path() ) !== 0
) {
return $settings;
}

$view_script_path = realpath( dirname( $metadata['file'] ) . '/' . remove_block_asset_path_prefix( $metadata['viewScript'] ) );

if ( file_exists( $view_script_path ) ) {
$view_script_id = str_replace( array( '.min.js', '.js' ), '', basename( remove_block_asset_path_prefix( $metadata['viewScript'] ) ) );
$view_script_handle = str_replace( 'core/', 'wp-block-', $metadata['name'] ) . '-' . $view_script_id;
wp_deregister_script( $view_script_handle );

// Replace suffix and extension with `.asset.php` to find the generated dependencies file.
$view_asset_file = substr( $view_script_path, 0, -( strlen( '.js' ) ) ) . '.asset.php';
$view_asset = file_exists( $view_asset_file ) ? require( $view_asset_file ) : null;
$view_script_dependencies = isset( $view_asset['dependencies'] ) ? $view_asset['dependencies'] : array();
$view_script_version = isset( $view_asset['version'] ) ? $view_asset['version'] : false;
$result = wp_register_script(
$view_script_handle,
gutenberg_url( str_replace( gutenberg_dir_path(), '', $view_script_path ) ),
$view_script_dependencies,
$view_script_version
);
if ( $result ) {
$settings['view_script'] = $view_script_handle;

if ( ! empty( $metadata['textdomain'] ) && in_array( 'wp-i18n', $view_script_dependencies, true ) ) {
wp_set_script_translations( $view_script_handle, $metadata['textdomain'] );
}
}
aristath marked this conversation as resolved.
Show resolved Hide resolved
}
return $settings;
}
add_filter( 'block_type_metadata_settings', 'gutenberg_block_type_metadata_view_script', 10, 2 );
2 changes: 1 addition & 1 deletion packages/block-library/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ _This package assumes that your code will run in an **ES2015+** environment. If

## Building JavaScript for the browser

If a `view.js` file is present in the block's directory, this file will be built along other assets, making it available to load from the browser.
If a `view.js` file (or a file prefixed with `view`, e.g. `view-example.js`) is present in the block's directory, this file will be built along other assets, making it available to load from the browser.

This enables us to, for instance, load this file when the block is present on the page in two ways:

Expand Down
14 changes: 12 additions & 2 deletions schemas/json/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,18 @@
"description": "Block type frontend and editor script definition. It will be enqueued both in the editor and when viewing the content on the front of the site."
},
"viewScript": {
"type": "string",
"description": "Block type frontend script definition. It will be enqueued only when viewing the content on the front of the site."
"description": "Block type frontend script definition. It will be enqueued only when viewing the content on the front of the site.",
"oneOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
]
},
"editorStyle": {
"description": "Block type editor style definition. It will only be enqueued in the context of the editor.",
Expand Down
24 changes: 14 additions & 10 deletions tools/webpack/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ const DependencyExtractionWebpackPlugin = require( '@wordpress/dependency-extrac
const { baseConfig, plugins, stylesTransform } = require( './shared' );

/*
* Matches a block's name in paths in the form
* build-module/<blockName>/view.js
* Matches a block's filepaths in the form build-module/<filename>.js
*/
const blockNameRegex = new RegExp( /(?<=build-module\/).*(?=(\/view))/g );
const blockViewRegex = new RegExp(
/build-module\/(?<filename>.*\/view.*).js$/
);

/**
* We need to automatically rename some functions when they are called inside block files,
Expand All @@ -31,26 +32,29 @@ const prefixFunctions = [ 'build_query_vars_from_query_block' ];

const createEntrypoints = () => {
/*
* Returns an array of paths to view.js files within the `@wordpress/block-library` package.
* These paths can be matched by the regex `blockNameRegex` in order to extract
* the block's name.
* Returns an array of paths to block view files within the `@wordpress/block-library` package.
* These paths can be matched by the regex `blockViewRegex` in order to extract
* the block's filename.
*
* Returns an empty array if no files were found.
*/
const blockViewScriptPaths = fastGlob.sync(
'./packages/block-library/build-module/**/view.js'
'./packages/block-library/build-module/**/view*.js'
);

/*
* Go through the paths found above, in order to define webpack entry points for
* each block's view.js file.
*/
return blockViewScriptPaths.reduce( ( entries, scriptPath ) => {
const [ blockName ] = scriptPath.match( blockNameRegex );
const result = scriptPath.match( blockViewRegex );
if ( ! result?.groups?.filename ) {
return entries;
}

return {
...entries,
[ 'blocks/' + blockName ]: scriptPath,
[ result.groups.filename ]: scriptPath,
};
}, {} );
};
Expand All @@ -61,7 +65,7 @@ module.exports = {
entry: createEntrypoints(),
output: {
devtoolNamespace: 'wp',
filename: './build/block-library/[name]/view.min.js',
filename: './build/block-library/blocks/[name].min.js',
path: join( __dirname, '..', '..' ),
},
plugins: [
Expand Down