diff --git a/packages/block-library/README.md b/packages/block-library/README.md index a08824d974795..dd8f55b705fed 100644 --- a/packages/block-library/README.md +++ b/packages/block-library/README.md @@ -12,6 +12,63 @@ npm install @wordpress/block-library --save _This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for ES2015+ such as lower versions of IE then using [core-js](https://github.com/zloirock/core-js) or [@babel/polyfill](https://babeljs.io/docs/en/next/babel-polyfill) will add support for these methods. Learn more about it in [Babel docs](https://babeljs.io/docs/en/next/caveats)._ +## Building JavaScript for the browser + +If a `frontend.js` file is present in the block's directory, this file will be built along other assets, making it available to load it from the browser. + +This enables us to, for instance, load this file when the block is present on the page in several ways: + +1. Using the block's `render_callback`: + +```php + +function render_my_block() { + $script_path = __DIR__ . '/block-name/frontend.js'; + + if ( file_exists( $script_path ) ) { + wp_enqueue_script( + 'my_block_frontend_script', + plugins_url( 'frontend.js', $script_path ), + array(), + false, + true + ); + } +} + +function register_block_my_block() { + register_block_type_from_metadata( + __DIR__ . '/block-name', + array( + 'render_callback' => 'render_my_block', + ) + ); +} + + +add_action( 'init', 'register_block_my_block' ); +``` + +2. Using the `render_block` filter: + +```php +function render_my_block() { + $script_path = __DIR__ . '/block-name/frontend.js'; + + if ( file_exists( $script_path ) ) { + wp_enqueue_script( + 'my_block_frontend_script', + plugins_url( 'frontend.js', $script_path ), + array(), + false, + true + ); + } +} + +apply_filter( 'render_block', 'render_my_block' ); +``` + ## API diff --git a/webpack.config.js b/webpack.config.js index 12ea84441fde7..c67712cb4a26a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,8 +6,9 @@ const { DefinePlugin } = require( 'webpack' ); const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); const TerserPlugin = require( 'terser-webpack-plugin' ); const postcss = require( 'postcss' ); -const { get, escapeRegExp, compact } = require( 'lodash' ); -const { basename, sep } = require( 'path' ); +const { escapeRegExp, compact } = require( 'lodash' ); +const { sep } = require( 'path' ); +const fastGlob = require( 'fast-glob' ); /** * WordPress dependencies @@ -64,6 +65,34 @@ const stylesTransform = ( content ) => { return content; }; +const blockNameRegex = new RegExp( /(?<=src\/).*(?=(\/frontend))/g ); + +const createEntrypoints = () => { + const scriptPaths = fastGlob.sync( + './packages/block-library/src/**/frontend.js', + { + ignore: [ '**/build*/**' ], + } + ); + + const scriptEntries = scriptPaths.reduce( ( entries, scriptPath ) => { + const [ blockName ] = scriptPath.match( blockNameRegex ) || []; + + return { + ...entries, + [ blockName ]: scriptPath, + }; + }, {} ); + + const packageEntries = gutenbergPackages.reduce( ( memo, packageName ) => { + memo[ packageName ] = `./packages/${ packageName }`; + + return memo; + }, {} ); + + return { ...packageEntries, ...scriptEntries }; +}; + module.exports = { optimization: { // Only concatenate modules in production, when not analyzing bundles. @@ -90,16 +119,22 @@ module.exports = { ], }, mode, - entry: gutenbergPackages.reduce( ( memo, packageName ) => { - const name = camelCaseDash( packageName ); - memo[ name ] = `./packages/${ packageName }`; - return memo; - }, {} ), + entry: createEntrypoints(), output: { devtoolNamespace: 'wp', - filename: './build/[basename]/index.js', + filename: ( data ) => { + const { chunk } = data; + const { entryModule } = chunk; + const { rawRequest } = entryModule; + + if ( rawRequest && rawRequest.includes( '/frontend.js' ) ) { + return `./build/block-library/blocks/[name]/frontend.js`; + } + + return './build/[name]/index.js'; + }, path: __dirname, - library: [ 'wp', '[name]' ], + library: [ 'wp', '[camelName]' ], libraryTarget: 'window', }, module: { @@ -135,39 +170,20 @@ module.exports = { ), } ), new CustomTemplatedPathPlugin( { - basename( path, data ) { - let rawRequest; - - const entryModule = get( data, [ 'chunk', 'entryModule' ], {} ); - switch ( entryModule.type ) { - case 'javascript/auto': - rawRequest = entryModule.rawRequest; - break; - - case 'javascript/esm': - rawRequest = entryModule.rootModule.rawRequest; - break; - } - - if ( rawRequest ) { - return basename( rawRequest ); - } - - return path; + camelName( path, data ) { + return camelCaseDash( data.chunk.name ); }, } ), - new LibraryExportDefaultPlugin( - [ - 'api-fetch', - 'deprecated', - 'dom-ready', - 'redux-routine', - 'token-list', - 'server-side-render', - 'shortcode', - 'warning', - ].map( camelCaseDash ) - ), + new LibraryExportDefaultPlugin( [ + 'api-fetch', + 'deprecated', + 'dom-ready', + 'redux-routine', + 'token-list', + 'server-side-render', + 'shortcode', + 'warning', + ] ), new CopyWebpackPlugin( gutenbergPackages.map( ( packageName ) => ( { from: `./packages/${ packageName }/build-style/*.css`,