diff --git a/.gitignore b/.gitignore index c635d8dc1d5d2..22a1427daa1c1 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,9 @@ 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 +/src/wp-includes/blocks/**/*.asset.php /packagehash.txt /artifacts diff --git a/Gruntfile.js b/Gruntfile.js index a24e701e0637e..6f5a80ae15e8b 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -996,6 +996,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', ] } diff --git a/src/wp-includes/blocks.php b/src/wp-includes/blocks.php index 8f2cd7497990a..c60ddf18e0096 100644 --- a/src/wp-includes/blocks.php +++ b/src/wp-includes/blocks.php @@ -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'] ); } @@ -306,6 +315,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, diff --git a/src/wp-includes/class-wp-block-type.php b/src/wp-includes/class-wp-block-type.php index 84e956ec9a647..d5f34a25207b0 100644 --- a/src/wp-includes/class-wp-block-type.php +++ b/src/wp-includes/class-wp-block-type.php @@ -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.0.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 @@ -225,10 +233,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() ) { diff --git a/tests/phpunit/data/blocks/notice/block.json b/tests/phpunit/data/blocks/notice/block.json index 9a0ecc018da64..610a96099f90a 100644 --- a/tests/phpunit/data/blocks/notice/block.json +++ b/tests/phpunit/data/blocks/notice/block.json @@ -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" } diff --git a/tests/phpunit/tests/blocks/register.php b/tests/phpunit/tests/blocks/register.php index 7388fd6ab6e10..b8b9fc1aad661 100644 --- a/tests/phpunit/tests/blocks/register.php +++ b/tests/phpunit/tests/blocks/register.php @@ -132,6 +132,10 @@ function test_generate_block_asset_handle() { '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 @@ function test_generate_block_asset_handle_core_block() { '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 @@ function test_block_registers_with_metadata_fixture() { ); $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 ); diff --git a/tools/webpack/blocks.js b/tools/webpack/blocks.js new file mode 100644 index 0000000000000..bf7e1dff24c94 --- /dev/null +++ b/tools/webpack/blocks.js @@ -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; +}; diff --git a/tools/webpack/packages.js b/tools/webpack/packages.js index 80840b8b90595..cc94118dbed57 100644 --- a/tools/webpack/packages.js +++ b/tools/webpack/packages.js @@ -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, ], ), ], diff --git a/webpack.config.js b/webpack.config.js index bc6ab59bee0e0..5b7b7b632de02 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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 ), ];