Build: Split packages and blocks to their webpack configs

It aligns with the changes proposed added in Gutenberg: https://github.com/WordPress/gutenberg/pull/33293.

The idea here is to split the growing webpack config into two parts: blocks and  packages.

We need to add handling for JavaScript files that are going to be used with blocks on the frontend. They didn't work quite well with the current setup for entry points created for packages.

As part of the effort, it adds support for `viewScript` in `block.json` metadata file that is later translated to `$view_script` in `WP_Block_Type` class and exposed as `view_script` from the REST API endpoint for block types.

Props youknowriad, desrosj, aristath.
Fixes .



git-svn-id: https://develop.svn.wordpress.org/trunk@51501 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
gziolo 2021-07-28 10:05:01 +00:00
parent 31c328d990
commit ad976addb3
14 changed files with 290 additions and 126 deletions

2
.gitignore vendored

@ -29,6 +29,8 @@ wp-tests-config.php
/src/wp-includes/css/*.min.css
/src/wp-includes/css/*-rtl.css
/src/wp-includes/blocks/**/*.css
/src/wp-includes/blocks/**/*.js
/src/wp-includes/blocks/**/*.js.map
/packagehash.txt
/artifacts

@ -997,6 +997,7 @@ module.exports = function(grunt) {
WORKING_DIR + 'wp-{admin,includes}/**/*.js',
WORKING_DIR + 'wp-content/themes/twenty*/**/*.js',
'!' + WORKING_DIR + 'wp-content/themes/twenty*/node_modules/**/*.js',
'!' + WORKING_DIR + 'wp-includes/blocks/**/*.js',
'!' + WORKING_DIR + 'wp-includes/js/dist/**/*.js',
]
}

