Skip to content

Commit

Permalink
Build: Prepare for more Script Modules
Browse files Browse the repository at this point in the history
This is a companion to WordPress/gutenberg#65460 that requires syncing in WordPress Core. Namely, the block-library changes require registration with their updated script module IDs so that the blocks continue to work correctly.

They key improvement is script modules registration is handled in one central place, and a combined asset file is used to improve the performance by avoiding multiple disk operations for every individual file.

Props jonsurrell, gziolo, wildworks, noisysocks.
See #60647, #59462.



git-svn-id: https://develop.svn.wordpress.org/trunk@59083 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
gziolo committed Sep 24, 2024
1 parent a390799 commit 0b8b804
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 95 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ wp-tests-config.php
/src/wp-admin/js
/src/wp-includes/assets/*
!/src/wp-includes/assets/script-loader-packages.min.php
!/src/wp-includes/assets/script-modules-packages.min.php
/src/wp-includes/js
/src/wp-includes/css/dist
/src/wp-includes/css/*.min.css
Expand Down
1 change: 1 addition & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ module.exports = function(grunt) {
'wp-includes/css/dist',
'wp-includes/blocks/**/*.css',
'!wp-includes/assets/script-loader-packages.min.php',
'!wp-includes/assets/script-modules-packages.min.php',
],

// Prepend `dir` to `file`, and keep `!` in place.
Expand Down
1 change: 1 addition & 0 deletions src/wp-includes/assets/script-modules-packages.min.php
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php return array('interactivity/index.min.js' => array('dependencies' => array(), 'version' => '2d6d1fdbcb3fda39c768', 'type' => 'module'), 'interactivity/debug.min.js' => array('dependencies' => array(), 'version' => '1ccc67b05c275e51a8f8', 'type' => 'module'), 'interactivity-router/index.min.js' => array('dependencies' => array('@wordpress/interactivity'), 'version' => '64645ef3cd2d32860d7d', 'type' => 'module'), 'block-library/file/view.min.js' => array('dependencies' => array('@wordpress/interactivity'), 'version' => 'fdc2f6842e015af83140', 'type' => 'module'), 'block-library/image/view.min.js' => array('dependencies' => array('@wordpress/interactivity'), 'version' => 'acfec7b3c0be4a859b31', 'type' => 'module'), 'block-library/navigation/view.min.js' => array('dependencies' => array('@wordpress/interactivity'), 'version' => '8ff192874fc8910a284c', 'type' => 'module'), 'block-library/query/view.min.js' => array('dependencies' => array('@wordpress/interactivity', array('id' => '@wordpress/interactivity-router', 'import' => 'dynamic')), 'version' => 'f4c91c89fa5271f3dad9', 'type' => 'module'), 'block-library/search/view.min.js' => array('dependencies' => array('@wordpress/interactivity'), 'version' => '2a73400a693958f604de', 'type' => 'module'));
1 change: 1 addition & 0 deletions src/wp-includes/default-filters.php
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@
// Script Loader.
add_action( 'wp_default_scripts', 'wp_default_scripts' );
add_action( 'wp_default_scripts', 'wp_default_packages' );
add_action( 'wp_default_scripts', 'wp_default_script_modules' );

