Commit 4f907fe225a for woocommerce
commit 4f907fe225a847849c0a639700f81461d6b30dd6
Author: Daniel Mallory <daniel.mallory@automattic.com>
Date: Tue Apr 14 07:13:58 2026 +0100
Cache admin and blocks webpack builds outside watch mode (#64080)
* perf(build): cache webpack bundles outside watch mode
* fix(build): address webpack cache review feedback
* fix(build): refine webpack cache configuration
* build(admin,blocks): skip filesystem cache in ci
* Monorepo: fix webpack caching issues in admin.
. build:project:bundle: orphan modules 12.2 MiB (javascript) 139 KiB (asset) 159 KiB (css/mini-extract) 30.3 KiB (runtime) [orphan] 6824 modules
. build:project:bundle: runtime modules 269 KiB 1020 modules
. build:project:bundle: built modules 12.3 MiB (javascript) 648 KiB (css/mini-extract) 417 KiB (asset) [built]
vs
. build:project:bundle: cached modules 16.5 MiB (javascript) 807 KiB (css/mini-extract) 448 KiB (asset) 80.2 KiB (runtime) [cached] 7852 modules
. build:project:bundle: orphan modules 546 bytes [orphan] 13 modules
. build:project:bundle: built modules 6.97 MiB [built]
* Monorepo: fix webpack caching issues in admin.
* Monorepo: fix webpack caching issues in blocks.
* Monorepo: account for more dependencies in updated configurations.
* Monorepo: cleanup.
* Monorepo: cleanup.
* Monorepo: cleanup.
* Monorepo: cleanup.
* Monorepo: account for more dependencies in updated configurations.
* chore: update changelog entry for webpack filesystem cache
---------
Co-authored-by: Vladimir Reznichenko <kalessil@gmail.com>
diff --git a/plugins/woocommerce/changelog/build-webpack-filesystem-cache b/plugins/woocommerce/changelog/build-webpack-filesystem-cache
new file mode 100644
index 00000000000..707d3dd60dc
--- /dev/null
+++ b/plugins/woocommerce/changelog/build-webpack-filesystem-cache
@@ -0,0 +1,4 @@
+Significance: minor
+Type: dev
+
+Improve repeated WooCommerce admin and blocks production builds by using webpack filesystem cache outside watch mode.
diff --git a/plugins/woocommerce/client/admin/bin/custom-templated-path-webpack-plugin.js b/plugins/woocommerce/client/admin/bin/custom-templated-path-webpack-plugin.js
index 00e69a4ea31..01ac82563bd 100644
--- a/plugins/woocommerce/client/admin/bin/custom-templated-path-webpack-plugin.js
+++ b/plugins/woocommerce/client/admin/bin/custom-templated-path-webpack-plugin.js
@@ -45,7 +45,7 @@ class CustomTemplatedPathPlugin {
compiler.hooks.compilation.tap(
'CustomTemplatedPathPlugin',
( compilation ) => {
- compilation.mainTemplate.hooks.assetPath.tap(
+ compilation.hooks.assetPath.tap(
'CustomTemplatedPathPlugin',
( path, data ) => {
for ( let i = 0; i < this.handlers.length; i++ ) {
diff --git a/plugins/woocommerce/client/admin/bin/filesystem-cache-warnings-webpack-plugin.js b/plugins/woocommerce/client/admin/bin/filesystem-cache-warnings-webpack-plugin.js
new file mode 100644
index 00000000000..2e4df624c1c
--- /dev/null
+++ b/plugins/woocommerce/client/admin/bin/filesystem-cache-warnings-webpack-plugin.js
@@ -0,0 +1,21 @@
+// ExternalModule instances (wp.*, wc.* externals) are not serializable by webpack's PackFileCacheStrategy.
+// Suppress here since ignoreWarnings only affects compilation warnings, not infrastructure logs.
+
+function FilesystemCacheWarningsPlugin() {}
+
+FilesystemCacheWarningsPlugin.prototype.apply = function ( compiler ) {
+ compiler.hooks.infrastructureLog.tap(
+ 'SuppressExternalModuleCacheWarning',
+ ( name, type, args ) => {
+ if ( type === 'warn' && name === 'webpack.cache.PackFileCacheStrategy' ) {
+ return (
+ args[ 0 ]?.includes?.( 'No serializer registered for ModuleExternalInitFragment' ) ||
+ args[ 0 ]?.includes?.( 'No serializer registered for ExternalModule' ) ||
+ args[ 0 ]?.includes?.( 'No serializer registered for Warning' )
+ );
+ }
+ }
+ );
+};
+
+module.exports = FilesystemCacheWarningsPlugin;
diff --git a/plugins/woocommerce/client/admin/webpack.config.js b/plugins/woocommerce/client/admin/webpack.config.js
index c724e0d2035..d66e9b01c07 100644
--- a/plugins/woocommerce/client/admin/webpack.config.js
+++ b/plugins/woocommerce/client/admin/webpack.config.js
@@ -14,6 +14,7 @@ const ReactRefreshWebpackPlugin = require( '@pmmmwh/react-refresh-webpack-plugin
* Internal dependencies
*/
const CustomTemplatedPathPlugin = require( './bin/custom-templated-path-webpack-plugin' );
+const FilesystemCacheWarningsPlugin = require( './bin/filesystem-cache-warnings-webpack-plugin.js' );
const UnminifyWebpackPlugin = require( './bin/unminify-webpack-plugin.js' );
const {
webpackConfig: styleConfig,
@@ -24,6 +25,7 @@ const NODE_ENV = process.env.NODE_ENV || 'development';
const WC_ADMIN_PHASE = process.env.WC_ADMIN_PHASE || 'development';
const isHot = Boolean( process.env.HOT );
const isProduction = NODE_ENV === 'production';
+const isWatch = ! isProduction && process.argv.includes( '--watch' );
const getSubdirectoriesAt = ( searchPath ) => {
const dir = path.resolve( __dirname, searchPath );
@@ -90,6 +92,18 @@ require( 'fs-extra' ).ensureSymlinkSync(
const webpackConfig = {
mode: NODE_ENV,
+ cache: ( isWatch || process.env.CI || process.env.HOT || process.env.STORYBOOK )
+ ? { type: 'memory' }
+ : {
+ type: 'filesystem',
+ cacheDirectory: path.resolve(
+ __dirname,
+ `node_modules/.cache/webpack-${ WC_ADMIN_PHASE }`
+ ),
+ buildDependencies: {
+ config: [ __filename ],
+ },
+ },
entry: getEntryPoints(),
output: {
filename: ( data ) => {
@@ -277,6 +291,8 @@ const webpackConfig = {
test: /\.js($|\?)/i,
mainEntry: 'app/index.min.js',
} ),
+ // Suppress file system cache warnings (unsupported serialization related).
+ new FilesystemCacheWarningsPlugin(),
].filter( Boolean ),
optimization: {
minimize: NODE_ENV !== 'development',
diff --git a/plugins/woocommerce/client/blocks/bin/filesystem-cache-warnings-webpack-plugin.js b/plugins/woocommerce/client/blocks/bin/filesystem-cache-warnings-webpack-plugin.js
new file mode 100644
index 00000000000..7158a75e58a
--- /dev/null
+++ b/plugins/woocommerce/client/blocks/bin/filesystem-cache-warnings-webpack-plugin.js
@@ -0,0 +1,30 @@
+// ExternalModule instances (wp.*, wc.* externals) are not serializable by webpack's PackFileCacheStrategy.
+// Suppress here since ignoreWarnings only affects compilation warnings, not infrastructure logs.
+
+function FilesystemCacheWarningsPlugin() {}
+
+FilesystemCacheWarningsPlugin.prototype.apply = function ( compiler ) {
+ compiler.hooks.infrastructureLog.tap(
+ 'SuppressExternalModuleCacheWarning',
+ ( name, type, args ) => {
+ if (
+ type === 'warn' &&
+ name === 'webpack.cache.PackFileCacheStrategy'
+ ) {
+ return (
+ args[ 0 ]?.includes?.(
+ 'No serializer registered for ModuleExternalInitFragment'
+ ) ||
+ args[ 0 ]?.includes?.(
+ 'No serializer registered for ExternalModule'
+ ) ||
+ args[ 0 ]?.includes?.(
+ 'No serializer registered for Warning'
+ )
+ );
+ }
+ }
+ );
+};
+
+module.exports = FilesystemCacheWarningsPlugin;
diff --git a/plugins/woocommerce/client/blocks/bin/webpack-config-dependency-detection.js b/plugins/woocommerce/client/blocks/bin/webpack-config-dependency-detection.js
index f9a9c23743f..f2a8d78acdd 100644
--- a/plugins/woocommerce/client/blocks/bin/webpack-config-dependency-detection.js
+++ b/plugins/woocommerce/client/blocks/bin/webpack-config-dependency-detection.js
@@ -11,6 +11,7 @@ const TerserPlugin = require( 'terser-webpack-plugin' );
/**
* Internal dependencies
*/
+const FilesystemCacheWarningsPlugin = require( './filesystem-cache-warnings-webpack-plugin.js' );
const { getProgressBarPluginConfig } = require( './webpack-helpers' );
const ROOT_DIR = path.resolve( __dirname, '../../../../../' );
@@ -65,6 +66,8 @@ module.exports = {
new ProgressBarPlugin(
getProgressBarPluginConfig( 'Dependency Detection' )
),
+ // Suppress file system cache warnings (unsupported serialization related).
+ new FilesystemCacheWarningsPlugin(),
],
optimization: {
// Always minimize - this is an inline script embedded in page HTML.
diff --git a/plugins/woocommerce/client/blocks/bin/webpack-config-interactive-blocks.js b/plugins/woocommerce/client/blocks/bin/webpack-config-interactive-blocks.js
index 527ea850b56..e9f023fed18 100644
--- a/plugins/woocommerce/client/blocks/bin/webpack-config-interactive-blocks.js
+++ b/plugins/woocommerce/client/blocks/bin/webpack-config-interactive-blocks.js
@@ -14,6 +14,7 @@ const RemoveFilesPlugin = require( './remove-files-webpack-plugin' );
/**
* Internal dependencies
*/
+const FilesystemCacheWarningsPlugin = require( './filesystem-cache-warnings-webpack-plugin.js' );
const { sharedOptimizationConfig } = require( './webpack-shared-config' );
const {
scriptModuleEntries,
@@ -95,6 +96,8 @@ module.exports = {
} ),
// Remove JS files generated by MiniCssExtractPlugin.
new RemoveFilesPlugin( './build/**/*-@(editor|style).js' ),
+ // Suppress file system cache warnings (unsupported serialization related).
+ new FilesystemCacheWarningsPlugin(),
],
module: {
rules: [
diff --git a/plugins/woocommerce/client/blocks/bin/webpack-config-interactivity.js b/plugins/woocommerce/client/blocks/bin/webpack-config-interactivity.js
index 963cf6d948f..ad244e3ac70 100644
--- a/plugins/woocommerce/client/blocks/bin/webpack-config-interactivity.js
+++ b/plugins/woocommerce/client/blocks/bin/webpack-config-interactivity.js
@@ -8,6 +8,7 @@ const { DefinePlugin } = require( 'webpack' );
/**
* Internal dependencies
*/
+const FilesystemCacheWarningsPlugin = require( './filesystem-cache-warnings-webpack-plugin.js' );
const { sharedOptimizationConfig } = require( './webpack-shared-config' );
const { NODE_ENV: mode = 'development' } = process.env;
@@ -55,6 +56,8 @@ module.exports = {
new DefinePlugin( {
'globalThis.SCRIPT_DEBUG': JSON.stringify( mode === 'development' ),
} ),
+ // Suppress file system cache warnings (unsupported serialization related).
+ new FilesystemCacheWarningsPlugin(),
],
module: {
rules: [
diff --git a/plugins/woocommerce/client/blocks/bin/webpack-configs.js b/plugins/woocommerce/client/blocks/bin/webpack-configs.js
index 27b7d64ad22..fe9feda129b 100644
--- a/plugins/woocommerce/client/blocks/bin/webpack-configs.js
+++ b/plugins/woocommerce/client/blocks/bin/webpack-configs.js
@@ -16,6 +16,7 @@ const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
/**
* Internal dependencies
*/
+const FilesystemCacheWarningsPlugin = require( './filesystem-cache-warnings-webpack-plugin.js' );
const { getEntryConfig, genericBlocks } = require( './webpack-entries' );
const {
ASSET_CHECK,
@@ -67,6 +68,8 @@ const getSharedPlugins = ( {
requestToExternal,
requestToHandle,
} ),
+ // Suppress file system cache warnings (unsupported serialization related).
+ new FilesystemCacheWarningsPlugin(),
].filter( Boolean );
/**
diff --git a/plugins/woocommerce/client/blocks/webpack.config.js b/plugins/woocommerce/client/blocks/webpack.config.js
index 2c71a476c00..4b6eca5e4d1 100644
--- a/plugins/woocommerce/client/blocks/webpack.config.js
+++ b/plugins/woocommerce/client/blocks/webpack.config.js
@@ -1,3 +1,8 @@
+/**
+ * External dependencies
+ */
+const path = require( 'path' );
+
/**
* Internal dependencies
*/
@@ -16,6 +21,29 @@ const {
const interactivityBlocksConfig = require( './bin/webpack-config-interactive-blocks.js' );
const interactivityAPIConfig = require( './bin/webpack-config-interactivity.js' );
const dependencyDetectionConfig = require( './bin/webpack-config-dependency-detection.js' );
+const isWatch =
+ NODE_ENV === 'development' && process.argv.includes( '--watch' );
+
+const getCacheConfig = ( name, configPaths = [] ) =>
+ isWatch || process.env.CI
+ ? { type: 'memory' }
+ : {
+ type: 'filesystem',
+ cacheDirectory: path.resolve(
+ __dirname,
+ `node_modules/.cache/webpack-${ name }`
+ ),
+ buildDependencies: {
+ config: [
+ __filename,
+ path.resolve( __dirname, 'bin/webpack-configs.js' ),
+ path.resolve( __dirname, 'bin/webpack-helpers.js' ),
+ ...configPaths.map( ( configPath ) =>
+ path.resolve( __dirname, configPath )
+ ),
+ ],
+ },
+ };
// Only options shared between all configs should be defined here.
const sharedConfig = {
@@ -40,26 +68,28 @@ const sharedConfig = {
const CartAndCheckoutFrontendConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'cart-and-checkout-frontend', [] ),
...getCartAndCheckoutFrontendConfig( { alias: getAlias() } ),
};
// Core config for shared libraries.
const CoreConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'core', [] ),
...getCoreConfig( { alias: getAlias() } ),
};
// Main Blocks config for registering Blocks and for the Editor.
const MainConfig = {
...sharedConfig,
- ...getMainConfig( {
- alias: getAlias(),
- } ),
+ cache: getCacheConfig( 'main', [] ),
+ ...getMainConfig( { alias: getAlias() } ),
};
// Frontend config for scripts used in the store itself.
const FrontendConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'frontend', [] ),
...getFrontConfig( { alias: getAlias() } ),
};
@@ -68,6 +98,7 @@ const FrontendConfig = {
*/
const ExtensionsConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'extensions', [] ),
...getExtensionsConfig( { alias: getAlias() } ),
};
@@ -76,6 +107,7 @@ const ExtensionsConfig = {
*/
const PaymentsConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'payments', [] ),
...getPaymentsConfig( { alias: getAlias() } ),
};
@@ -84,6 +116,7 @@ const PaymentsConfig = {
*/
const StylingConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'styling', [] ),
...getStylingConfig( { alias: getAlias() } ),
};
@@ -92,16 +125,23 @@ const StylingConfig = {
*/
const SiteEditorConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'site-editor', [] ),
...getSiteEditorConfig( { alias: getAlias() } ),
};
const InteractivityBlocksConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'interactivity-blocks', [
+ 'bin/webpack-config-interactive-blocks.js',
+ ] ),
...interactivityBlocksConfig,
};
const InteractivityAPIConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'interactivity-api', [
+ 'bin/webpack-config-interactivity.js',
+ ] ),
...interactivityAPIConfig,
};
@@ -111,6 +151,9 @@ const InteractivityAPIConfig = {
*/
const DependencyDetectionConfig = {
...sharedConfig,
+ cache: getCacheConfig( 'dependency-detection', [
+ 'bin/webpack-config-dependency-detection.js',
+ ] ),
...dependencyDetectionConfig,
};