@ -42,12 +42,16 @@ function generate_block_asset_handle( $block_name, $field_name ) {
if ( 0 === strpos( $field_name, 'editor' ) ) {
$asset_handle .= '-editor';
}
if ( 0 === strpos( $field_name, 'view' ) ) {
$asset_handle .= '-view';
}
return $asset_handle;
}
$field_mappings = array(
'editorScript' => 'editor-script',
'script' => 'script',
'viewScript' => 'view-script',
'editorStyle' => 'editor-style',
'style' => 'style',
);
@ -96,18 +100,23 @@ function register_block_script_handle( $metadata, $field_name ) {
);
return false;
}
$script_asset = require $script_asset_path;
$result = wp_register_script(
$is_core_block = isset( $metadata['file'] ) && 0 === strpos( $metadata['file'], ABSPATH . WPINC );
$script_uri = $is_core_block ?
includes_url( str_replace( ABSPATH . WPINC, '', realpath( dirname( $metadata['file'] ) . '/' . $script_path ) ) ) :
plugins_url( $script_path, $metadata['file'] );
$script_asset = require $script_asset_path;
$script_dependencies = isset( $script_asset['dependencies'] ) ? $script_asset['dependencies'] : array();
$result = wp_register_script(
$script_handle,
plugins_url( $script_path, $metadata['file'] ),
$script_asset['dependencies'],
$script_asset['version']
$script_uri,
$script_dependencies,
isset( $script_asset['version'] ) ? $script_asset['version'] : false
);
if ( ! $result ) {
return false;
}
if ( ! empty( $metadata['textdomain'] ) ) {
if ( ! empty( $metadata['textdomain'] ) && in_array( 'wp-i18n', $script_dependencies ) ) {
wp_set_script_translations( $script_handle, $metadata['textdomain'] );
}
@ -182,6 +191,7 @@ function register_block_style_handle( $metadata, $field_name ) {
* Registers a block type from the metadata stored in the `block.json` file.
*
* @since 5.5.0
* @since 5.9.0 Added support for the `viewScript` field.
*
* @param string $file_or_folder Path to the JSON file with metadata definition for
* the block or path to the folder where the `block.json` file is located.
@ -304,6 +314,13 @@ function register_block_type_from_metadata( $file_or_folder, $args = array() ) {
);
}
if ( ! empty( $metadata['viewScript'] ) ) {
$settings['view_script'] = register_block_script_handle(
$metadata,
'viewScript'
);
}
if ( ! empty( $metadata['editorStyle'] ) ) {
$settings['editor_style'] = register_block_style_handle(
$metadata,

@ -0,0 +1 @@
<?php return array('dependencies' => array('wp-polyfill'), 'version' => '499eaf2efb98327a07f222e92d742380');

@ -0,0 +1 @@
<?php return array('dependencies' => array('wp-polyfill'), 'version' => 'e8d668b8e69d9bf1c99dc250d92f2b72');

@ -156,7 +156,7 @@ class WP_Block_Type {
public $provides_context = null;
/**
* Block type editor script handle.
* Block type editor only script handle.
*
* @since 5.0.0
* @var string|null
@ -164,7 +164,7 @@ class WP_Block_Type {
public $editor_script = null;
/**
* Block type front end script handle.
* Block type front end and editor script handle.
*
* @since 5.0.0
* @var string|null
@ -172,7 +172,15 @@ class WP_Block_Type {
public $script = null;
/**
* Block type editor style handle.
* Block type front end only script handle.
*
* @since 5.9.0
* @var string|null
*/
public $view_script = null;
/**
* Block type editor only style handle.
*
* @since 5.0.0
* @var string|null
@ -180,7 +188,7 @@ class WP_Block_Type {
public $editor_style = null;
/**
* Block type front end style handle.
* Block type front end and editor style handle.
*
* @since 5.0.0
* @var string|null
@ -198,6 +206,7 @@ class WP_Block_Type {
* `uses_context`, and `provides_context` properties.
* @since 5.6.0 Added the `api_version` property.
* @since 5.8.0 Added the `variations` property.
* @since 5.9.0 Added the `view_script` property.
*
* @see register_block_type()
*
@ -225,10 +234,11 @@ class WP_Block_Type {
* @type array|null $attributes Block type attributes property schemas.
* @type array $uses_context Context values inherited by blocks of this type.
* @type array|null $provides_context Context provided by blocks of this type.
* @type string|null $editor_script Block type editor script handle.
* @type string|null $script Block type front end script handle.
* @type string|null $editor_style Block type editor style handle.
* @type string|null $style Block type front end style handle.
* @type string|null $editor_script Block type editor only script handle.
* @type string|null $script Block type front end and editor script handle.
* @type string|null $view_script Block type front end only script handle.
* @type string|null $editor_style Block type editor only style handle.
* @type string|null $style Block type front end and editor style handle.
* }
*/
public function __construct( $block_type, $args = array() ) {

@ -229,6 +229,10 @@ class WP_Block {
wp_enqueue_script( $this->block_type->script );
}
if ( ! empty( $this->block_type->view_script ) && empty( $this->block_type->render_callback ) ) {
wp_enqueue_script( $this->block_type->view_script );
}
if ( ! empty( $this->block_type->style ) ) {
wp_enqueue_style( $this->block_type->style );
}

@ -272,6 +272,7 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
'example',
'editor_script',
'script',
'view_script',
'editor_style',
'style',
'variations',
@ -517,6 +518,13 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
'readonly' => true,
),
'script' => array(
'description' => __( 'Public facing and editor script handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'context' => array( 'embed', 'view', 'edit' ),
'readonly' => true,
),
'view_script' => array(
'description' => __( 'Public facing script handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
@ -531,7 +539,7 @@ class WP_REST_Block_Types_Controller extends WP_REST_Controller {
'readonly' => true,
),
'style' => array(
'description' => __( 'Public facing style handle.' ),
'description' => __( 'Public facing and editor style handle.' ),
'type' => array( 'string', 'null' ),
'default' => null,
'context' => array( 'embed', 'view', 'edit' ),

@ -48,6 +48,7 @@
},
"editorScript": "tests-notice-editor-script",
"script": "tests-notice-script",
"viewScript": "tests-notice-view-script",
"editorStyle": "tests-notice-editor-style",
"style": "tests-notice-style"
}

@ -132,6 +132,10 @@ class Tests_Blocks_Register extends WP_UnitTestCase {
'unit-tests-my-block-script',
generate_block_asset_handle( $block_name, 'script' )
);
$this->assertSame(
'unit-tests-my-block-view-script',
generate_block_asset_handle( $block_name, 'viewScript' )
);
$this->assertSame(
'unit-tests-my-block-editor-style',
generate_block_asset_handle( $block_name, 'editorStyle' )
@ -156,6 +160,10 @@ class Tests_Blocks_Register extends WP_UnitTestCase {
'wp-block-paragraph',
generate_block_asset_handle( $block_name, 'script' )
);
$this->assertSame(
'wp-block-paragraph-view',
generate_block_asset_handle( $block_name, 'viewScript' )
);
$this->assertSame(
'wp-block-paragraph-editor',
generate_block_asset_handle( $block_name, 'editorStyle' )
@ -372,6 +380,7 @@ class Tests_Blocks_Register extends WP_UnitTestCase {
);
$this->assertSame( 'tests-notice-editor-script', $result->editor_script );
$this->assertSame( 'tests-notice-script', $result->script );
$this->assertSame( 'tests-notice-view-script', $result->view_script );
$this->assertSame( 'tests-notice-editor-style', $result->editor_style );
$this->assertSame( 'tests-notice-style', $result->style );

@ -215,6 +215,7 @@ class REST_Block_Type_Controller_Test extends WP_Test_REST_Controller_Testcase {
'category' => true,
'editor_script' => true,
'script' => true,
'view_script' => true,
'editor_style' => true,
'style' => true,
'keywords' => 'invalid_keywords',
@ -237,6 +238,7 @@ class REST_Block_Type_Controller_Test extends WP_Test_REST_Controller_Testcase {
$this->assertNull( $data['icon'] );
$this->assertNull( $data['editor_script'] );
$this->assertNull( $data['script'] );
$this->assertNull( $data['view_script'] );
$this->assertNull( $data['editor_style'] );
$this->assertNull( $data['style'] );
$this->assertSameSets( array(), $data['provides_context'] );
@ -268,6 +270,7 @@ class REST_Block_Type_Controller_Test extends WP_Test_REST_Controller_Testcase {
'category' => false,
'editor_script' => false,
'script' => false,
'view_script' => false,
'editor_style' => false,
'style' => false,
'keywords' => false,
@ -290,6 +293,7 @@ class REST_Block_Type_Controller_Test extends WP_Test_REST_Controller_Testcase {
$this->assertNull( $data['icon'] );
$this->assertNull( $data['editor_script'] );
$this->assertNull( $data['script'] );
$this->assertNull( $data['view_script'] );
$this->assertNull( $data['editor_style'] );
$this->assertNull( $data['style'] );
$this->assertSameSets( array(), $data['attributes'] );
@ -374,7 +378,7 @@ class REST_Block_Type_Controller_Test extends WP_Test_REST_Controller_Testcase {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$properties = $data['schema']['properties'];
$this->assertCount( 21, $properties );
$this->assertCount( 22, $properties );
$this->assertArrayHasKey( 'api_version', $properties );
$this->assertArrayHasKey( 'title', $properties );
$this->assertArrayHasKey( 'icon', $properties );
@ -389,6 +393,7 @@ class REST_Block_Type_Controller_Test extends WP_Test_REST_Controller_Testcase {
$this->assertArrayHasKey( 'is_dynamic', $properties );
$this->assertArrayHasKey( 'editor_script', $properties );
$this->assertArrayHasKey( 'script', $properties );
$this->assertArrayHasKey( 'view_script', $properties );
$this->assertArrayHasKey( 'editor_style', $properties );
$this->assertArrayHasKey( 'style', $properties );
$this->assertArrayHasKey( 'parent', $properties );
@ -500,6 +505,7 @@ class REST_Block_Type_Controller_Test extends WP_Test_REST_Controller_Testcase {
'category',
'editor_script',
'script',
'view_script',
'editor_style',
'style',
'title',

212
tools/webpack/blocks.js Normal file

@ -0,0 +1,212 @@
/**
* External dependencies
*/
const { DefinePlugin } = require( 'webpack' );
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
const postcss = require( 'postcss' );
const UglifyJS = require( 'uglify-js' );
const { join, basename } = require( 'path' );
const { get } = require( 'lodash' );
/**
* WordPress dependencies
*/
const DependencyExtractionPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );
const baseDir = join( __dirname, '../../' );
module.exports = function( env = { environment: 'production', watch: false, buildTarget: false } ) {
const mode = env.environment;
const suffix = mode === 'production' ? '.min' : '';
let buildTarget = env.buildTarget ? env.buildTarget : ( mode === 'production' ? 'build' : 'src' );
buildTarget = buildTarget + '/wp-includes';
const dynamicBlockFolders = [
'archives',
'block',
'calendar',
'categories',
'file',
'latest-comments',
'latest-posts',
'loginout',
'page-list',
'post-content',
'post-date',
'post-excerpt',
'post-featured-image',
'post-terms',
'post-title',
'post-template',
'query',
'query-pagination',
'query-pagination-next',
'query-pagination-numbers',
'query-pagination-previous',
'query-title',
'rss',
'search',
'shortcode',
'site-logo',
'site-tagline',
'site-title',
'social-link',
'tag-cloud',
];
const blockFolders = [
'audio',
'button',
'buttons',
'code',
'column',
'columns',
'cover',
'embed',
'freeform',
'gallery',
'group',
'heading',
'html',
'image',
'list',
'media-text',
'missing',
'more',
'nextpage',
'paragraph',
'preformatted',
'pullquote',
'quote',
'separator',
'social-links',
'spacer',
'table',
'text-columns',
'verse',
'video',
...dynamicBlockFolders,
];
const blockPHPFiles = {
'widgets/src/blocks/legacy-widget/index.php': 'wp-includes/blocks/legacy-widget.php',
...dynamicBlockFolders.reduce( ( files, blockName ) => {
files[ `block-library/src/${ blockName }/index.php` ] = `wp-includes/blocks/${ blockName }.php`;
return files;
} , {} ),
};
const blockMetadataFiles = {
'widgets/src/blocks/legacy-widget/block.json': 'wp-includes/blocks/legacy-widget/block.json',
...blockFolders.reduce( ( files, blockName ) => {
files[ `block-library/src/${ blockName }/block.json` ] = `wp-includes/blocks/${ blockName }/block.json`;
return files;
} , {} ),
};
const blockPHPCopies = Object.keys( blockPHPFiles ).map( ( filename ) => ( {
from: join( baseDir, `node_modules/@wordpress/${ filename }` ),
to: join( baseDir, `src/${ blockPHPFiles[ filename ] }` ),
} ) );
const blockMetadataCopies = Object.keys( blockMetadataFiles ).map( ( filename ) => ( {
from: join( baseDir, `node_modules/@wordpress/${ filename }` ),
to: join( baseDir, `src/${ blockMetadataFiles[ filename ] }` ),
} ) );
const blockStylesheetCopies = blockFolders.map( ( blockName ) => ( {
from: join( baseDir, `node_modules/@wordpress/block-library/build-style/${ blockName }/*.css` ),
to: join( baseDir, `${ buildTarget }/blocks/${ blockName }/` ),
flatten: true,
transform: ( content ) => {
if ( mode === 'production' ) {
return postcss( [
require( 'cssnano' )( {
preset: 'default',
} ),
] )
.process( content, { from: 'src/app.css', to: 'dest/app.css' } )
.then( ( result ) => result.css );
}
return content;
},
transformPath: ( targetPath, sourcePath ) => {
if ( mode === 'production' ) {
return targetPath.replace( /\.css$/, '.min.css' );
}
return targetPath;
}
} ) );
const config = {
mode,
entry: {
'file/view': join( baseDir, `node_modules/@wordpress/block-library/build-module/file/view` ),
},
output: {
devtoolNamespace: 'wp',
filename: `[name]${ suffix }.js`,
path: join( baseDir, `${ buildTarget }/blocks` ),
},
resolve: {
modules: [
baseDir,
'node_modules',
],
alias: {
'lodash-es': 'lodash',
},
},
module: {
rules: [
{
test: /\.js$/,
use: [ 'source-map-loader' ],
enforce: 'pre',
},
],
},
optimization: {
moduleIds: mode === 'production' ? 'hashed' : 'named',
},
plugins: [
new DefinePlugin( {
// Inject the `GUTENBERG_PHASE` global, used for feature flagging.
'process.env.GUTENBERG_PHASE': 1,
'process.env.FORCE_REDUCED_MOTION': JSON.stringify(
process.env.FORCE_REDUCED_MOTION
),
} ),
new DependencyExtractionPlugin( {
injectPolyfill: true,
} ),
new CopyWebpackPlugin(
[
...blockPHPCopies,
...blockMetadataCopies,
...blockStylesheetCopies,
],
),
],
stats: {
children: false,
},
watch: env.watch,
};
if ( config.mode !== 'production' ) {
config.devtool = process.env.SOURCEMAP || 'source-map';
}
if ( mode === 'development' && env.buildTarget === 'build/' ) {
delete config.devtool;
config.mode = 'production';
config.optimization = {
minimize: false,
moduleIds: 'hashed',
};
}
return config;
};

@ -106,85 +106,8 @@ module.exports = function( env = { environment: 'production', watch: false, buil
'wp-polyfill-dom-rect.min.js': 'polyfill-library/polyfills/__dist/DOMRect/raw.js',
};
const dynamicBlockFolders = [
'archives',
'block',
'calendar',
'categories',
'file',
'latest-comments',
'latest-posts',
'loginout',
'page-list',
'post-content',
'post-date',
'post-excerpt',
'post-featured-image',
'post-terms',
'post-title',
'post-template',
'query',
'query-pagination',
'query-pagination-next',
'query-pagination-numbers',
'query-pagination-previous',
'query-title',
'rss',
'search',
'shortcode',
'site-logo',
'site-tagline',
'site-title',
'social-link',
'tag-cloud',
];
const blockFolders = [
'audio',
'button',
'buttons',
'code',
'column',
'columns',
'cover',
'embed',
'freeform',
'gallery',
'group',
'heading',
'html',
'image',
'list',
'media-text',
'missing',
'more',
'nextpage',
'paragraph',
'preformatted',
'pullquote',
'quote',
'separator',
'social-links',
'spacer',
'table',
'text-columns',
'verse',
'video',
...dynamicBlockFolders,
];
const phpFiles = {
'block-serialization-default-parser/parser.php': 'wp-includes/class-wp-block-parser.php',
'widgets/src/blocks/legacy-widget/index.php': 'wp-includes/blocks/legacy-widget.php',
...dynamicBlockFolders.reduce( ( files, blockName ) => {
files[ `block-library/src/${ blockName }/index.php` ] = `wp-includes/blocks/${ blockName }.php`;
return files;
} , {} ),
};
const blockMetadataFiles = {
'widgets/src/blocks/legacy-widget/block.json': 'wp-includes/blocks/legacy-widget/block.json',
...blockFolders.reduce( ( files, blockName ) => {
files[ `block-library/src/${ blockName }/block.json` ] = `wp-includes/blocks/${ blockName }/block.json`;
return files;
} , {} ),
};
const developmentCopies = mapVendorCopies( vendors, buildTarget );
@ -231,37 +154,6 @@ module.exports = function( env = { environment: 'production', watch: false, buil
to: join( baseDir, `src/${ phpFiles[ filename ] }` ),
} ) );
const blockMetadataCopies = Object.keys( blockMetadataFiles ).map( ( filename ) => ( {
from: join( baseDir, `node_modules/@wordpress/${ filename }` ),
to: join( baseDir, `src/${ blockMetadataFiles[ filename ] }` ),
} ) );
const blockStylesheetCopies = blockFolders.map( ( blockName ) => ( {
from: join( baseDir, `node_modules/@wordpress/block-library/build-style/${ blockName }/*.css` ),
to: join( baseDir, `${ buildTarget }/blocks/${ blockName }/` ),
flatten: true,
transform: ( content ) => {
if ( mode === 'production' ) {
return postcss( [
require( 'cssnano' )( {
preset: 'default',
} ),
] )
.process( content, { from: 'src/app.css', to: 'dest/app.css' } )
.then( ( result ) => result.css );
}
return content;
},
transformPath: ( targetPath, sourcePath ) => {
if ( mode === 'production' ) {
return targetPath.replace( /\.css$/, '.min.css' );
}
return targetPath;
}
} ) );
const config = {
mode,
@ -352,8 +244,6 @@ module.exports = function( env = { environment: 'production', watch: false, buil
...vendorCopies,
...cssCopies,
...phpCopies,
...blockMetadataCopies,
...blockStylesheetCopies,
],
),
],

@ -1,3 +1,4 @@
const blocksConfig = require( './tools/webpack/blocks' );
const mediaConfig = require( './tools/webpack/media' );
const packagesConfig = require( './tools/webpack/packages' );
@ -11,6 +12,7 @@ module.exports = function( env = { environment: "production", watch: false, buil
}
const config = [
blocksConfig( env ),
mediaConfig( env ),
packagesConfig( env ),
];