add_action( 'wp_enqueue_scripts', 'wp_localize_jquery_ui_datepicker', 1000 );
add_action( 'wp_enqueue_scripts', 'wp_common_block_scripts_and_styles' );
Expand Down
19 changes: 3 additions & 16 deletions src/wp-includes/interactivity-api/class-wp-interactivity-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,33 +281,20 @@ public function get_context( ?string $store_namespace = null ): array {
/**
* Registers the `@wordpress/interactivity` script modules.
*
* @deprecated 6.7.0 Script Modules registration is handled by {@see wp_default_script_modules()}.
*
* @since 6.5.0
*/
public function register_script_modules() {
$suffix = wp_scripts_get_suffix();

wp_register_script_module(
'@wordpress/interactivity',
includes_url( "js/dist/interactivity$suffix.js" )
);

wp_register_script_module(
'@wordpress/interactivity-router',
includes_url( "js/dist/interactivity-router$suffix.js" ),
array( '@wordpress/interactivity' )
);
_deprecated_function( __METHOD__, '6.7.0', 'wp_default_script_modules' );
}

/**
* Adds the necessary hooks for the Interactivity API.
*
* @since 6.5.0
* @since 6.7.0 Use the {@see "script_module_data_{$module_id}"} filter to pass client-side data.
*/
public function add_hooks() {
add_action( 'wp_enqueue_scripts', array( $this, 'register_script_modules' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'register_script_modules' ) );

add_filter( 'script_module_data_@wordpress/interactivity', array( $this, 'filter_script_module_interactivity_data' ) );
}

Expand Down
50 changes: 50 additions & 0 deletions src/wp-includes/script-modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,53 @@ function wp_dequeue_script_module( string $id ) {
function wp_deregister_script_module( string $id ) {
wp_script_modules()->deregister( $id );
}

/**
* Registers all the default WordPress Script Modules.
*
* @since 6.7.0
*/
function wp_default_script_modules() {
$suffix = defined( 'WP_RUN_CORE_TESTS' ) ? '.min' : wp_scripts_get_suffix();

/*
* Expects multidimensional array like:
*
* 'interactivity/index.min.js' => array('dependencies' => array(…), 'version' => '…'),
* 'interactivity/debug.min.js' => array('dependencies' => array(…), 'version' => '…'),
* 'interactivity-router/index.min.js' => …
*/
$assets = include ABSPATH . WPINC . "/assets/script-modules-packages{$suffix}.php";

foreach ( $assets as $file_name => $script_module_data ) {
/*
* Build the WordPress Script Module ID from the file name.
* Prepend `@wordpress/` and remove extensions and `/index` if present:
* - interactivity/index.min.js => @wordpress/interactivity
* - interactivity/debug.min.js => @wordpress/interactivity/debug
* - block-library/query/view.js => @wordpress/block-library/query/view
*/
$script_module_id = '@wordpress/' . preg_replace( '~(?:/index)?(?:\.min)?\.js$~D', '', $file_name, 1 );

switch ( $script_module_id ) {
/*
* Interactivity exposes two entrypoints, "/index" and "/debug".
* "/debug" should replalce "/index" in devlopment.
*/
case '@wordpress/interactivity/debug':
if ( ! SCRIPT_DEBUG ) {
continue 2;
}
$script_module_id = '@wordpress/interactivity';
break;
case '@wordpress/interactivity':
if ( SCRIPT_DEBUG ) {
continue 2;
}
break;
}

$path = "/wp-includes/js/dist/script-modules/{$file_name}";
wp_register_script_module( $script_module_id, $path, $script_module_data['dependencies'], $script_module_data['version'] );
}
}
12 changes: 11 additions & 1 deletion tests/phpunit/tests/interactivity-api/wpInteractivityAPI.php
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,17 @@ public function test_config_not_printed_when_empty() {
$this->expectOutputString( '' );
}

/**
* Test that the deprecated register_script_modules method is deprecated but does not throw.
*
* @ticket 60647
*
* @expectedDeprecated WP_Interactivity_API::register_script_modules
*/
public function test_register_script_modules_deprecated() {
$this->interactivity->register_script_modules();
}

/**
* Sets up an activity, runs an optional callback, and returns a MockAction for inspection.
*
Expand All @@ -221,7 +232,6 @@ public function test_config_not_printed_when_empty() {
*/
private function get_script_data_filter_result( ?Closure $callback = null ): MockAction {
$this->interactivity->add_hooks();
$this->interactivity->register_script_modules();
wp_enqueue_script_module( '@wordpress/interactivity' );
$filter = new MockAction();
add_filter( 'script_module_data_@wordpress/interactivity', array( $filter, 'filter' ) );
Expand Down
76 changes: 0 additions & 76 deletions tools/webpack/modules.js

This file was deleted.

119 changes: 119 additions & 0 deletions tools/webpack/script-modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* External dependencies
*/
const { createRequire } = require( 'node:module' );
const { dirname } = require( 'node:path' );

/**
* WordPress dependencies
*/
const DependencyExtractionPlugin = require( '@wordpress/dependency-extraction-webpack-plugin' );

/**
* Internal dependencies
*/
const {
baseDir,
getBaseConfig,
normalizeJoin,
MODULES,
SCRIPT_AND_MODULE_DUAL_PACKAGES,
WORDPRESS_NAMESPACE,
} = require( './shared' );

/** @type {Map<string, string>} */
const scriptModules = new Map();
for ( const packageName of MODULES.concat( SCRIPT_AND_MODULE_DUAL_PACKAGES ) ) {
const packageRequire = createRequire(
`${ dirname( require.resolve( `${ packageName }/package.json` ) ) }/`
);

const depPackageJson = packageRequire( './package.json' );
if ( ! Object.hasOwn( depPackageJson, 'wpScriptModuleExports' ) ) {
continue;
}

const moduleName = packageName.substring( WORDPRESS_NAMESPACE.length );
let { wpScriptModuleExports } = depPackageJson;

// Special handling for { "wpScriptModuleExports": "./build-module/index.js" }.
if ( typeof wpScriptModuleExports === 'string' ) {
wpScriptModuleExports = { '.': wpScriptModuleExports };
}

if ( Object.getPrototypeOf( wpScriptModuleExports ) !== Object.prototype ) {
throw new Error( 'wpScriptModuleExports must be an object' );
}

for ( const [ exportName, exportPath ] of Object.entries(
wpScriptModuleExports
) ) {
if ( typeof exportPath !== 'string' ) {
throw new Error( 'wpScriptModuleExports paths must be strings' );
}

if ( ! exportPath.startsWith( './' ) ) {
throw new Error(
'wpScriptModuleExports paths must start with "./"'
);
}

const name =
exportName === '.' ? 'index' : exportName.replace( /^\.\/?/, '' );

scriptModules.set(
`${ moduleName }/${ name }`,
packageRequire.resolve( exportPath )
);
}
}

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 baseConfig = getBaseConfig( env );
const config = {
...baseConfig,
entry: Object.fromEntries( scriptModules.entries() ),
experiments: {
outputModule: true,
},
output: {
devtoolNamespace: 'wp',
filename: `[name]${ suffix }.js`,
path: normalizeJoin(
baseDir,
`${ buildTarget }/js/dist/script-modules`
),
library: {
type: 'module',
},
environment: { module: true },
module: true,
chunkFormat: 'module',
asyncChunks: false,
},
plugins: [
...baseConfig.plugins,
new DependencyExtractionPlugin( {
injectPolyfill: false,
combineAssets: true,
combinedOutputFile: normalizeJoin(
baseDir,
`${ buildTarget }/assets/script-modules-packages${ suffix }.php`
),
} ),
],
};

return config;
};
4 changes: 4 additions & 0 deletions tools/webpack/shared.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ const MODULES = [
'@wordpress/interactivity',
'@wordpress/interactivity-router',
];
const SCRIPT_AND_MODULE_DUAL_PACKAGES = [
'@wordpress/block-library',
];
const WORDPRESS_NAMESPACE = '@wordpress/';

module.exports = {
Expand All @@ -111,5 +114,6 @@ module.exports = {
stylesTransform,
BUNDLED_PACKAGES,
MODULES,
SCRIPT_AND_MODULE_DUAL_PACKAGES,
WORDPRESS_NAMESPACE,
};
4 changes: 2 additions & 2 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const blocksConfig = require( './tools/webpack/blocks' );
const developmentConfig = require( './tools/webpack/development' );
const mediaConfig = require( './tools/webpack/media' );
const packagesConfig = require( './tools/webpack/packages' );
const modulesConfig = require( './tools/webpack/modules' );
const scriptModulesConfig = require( './tools/webpack/script-modules' );
const vendorsConfig = require( './tools/webpack/vendors' );

module.exports = function( env = { environment: "production", watch: false, buildTarget: false } ) {
Expand All @@ -19,7 +19,7 @@ module.exports = function( env = { environment: "production", watch: false, buil
...developmentConfig( env ),
mediaConfig( env ),
packagesConfig( env ),
modulesConfig( env ),
scriptModulesConfig( env ),
...vendorsConfig( env ),
];

Expand Down

0 comments on commit 0b8b804

Please sign in to comment.