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

Build: Prepare build for more script modules #65064

Merged
merged 17 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/bundle-size.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ jobs:
- uses: preactjs/compressed-size-action@f780fd104362cfce9e118f9198df2ee37d12946c # v2.6.0
with:
repo-token: '${{ secrets.GITHUB_TOKEN }}'
pattern: '{build/**/*.min.js,build/**/*.css}'
pattern: '{build/**/*.min.js,build/**/*.css,build-module/**/*.min.js}'
clean-script: 'distclean'
1 change: 1 addition & 0 deletions bin/build-plugin-zip.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ zip -r gutenberg.zip \
packages/block-serialization-default-parser/*.php \
post-content.php \
$build_files \
build-module \
Copy link
Member

@gziolo gziolo Sep 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember why there was pattern matching used for files in the build folder. @youknowriad, is there anything important we might miss when including the entire build-module? At the moment, we want all files to be included from there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created related PR #65064

readme.txt \
changelog.txt \
README.md
Expand Down
4 changes: 2 additions & 2 deletions lib/interactivity-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ function gutenberg_reregister_interactivity_script_modules() {

wp_register_script_module(
'@wordpress/interactivity',
gutenberg_url( '/build/interactivity/' . ( SCRIPT_DEBUG ? 'debug.min.js' : 'index.min.js' ) ),
gutenberg_url( '/build-module/' . ( SCRIPT_DEBUG ? 'interactivity/debug.min.js' : 'interactivity/index.min.js' ) ),
array(),
$default_version
);

wp_register_script_module(
'@wordpress/interactivity-router',
gutenberg_url( '/build/interactivity/router.min.js' ),
gutenberg_url( '/build-module/interactivity-router/index.min.js' ),
array( '@wordpress/interactivity' ),
$default_version
);
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"@wordpress/i18n": "file:packages/i18n",
"@wordpress/icons": "file:packages/icons",
"@wordpress/interactivity": "file:packages/interactivity",
"@wordpress/interactivity-router": "file:packages/interactivity-router",
"@wordpress/interface": "file:packages/interface",
"@wordpress/is-shallow-equal": "file:packages/is-shallow-equal",
"@wordpress/keyboard-shortcuts": "file:packages/keyboard-shortcuts",
Expand Down
7 changes: 7 additions & 0 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@
"src/**/*.scss",
"{src,build,build-module}/*/init.js"
],
"wpScriptModuleExports": {
"./file/view": "./build-module/file/view.js",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It starts to look like exports from package.json. Nice! 👍🏻

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I think this is going to be a very good direction. It moves in the direction of exports, but doesn't switch all the packages immediately.

It's also a custom field, and these are custom builds in general specific for use as WordPress Script Modules. It makes it clear that they're not intended for general consumption.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think we can move in the direction of generating the entrypoints in a standard way by reading package.json files.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it doesn't work 🙃 (I thought it did but I'm not sure why that was).

Problems:
When entrypoints are paths (./packages/block-library/file/view), webpack never sees the package.json in order to find these paths.

ERROR in block-library/file/view
Module not found: Error: Can't resolve './packages/block-library/file/view' in '…'

When entrypoints are to modules (@wordpress/block-library/file/view) webpack resolves these correctly, but they're handled as externals. DEWP rejects these. We could fix DEWP, but even then we have entrypoints like @wordpress/interactivity which DEWP will externalize and there doesn't seem to be a good way to say "Externalize this except when it's the entrypoint." Externalizing entrypoints is, needless to say, bad and broken 🙂

So I don't think actually using this field as webpack exportsFields is going to work, but we can read this field and build a path to it programatically.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I don't think actually using this field as webpack exportsFields is going to work, but we can read this field and build a path to it programatically.

That sounds like a great first step with some thin compact layer in the webpack config that scans package.json files and produces entry points that are hardcoded today. For blocks you could eventually use pattern matching.

