-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Experiment: A simple Modules API with no server dependency graph #56092
Changes from 7 commits
1f3f99d
8fe36f9
68692d1
0215330
9c5ea5d
3ffc77c
7cc08c8
11b4e1b
c080fa7
bd0a881
e0201ce
89f6be0
c5b2c83
68cd6c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
/** | ||
* Interactive modules. | ||
* | ||
* @package Gutenberg | ||
* @subpackage Interactivity API | ||
*/ | ||
|
||
/** | ||
* Register the `@wordpress/interactivity` module. | ||
*/ | ||
function gutenberg_register_interactivity_module() { | ||
gutenberg_register_module( | ||
'@wordpress/interactivity', | ||
'/wp-content/plugins/gutenberg/build/interactivity/index.min.js' | ||
); | ||
} | ||
|
||
add_action( 'wp_enqueue_scripts', 'gutenberg_register_interactivity_module' ); |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
<?php | ||
/** | ||
* Gutenberg_Modules class. | ||
* | ||
* Native support for ES Modules and Import Maps. | ||
* | ||
* @package Gutenberg | ||
* @subpackage Modules | ||
*/ | ||
class Gutenberg_Modules { | ||
/** | ||
* An array of registered modules, keyed by module identifier. | ||
* | ||
* @var array | ||
*/ | ||
private static $registered = array(); | ||
|
||
|
||
/** | ||
* An array of queued modules. | ||
* | ||
* @var string[] | ||
*/ | ||
private static $enqueued = array(); | ||
|
||
/** | ||
* Registers the module if no module with that module identifier already | ||
* exists. | ||
* | ||
* @param string $module_identifier The identifier of the module. Should be unique. It will be used in the final import map. | ||
* @param string $src Full URL of the module, or path of the script relative to the WordPress root directory. | ||
* @param array $args { | ||
* Optional array of arguments. | ||
* | ||
* @type string|bool $ver Optional. String specifying script version number, if it has one, it is added to the URL | ||
* as a query string for cache busting purposes. If version is set to false, a version | ||
* number is automatically added equal to current installed WordPress version. If SCRIPT_DEBUG | ||
* is set to true, it uses the timestamp instead. | ||
* } | ||
*/ | ||
public static function register( $module_identifier, $src, $args = array() ) { | ||
// Register the module if it's not already registered. | ||
if ( ! isset( self::$registered[ $module_identifier ] ) ) { | ||
self::$registered[ $module_identifier ] = array( | ||
'src' => $src, | ||
'args' => $args, | ||
); | ||
} | ||
} | ||
|
||
/** | ||
* Enqueues a module for output in the page. | ||
* | ||
* @param string $module_identifier The identifier of the module. | ||
* @param string $src Optional. Full URL of the module, or path of the script relative to the WordPress root directory. | ||
* @param array $args { | ||
* Optional array of arguments. | ||
* | ||
* @type string|bool $ver Optional. String specifying script version number, if it has one, it is added to the URL | ||
* as a query string for cache busting purposes. If version is set to false, a version | ||
* number is automatically added equal to current installed WordPress version. If SCRIPT_DEBUG | ||
* is set to true, it uses the timestamp instead. | ||
* } | ||
*/ | ||
public static function enqueue( $module_identifier, $src = null, $args = array() ) { | ||
// Register the module if a source is provided and it's not already registered. | ||
if ( $src && ! isset( self::$registered[ $module_identifier ] ) ) { | ||
self::register( $module_identifier, $src, $args ); | ||
} | ||
|
||
// Add the module to the queue if it's not already there. | ||
if ( ! in_array( $module_identifier, self::$enqueued, true ) ) { | ||
self::$enqueued[] = $module_identifier; | ||
} | ||
} | ||
|
||
/** | ||
* Returns the import map array. | ||
* | ||
* @return string The import map. | ||
*/ | ||
public static function get_import_map() { | ||
$import_map = array( | ||
'imports' => array(), | ||
); | ||
|
||
foreach ( self::$registered as $module_identifier => $module_data ) { | ||
$version = SCRIPT_DEBUG ? '?ver=' . time() : '?ver=' . $module_data['args']['version'] || ''; | ||
|
||
$import_map['imports'][ $module_identifier ] = $module_data['src'] . $version; | ||
} | ||
|
||
return $import_map; | ||
} | ||
|
||
/** | ||
* Prints the import map | ||
*/ | ||
public static function print_import_map() { | ||
echo '<script type="importmap">' . wp_json_encode( self::get_import_map(), JSON_HEX_TAG | JSON_HEX_AMP ) . '</script>'; | ||
} | ||
|
||
/** | ||
* Prints all enqueued modules using script tags with type "module". | ||
*/ | ||
public static function print_enqueued_modules() { | ||
foreach ( self::$enqueued as $module_identifier ) { | ||
if ( isset( self::$registered[ $module_identifier ] ) ) { | ||
$module = self::$registered[ $module_identifier ]; | ||
$version = SCRIPT_DEBUG ? '?ver=' . time() : '?ver=' . $module['args']['version'] || ''; | ||
echo '<script type="module" src="' . $module['src'] . $version . '" id="' . $module_identifier . '"></script>'; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Registers a JavaScript module. It will be added to the import map. | ||
* | ||
* @param string $module_identifier The identifier of the module. Should be unique. It will be used in the final import map. | ||
* @param string $src Full URL of the module, or path of the script relative to the WordPress root directory. | ||
* @param array $args { | ||
* Optional array of arguments. | ||
* | ||
* @type string|bool $ver Optional. String specifying script version number, if it has one, it is added to the URL | ||
* as a query string for cache busting purposes. If version is set to false, a version | ||
* number is automatically added equal to current installed WordPress version. If SCRIPT_DEBUG | ||
* is set to true, it uses the timestamp instead. | ||
* } | ||
*/ | ||
function gutenberg_register_module( $module_identifier, $src, $args = array() ) { | ||
Gutenberg_Modules::register( $module_identifier, $src, $args ); | ||
} | ||
|
||
/** | ||
* Enqueues a JavaScript module. It will be added to both the import map and a | ||
* script tag with the "module" type. | ||
* | ||
* It registers the module if a source is provided but it won't overwrites the | ||
* value if there is an existing one. | ||
* | ||
* @param string $module_identifier The identifier of the module. Should be unique. It will be used in the final import map. | ||
* @param string $src Optional. Full URL of the module, or path of the script relative to the WordPress root directory. | ||
* @param array $args { | ||
* Optional array of arguments. | ||
* | ||
* @type string|bool $ver Optional. String specifying script version number, if it has one, it is added to the URL | ||
* as a query string for cache busting purposes. If version is set to false, a version | ||
* number is automatically added equal to current installed WordPress version. If SCRIPT_DEBUG | ||
* is set to true, it uses the timestamp instead. | ||
* } | ||
*/ | ||
function gutenberg_enqueue_module( $module_identifier, $src = '', $args = array() ) { | ||
Gutenberg_Modules::enqueue( $module_identifier, $src, $args ); | ||
} | ||
|
||
// Attach the above function to 'wp_head' action hook. | ||
add_action( 'wp_head', array( 'Gutenberg_Modules', 'print_import_map' ) ); | ||
|
||
// Attach the new function to 'wp_head' action hook. | ||
add_action( 'wp_head', array( 'Gutenberg_Modules', 'print_enqueued_modules' ) ); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -637,20 +637,13 @@ function render_block_core_navigation( $attributes, $content, $block ) { | |
} | ||
|
||
$should_load_view_script = ( $has_submenus && ( $attributes['openSubmenusOnClick'] || $attributes['showSubmenuIcon'] ) ) || $is_responsive_menu; | ||
$view_js_file = 'wp-block-navigation-view'; | ||
|
||
// If the script already exists, there is no point in removing it from viewScript. | ||
if ( ! wp_script_is( $view_js_file ) ) { | ||
$script_handles = $block->block_type->view_script_handles; | ||
|
||
// If the script is not needed, and it is still in the `view_script_handles`, remove it. | ||
if ( ! $should_load_view_script && in_array( $view_js_file, $script_handles, true ) ) { | ||
$block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) ); | ||
} | ||
// If the script is needed, but it was previously removed, add it again. | ||
if ( $should_load_view_script && ! in_array( $view_js_file, $script_handles, true ) ) { | ||
$block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) ); | ||
} | ||
// Load the modules. | ||
if ( $should_load_view_script ) { | ||
gutenberg_enqueue_module( | ||
'@wordpress/block-library/navigation-block', | ||
'/wp-content/plugins/gutenberg/build/interactivity/navigation.min.js' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think we can avoid the file source from the enqueue function? I expect enqueued modules to be registered before hand no? (I know the current scripts API also allows this but it feels weird to me) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. I'll do that. One thing that feels a bit weird to me, though, is to add an entry to the import map for the enqueued modules because they rarely export anything. So I wonder if
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see what you mean, I'm not sure at the moment. I also wonder if there are scripts that could be entry points but also dependencies at the same time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think it's a possibility, but it's going to be uncommon. |
||
); | ||
} | ||
|
||
// Add directives to the submenu if needed. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the basic version (no server graph), I was wondering if we could support some kind of "flags" or something to say that a script is a backend script, frontend script or both. (It's a small optimization as there's a lot of backend script that are not meant to be loaded in the frontend)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. I don't think it makes sense to have a version of the API that doesn't do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in 11b4e1b.