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

Proposal: Support module metadata in block.json for Blocks + Modules API #57492

Closed
sirreal opened this issue Jan 2, 2024 · 10 comments
Closed
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Script Modules API Related to the Script Modules API that adds support for native ES modules and import maps [Type] Discussion For issues that are high-level and not yet ready to implement.

Comments

@sirreal
Copy link
Member

sirreal commented Jan 2, 2024

What problem does this address?

As the Modules API matures and is considered for WordPress Core (WordPress/wordpress-develop#5818), we should consider the experience for working with modules and blocks. The experience of working with modules should not regress compared to the experience of working with scripts.

This proposal is to add Modules API support to blocks via block.json. The proposal suggests the addition of editorModule, module, and viewModule to block.json metadata. These fields are analogous to editorScript, script, and viewScript but use the Modules API.

This issue is intended for discussion and tracking of the necessary work to better support modules when working with blocks. The ideas here came from discussions with @luisherranz.

Current status and suggested changes

Block registration

Blocks can be registered using register_block_type_from_metadata and a block.json file. These mechanisms support the editorScript, script, and viewScript fields to associate scripts with the block in different contexts so the scripts and be registered and enqueued under the appropriate circumstances.

The Modules API is in Gutenberg and has a proposal for WordPress Core: WordPress/wordpress-develop#5818.

Suggested changes:

WordPress script/module dependencies

@wordpress/dependency-extraction-webpack-plugin handles automatic externalization of WordPress script dependencies and generation of a [script].asset.php file used by register_block_type_from_metadata.

Suggested changes:


@wordpress/scripts will detect block.json files in a project and perform webpack compilation to generate the expected assets for use in a production site.

Suggested changes:

Script/Module compatibility

WordPress core scripts are only available as scripts at this time. None of the core scripts can be used as modules. The exception is @wordpress/interactivity which is only available as a module.

viewModule can be used with block using the Interactivity API (@wordpress/interactivity), but not with other core scripts. Some core scripts are not well suited for use on the frontend so this may not be limiting for viewModule. One notable exception is an internalization library, @wordpress/i18n would be nice to have available to modules.

Block.json editorModule and module become much less useful, because they will likely need to use scripts to integrate with the editor. We can still build out functionality to support these fields which is very similar to viewModule.

Suggested changes:

None. This is a broader consideration for the Modules API. Existing scripts need to be made available to modules. I know that @luisherranz has ideas for how to move forward in this area (#55942 (comment)), but for simplicity I'd like to consider the modules-scripts interoperability out of scope for this discussion.

@sirreal sirreal added [Feature] Blocks Overall functionality of blocks [Type] Discussion For issues that are high-level and not yet ready to implement. [Feature] Script Modules API Related to the Script Modules API that adds support for native ES modules and import maps labels Jan 2, 2024
@gziolo gziolo added [Feature] Block API API that allows to express the block paradigm. and removed [Feature] Blocks Overall functionality of blocks labels Jan 2, 2024
@sirreal
Copy link
Member Author

sirreal commented Jan 3, 2024

I'll some notes from speaking with @gziolo and @luisherranz about this.

General

Without more WordPress modules available, it doesn't seem like there's a use case for editorModule or module like there is for viewModule. There's a compelling usage for viewModule, which is to use the Interactivity API packaged as a WordPress module @wordpress/interactivity.

We can remove support for editorModule and module for now since they're unlikely to be used, or if they are used are highly susceptible to misue.

@wordpress/dependency-extraction-webpack-plugin

This part seems straightforward. We should mark module compilation as experimental for now. That could be via a configuration option or a notice in the README. I plan to add an experimental warning to the README in #57199. If anyone feels strongly that a configuration option should be required for modules, we can consider it.

With modules, there's some risk of bundling WordPress packages, e.g. @wordpress/i18n into a module unintentionally. These should not be bundled, but used as externals from the WordPress environment. It would be good to error if a non-module WordPress script is used in a module. That could be part of this plugin or another plugin in @wordpress/scripts. It's probably easiest to do in this plugin.

Block registration

It's a good idea to ship this in Gutenberg first and Core later. That will allow us to test it out. We can use it on https://wpmovies.dev/ once it's available in Gutenberg.

View modules will need to be exposed in the REST API endpoint for block types.

None of the core block.json files should be migrated to use viewModule until support has landed in WordPress core. We need to be mindful that these are synced to core and cannot rely on viewModule support until that's landed in Core.

As noted above, we'll start with just viewModule and not the other *module block.json properties.

@wordpress/scripts

The scripts package has more uncertainty than other changes. There remain some open questions. Keeping this feature clearly marked as experimental without changing any of the default behavior will allow us to iterate more freely.

  • Consumer rely on directly accessing the webpack config file from the package. That file for compiling scripts should continue to be available at the same location in the package.
  • The multi-compiler setup for scripts and modules should be put behind a command-line option, i.e. wp-scripts build --experimental-modules. This will run the multi-compiler build.
  • No webpack config for module compilation will be considered public at this time for consumers to extend.

There remain some open questions about outputting to a single build directory from a webpack multi-compilation. There's risk of assets colliding or being removed incorrectly. One of the compilations is responsible for copying PHP files (render.php) and block.json. Using another directory might help. but we'd need to rewrite parts of block.json instead of copying it if we change the relative path.

@fabiankaegy
Copy link
Member

fabiankaegy commented Jan 3, 2024

Hey all 👋

Thanks for this proposal and all the detailed notes here.

I have to say I am nowhere near an expert on ES Modules and all the benefits they bring but I have some specific workflow question relating to how this will impact (hopefully better) the experience of building blocks.

Lets imagine an example where I have two separate blocks that both have some frontend JS. Both are trying to also use some shared utility functions from a separate file.

We want to make sure that the JS only actually gets loaded when the block is on the current page and also we don't want the shared file with the utility functions to be loaded twice.

Today even without modules this is possible by registering the helper file separately in WordPress and then adding that files handle as a script dependency to the viewScript of both blocks.

This paired with the fact that we have the deferred loading strategy for scripts now means that we can archive a pretty good loading strategy today, though it requires a bit of manual work to register the utility function file as a separate asset in WordPress etc.

Of course if I didn't do that step this would still work but the code from the shared file would be bundled into both of the blocks view scripts and therefore add bloat to the page if both blocks are present.


Is my understanding of this proposal correct in that this would essentially simplify this step of having to manually register any file we want to import as a separate script and automate that by making each imported file an additional module that therefore also makes sure the code isn't duplicated?

@sirreal
Copy link
Member Author

sirreal commented Jan 4, 2024

Thanks for the questions! The short answer is that the situation you describe will not be drastically different. This is largely about adding viewModule that is on par with viewScript and providing an easy way to use @wordpress/interactivity (which is only available as a module).

In the case you describe, the shared module can still be shared, but it will be up to developers to split things up properly. The shared module will still need to be built as its own module and registered as a WordPress module. With modules coming to WordPress it may open up a lot of interesting opportunities in the bundling and tooling space, but that's beyond the scope of this proposal.

One important improvement over scripts we have here is that scripts and script dependencies will always be loaded, even if they're deferred. Modules don't have that limitation, they can be loaded on demand. If we have a shared module, but none of the modules that depend on it ever need it, it's possible to avoid ever downloading the shared module.

Is my understanding of this proposal correct in that this would essentially simplify this step of having to manually register any file we want to import as a separate script and automate that by making each imported file an additional module that therefore also makes sure the code isn't duplicated?

No. That may be possible but that's not what this proposal is about. We still expect to compile the viewModule with @wordpress/scripts into a single module. More advanced builds are possible, but they're not the focus of this proposal.

@gziolo
Copy link
Member

gziolo commented Jan 4, 2024

There is one thing that isn't covered in this issue, and it is tracked on WordPress Trac – Blocks: Introduce a way to enqueue view scripts only when needed for interactivity. It would be great to refactor existing core blocks by offering a formal way for block developers to automatically enqueue registered modules only when related directives are present in the printed HTML for the block. This is handled with the following code as of today:

if ( $is_gutenberg_plugin ) {
if ( $is_expandable_searchfield ) {
gutenberg_enqueue_module( '@wordpress/block-library/search-block' );
}
// Remove the view script because we are using the module.
$block->block_type->view_script_handles = array_diff( $script_handles, array( $view_js_file ) );
} else {
// If the script already exists, there is no point in removing it from viewScript.
if ( ! wp_script_is( $view_js_file ) ) {
// If the script is not needed, and it is still in the `view_script_handles`, remove it.
if ( ! $is_expandable_searchfield && 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 ( $is_expandable_searchfield && ! in_array( $view_js_file, $script_handles, true ) ) {
$block->block_type->view_script_handles = array_merge( $script_handles, array( $view_js_file ) );
}
}
}
}

In the prototypes shared, like for instance sirreal/wordpress-develop#2, all registered view modules with viewModule in block.json are going to be enqueued whenever the block gets rendered, so the same problem still exists. The short-term solution for these core blocks is to never put viewModule and register and enqueue these modules manually, as it works today in the Gutenberg plugin. The default behavior for all registered scripts through block.json (editorScript, script and viewScript) is always to enqueue when the block is registered in the editor or printed on the front end.

@sirreal
Copy link
Member Author

sirreal commented Jan 4, 2024

@gziolo Do you think that should be considered in the first iteration of this proposal? Or could this move ahead with a default behavior to always load the module, then be improved later with different module enqueuing strategies?

@gziolo
Copy link
Member

gziolo commented Jan 5, 2024

Do you think that should be considered in the first iteration of this proposal? Or could this move ahead with a default behavior to always load the module, then be improved later with different module enqueuing strategies?

It isn't mandatory as of today, but the truth is that all 5 core blocks won't benefit from integration with viewModule as, in all cases, the module will be enqueued conditionally based on the rendered output. If we are concerned only about custom blocks, then assuming they are fine always loading the viewModule when the block is printed then it will be a useful addition. It's also how viewScript works today.

@erikyo
Copy link

erikyo commented Jan 8, 2024

If it is helpful I would like to add as a reference an experiment I did recently, aiming to replace webpack with Vite.

Additionally, I need to include the link (and credits) to this repository: https://github.com/kucrut/vite-for-wp, and this discussion on Vite's GitHub page: vitejs/vite#9411 These resources provided much of the information I used to develop the Vite plugin that resolves dependencies and generates the necessary PHP file to load the block. Currently, some features, such as HMR, are not working

sirreal added a commit that referenced this issue Jan 9, 2024
Add handling of viewModule field in block.json when using wp-scripts build or start. As the Modules API matures, our tooling should support it.

We add an option to clearly mark this an an experimental feature: `wp-scripts build --experimental-modules` or `wp-scripts start --experimental-modules`.

To support modules with webpack, it was necessary to run a multi-compilation. An array of webpack configurations is used instead of a single webpack configuration. This is because module compilation is an option at the compilation level and cannot be set for specific entrypoints.

If the `--experimental-modules` option is found, we use an environment variable to change the webpack.config export to return an array `[ scriptWebpackConfig, moduleWebpackConfig ]`. Without the experimental option, the `webpack.config` export is the same. Consumers should be able to continue extending this config without noticing any differences.

Part of #57492.
@sirreal
Copy link
Member Author

sirreal commented Jan 9, 2024

The work in Gutenberg had landed so as of now:

  • @wordpress/dependency-extraction-webpack-plugin supports module builds (merged but not yet released)
  • @wordpress/scripts supports viewModule via --experimental-modules CLI option (merged but not yet released)
  • The next Gutenberg release will support registering blocks with viewModule in their block.json metadata.

@sirreal
Copy link
Member Author

sirreal commented Jan 23, 2024

I've started implementing modules support in WordPress Core in WordPress/wordpress-develop#5860.

"Modules" became "script modules" in the Core implementation, so I'm planning to rename the viewModule field as viewScriptModule and plan to deprecated the viewModule field in Gutenberg.

@sirreal
Copy link
Member Author

sirreal commented Feb 29, 2024

viewScriptModule has landed in core and will be part of the 6.5 release! It will have a corresponding dev note on the make core blog.

I'll close this issue, the most significant topics have been covered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Block API API that allows to express the block paradigm. [Feature] Script Modules API Related to the Script Modules API that adds support for native ES modules and import maps [Type] Discussion For issues that are high-level and not yet ready to implement.
Projects
None yet
Development

No branches or pull requests

4 participants