"./image/view": "./build-module/image/view.js",
"./navigation/view": "./build-module/navigation/view.js",
"./query/view": "./build-module/query/view.js",
"./search/view": "./build-module/search/view.js"
},
"dependencies": {
"@babel/runtime": "^7.16.0",
"@wordpress/a11y": "file:../a11y",
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/file/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function render_block_core_file( $attributes, $content ) {
if ( ! empty( $attributes['displayPreview'] ) ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
$module_url = gutenberg_url( '/build/interactivity/file.min.js' );
$module_url = gutenberg_url( '/build-module/block-library/file/view.min.js' );
}

wp_register_script_module(
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/image/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function render_block_core_image( $attributes, $content, $block ) {
) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
$module_url = gutenberg_url( '/build/interactivity/image.min.js' );
$module_url = gutenberg_url( '/build-module/block-library/image/view.min.js' );
}

wp_register_script_module(
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/navigation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ private static function handle_view_script_module_loading( $attributes, $block,
if ( static::is_interactive( $attributes, $inner_blocks ) ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
$module_url = gutenberg_url( '/build/interactivity/navigation.min.js' );
$module_url = gutenberg_url( '/build-module/block-library/navigation/view.min.js' );
}

wp_register_script_module(
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/query/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function render_block_core_query( $attributes, $content, $block ) {
if ( $is_interactive ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
$module_url = gutenberg_url( '/build/interactivity/query.min.js' );
$module_url = gutenberg_url( '/build-module/block-library/query/view.min.js' );
}

wp_register_script_module(
Expand Down
2 changes: 1 addition & 1 deletion packages/block-library/src/search/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function render_block_core_search( $attributes ) {
if ( $is_expandable_searchfield ) {
$suffix = wp_scripts_get_suffix();
if ( defined( 'IS_GUTENBERG_PLUGIN' ) && IS_GUTENBERG_PLUGIN ) {
$module_url = gutenberg_url( '/build/interactivity/search.min.js' );
$module_url = gutenberg_url( '/build-module/block-library/search/view.min.js' );
}

wp_register_script_module(
Expand Down
1 change: 1 addition & 0 deletions packages/interactivity-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"module": "build-module/index.js",
"react-native": "src/index",
"types": "build-types",
"wpScriptModuleExports": "./build-module/index.js",
"dependencies": {
"@wordpress/interactivity": "file:../interactivity"
},
Expand Down
4 changes: 4 additions & 0 deletions packages/interactivity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
"module": "build-module/index.js",
"react-native": "src/index",
"types": "build-types",
"wpScriptModuleExports": {
".": "./build-module/index.js",
"./debug": "./build-module/debug.js"
},
"dependencies": {
"@preact/signals": "^1.2.2",
"preact": "^10.19.3"
Expand Down
74 changes: 0 additions & 74 deletions tools/webpack/interactivity.js

This file was deleted.

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

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

/**
* Internal dependencies
*/
const { baseConfig, plugins } = require( './shared' );

const WORDPRESS_NAMESPACE = '@wordpress/';
const { createRequire } = require( 'node:module' );

const rootURL = new URL( '..', `file://${ __dirname }` );
const fromRootRequire = createRequire( rootURL );

/** @type {Iterable<[string, string]>} */
const iterableDeps = Object.entries(
fromRootRequire( './package.json' ).dependencies
);

/** @type {Map<string, string>} */
const gutenbergScriptModules = new Map();
for ( const [ packageName, versionSpecifier ] of iterableDeps ) {
if (
! packageName.startsWith( WORDPRESS_NAMESPACE ) ||
! versionSpecifier.startsWith( 'file:' ) ||
packageName.startsWith( WORDPRESS_NAMESPACE + 'react-native' )
) {
continue;
}

const packageRequire = createRequire(
// Remove the leading "file:" specifier to build a package URL.
new URL( `${ versionSpecifier.substring( 5 ) }/`, rootURL )
);

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( /^\.\/?/, '' );

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

module.exports = {
...baseConfig,
name: 'script-modules',
entry: Object.fromEntries( gutenbergScriptModules.entries() ),
experiments: {
outputModule: true,
},
output: {
devtoolNamespace: 'wp',
filename: './build-module/[name].min.js',
library: {
type: 'module',
},
path: join( __dirname, '..', '..' ),
environment: { module: true },
module: true,
chunkFormat: 'module',
asyncChunks: false,
},
resolve: {
extensions: [ '.js', '.ts', '.tsx' ],
},
module: {
rules: [
{
test: /\.(j|t)sx?$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve( 'babel-loader' ),
options: {
cacheDirectory:
process.env.BABEL_CACHE_DIRECTORY || true,
babelrc: false,
configFile: false,
presets: [
'@babel/preset-typescript',
'@babel/preset-react',
],
},
},
],
},
],
},
plugins: [ ...plugins, new DependencyExtractionWebpackPlugin() ],
watchOptions: {
ignored: [ '**/node_modules' ],
aggregateTimeout: 500,
},
};
4 changes: 2 additions & 2 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
*/
const blocksConfig = require( './tools/webpack/blocks' );
const developmentConfigs = require( './tools/webpack/development' );
const interactivity = require( './tools/webpack/interactivity' );
const scriptModules = require( './tools/webpack/script-modules' );
const packagesConfig = require( './tools/webpack/packages' );
const vendorsConfig = require( './tools/webpack/vendors' );

module.exports = [
...blocksConfig,
interactivity,
scriptModules,
packagesConfig,
...developmentConfigs,
...vendorsConfig,
Expand Down
Loading