Create a separate bundle for cart and checkout (#48010)
This commit is contained in:
parent
a862dab4f7
commit
eaf57e9f15
|
@ -0,0 +1,75 @@
|
|||
// The Dependency Extraction Webpack Plugin does not add split chunks as dependencies
|
||||
// to the .asset.php files that list dependencies. In the past we manually enqueued
|
||||
// those dependencies in PHP.
|
||||
// For every generated .asset.php file in the whole bundle, this plugin prefixes the
|
||||
// list of dependencies with the handles of split chunks that were generated in the build.
|
||||
|
||||
// e.g. if your bundle has a vendors script called foo-vendors then for every entry-point
|
||||
// that has a .asset.php file generated by Dependency Extraction Webpack Plugin
|
||||
// the plugin will edit that file to include foo-vendors as a listed dependency.
|
||||
|
||||
// This means for any split chunk you build you'll only need to register it in PHP, but all
|
||||
// files that depend on it will automatically include it as a dependency.
|
||||
class AddSplitChunkDependencies {
|
||||
apply( compiler ) {
|
||||
compiler.hooks.thisCompilation.tap(
|
||||
'AddStableChunksToAssets',
|
||||
( compilation, callback ) => {
|
||||
compilation.hooks.processAssets.tap(
|
||||
{
|
||||
name: 'AddStableChunksToAssets',
|
||||
stage: compiler.webpack.Compilation
|
||||
.PROCESS_ASSETS_STAGE_ANALYSE,
|
||||
},
|
||||
() => {
|
||||
const { chunks } = compilation;
|
||||
|
||||
const splitChunks = chunks.filter( ( chunk ) => {
|
||||
return chunk?.chunkReason?.includes( 'split' );
|
||||
} );
|
||||
|
||||
// find files that have an asset.php file
|
||||
const chunksToAddSplitsTo = chunks.filter(
|
||||
( chunk ) => {
|
||||
return (
|
||||
! chunk?.chunkReason?.includes( 'split' ) &&
|
||||
chunk.files.find( ( file ) =>
|
||||
file.endsWith( 'asset.php' )
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
for ( const chunk of chunksToAddSplitsTo ) {
|
||||
const assetFile = chunk.files.find( ( file ) =>
|
||||
file.endsWith( 'asset.php' )
|
||||
);
|
||||
|
||||
const assetFileContent = compilation.assets[
|
||||
assetFile
|
||||
]
|
||||
.source()
|
||||
.toString();
|
||||
|
||||
const extraDependencies = splitChunks
|
||||
.map( ( c ) => `'${ c.name }'` )
|
||||
.join( ', ' );
|
||||
|
||||
const updatedFileContent = assetFileContent.replace(
|
||||
/('dependencies'\s*=>\s*array\s*\(\s*)([^)]*)\)/,
|
||||
`$1${ extraDependencies }, $2)`
|
||||
);
|
||||
|
||||
compilation.assets[ assetFile ] = {
|
||||
source: () => updatedFileContent,
|
||||
size: () => updatedFileContent.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AddSplitChunkDependencies;
|
|
@ -28,6 +28,7 @@ const {
|
|||
getProgressBarPluginConfig,
|
||||
getCacheGroups,
|
||||
} = require( './webpack-helpers' );
|
||||
const AddSplitChunkDependencies = require( './add-split-chunk-dependencies' );
|
||||
|
||||
const isProduction = NODE_ENV === 'production';
|
||||
|
||||
|
@ -330,17 +331,6 @@ const getFrontConfig = ( options = {} ) => {
|
|||
// @see https://github.com/Automattic/jetpack/pull/20926
|
||||
chunkFilename: `[name]-frontend${ fileSuffix }.js?ver=[contenthash]`,
|
||||
filename: ( pathData ) => {
|
||||
// blocksCheckout and blocksComponents were moved from core bundle,
|
||||
// retain their filenames to avoid breaking translations.
|
||||
if (
|
||||
pathData.chunk.name === 'blocksCheckout' ||
|
||||
pathData.chunk.name === 'blocksComponents'
|
||||
) {
|
||||
return `${ paramCase(
|
||||
pathData.chunk.name
|
||||
) }${ fileSuffix }.js`;
|
||||
}
|
||||
|
||||
return `[name]-frontend${ fileSuffix }.js`;
|
||||
},
|
||||
uniqueName: 'webpackWcBlocksFrontendJsonp',
|
||||
|
@ -395,9 +385,10 @@ const getFrontConfig = ( options = {} ) => {
|
|||
minSize: 200000,
|
||||
automaticNameDelimiter: '--',
|
||||
cacheGroups: {
|
||||
commons: {
|
||||
vendor: {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
name: 'wc-blocks-vendors',
|
||||
// Note that filenames are suffixed with `frontend` so the generated file is `wc-blocks-frontend-vendors-frontend`.
|
||||
name: 'wc-blocks-frontend-vendors',
|
||||
chunks: ( chunk ) => {
|
||||
return (
|
||||
chunk.name !== 'product-button-interactivity'
|
||||
|
@ -431,6 +422,7 @@ const getFrontConfig = ( options = {} ) => {
|
|||
bundleAnalyzerReportTitle: 'Frontend',
|
||||
} ),
|
||||
new ProgressBarPlugin( getProgressBarPluginConfig( 'Frontend' ) ),
|
||||
new AddSplitChunkDependencies(),
|
||||
],
|
||||
resolve: {
|
||||
...resolve,
|
||||
|
@ -986,6 +978,151 @@ const getInteractivityAPIConfig = ( options = {} ) => {
|
|||
};
|
||||
};
|
||||
|
||||
const getCartAndCheckoutFrontendConfig = ( options = {} ) => {
|
||||
let { fileSuffix } = options;
|
||||
const { alias, resolvePlugins = [] } = options;
|
||||
fileSuffix = fileSuffix ? `-${ fileSuffix }` : '';
|
||||
|
||||
const resolve = alias
|
||||
? {
|
||||
alias,
|
||||
plugins: resolvePlugins,
|
||||
}
|
||||
: {
|
||||
plugins: resolvePlugins,
|
||||
};
|
||||
return {
|
||||
entry: getEntryConfig(
|
||||
'cartAndCheckoutFrontend',
|
||||
options.exclude || []
|
||||
),
|
||||
output: {
|
||||
devtoolNamespace: 'wc',
|
||||
path: path.resolve( __dirname, '../build/' ),
|
||||
// This is a cache busting mechanism which ensures that the script is loaded via the browser with a ?ver=hash
|
||||
// string. The hash is based on the built file contents.
|
||||
// @see https://github.com/webpack/webpack/issues/2329
|
||||
// Using the ?ver string is needed here so the filename does not change between builds. The WordPress
|
||||
// i18n system relies on the hash of the filename, so changing that frequently would result in broken
|
||||
// translations which we must avoid.
|
||||
// @see https://github.com/Automattic/jetpack/pull/20926
|
||||
chunkFilename: `[name]-frontend${ fileSuffix }.js?ver=[contenthash]`,
|
||||
filename: ( pathData ) => {
|
||||
// blocksCheckout and blocksComponents were moved from core bundle,
|
||||
// retain their filenames to avoid breaking translations.
|
||||
if (
|
||||
pathData.chunk.name === 'blocksCheckout' ||
|
||||
pathData.chunk.name === 'blocksComponents'
|
||||
) {
|
||||
return `${ paramCase(
|
||||
pathData.chunk.name
|
||||
) }${ fileSuffix }.js`;
|
||||
}
|
||||
|
||||
return `[name]-frontend${ fileSuffix }.js`;
|
||||
},
|
||||
uniqueName: 'webpackWcBlocksCartCheckoutFrontendJsonp',
|
||||
library: [ 'wc', '[name]' ],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(j|t)sx?$/,
|
||||
exclude: /node_modules/,
|
||||
use: {
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: [
|
||||
[
|
||||
'@wordpress/babel-preset-default',
|
||||
{
|
||||
modules: false,
|
||||
targets: {
|
||||
browsers: [
|
||||
'extends @wordpress/browserslist-config',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: [
|
||||
isProduction
|
||||
? require.resolve(
|
||||
'babel-plugin-transform-react-remove-prop-types'
|
||||
)
|
||||
: false,
|
||||
'@babel/plugin-proposal-optional-chaining',
|
||||
'@babel/plugin-proposal-class-properties',
|
||||
].filter( Boolean ),
|
||||
cacheDirectory: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.s[c|a]ss$/,
|
||||
use: {
|
||||
loader: 'ignore-loader',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
concatenateModules:
|
||||
isProduction && ! process.env.WP_BUNDLE_ANALYZER,
|
||||
splitChunks: {
|
||||
minSize: 200000,
|
||||
automaticNameDelimiter: '--',
|
||||
cacheGroups: {
|
||||
commons: {
|
||||
test: /[\\/]node_modules[\\/]/,
|
||||
name: 'wc-cart-checkout-vendors',
|
||||
chunks: 'all',
|
||||
enforce: true,
|
||||
},
|
||||
base: {
|
||||
// A refined include blocks and settings that are shared between cart and checkout that produces the smallest possible bundle.
|
||||
test: /assets[\\/]js[\\/](settings|previews|base|data|utils|blocks[\\/]cart-checkout-shared|icons)|packages[\\/](checkout|components)|atomic[\\/]utils/,
|
||||
name: 'wc-cart-checkout-base',
|
||||
chunks: 'all',
|
||||
enforce: true,
|
||||
},
|
||||
...getCacheGroups(),
|
||||
},
|
||||
},
|
||||
minimizer: [
|
||||
new TerserPlugin( {
|
||||
parallel: true,
|
||||
terserOptions: {
|
||||
output: {
|
||||
comments: /translators:/i,
|
||||
},
|
||||
compress: {
|
||||
passes: 2,
|
||||
},
|
||||
mangle: {
|
||||
reserved: [ '__', '_n', '_nx', '_x' ],
|
||||
},
|
||||
},
|
||||
extractComments: false,
|
||||
} ),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
...getSharedPlugins( {
|
||||
bundleAnalyzerReportTitle: 'Cart & Checkout Frontend',
|
||||
} ),
|
||||
new ProgressBarPlugin(
|
||||
getProgressBarPluginConfig( 'Cart & Checkout Frontend' )
|
||||
),
|
||||
new AddSplitChunkDependencies(),
|
||||
],
|
||||
resolve: {
|
||||
...resolve,
|
||||
extensions: [ '.js', '.ts', '.tsx' ],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getCoreConfig,
|
||||
getFrontConfig,
|
||||
|
@ -995,4 +1132,5 @@ module.exports = {
|
|||
getSiteEditorConfig,
|
||||
getStylingConfig,
|
||||
getInteractivityAPIConfig,
|
||||
getCartAndCheckoutFrontendConfig,
|
||||
};
|
||||
|
|
|
@ -25,9 +25,7 @@ const blocks = {
|
|||
},
|
||||
'attribute-filter': {},
|
||||
breadcrumbs: {},
|
||||
cart: {},
|
||||
'catalog-sorting': {},
|
||||
checkout: {},
|
||||
'coming-soon': {},
|
||||
'customer-account': {},
|
||||
'featured-category': {
|
||||
|
@ -43,10 +41,6 @@ const blocks = {
|
|||
customDir: 'classic-template',
|
||||
},
|
||||
'classic-shortcode': {},
|
||||
'mini-cart': {},
|
||||
'mini-cart-contents': {
|
||||
customDir: 'mini-cart/mini-cart-contents',
|
||||
},
|
||||
'store-notices': {},
|
||||
'page-content-wrapper': {},
|
||||
'price-filter': {},
|
||||
|
@ -169,12 +163,22 @@ const blocks = {
|
|||
},
|
||||
};
|
||||
|
||||
// Intentional separation of cart and checkout entry points to allow for better code splitting.
|
||||
const cartAndCheckoutBlocks = {
|
||||
cart: {},
|
||||
checkout: {},
|
||||
'mini-cart': {},
|
||||
'mini-cart-contents': {
|
||||
customDir: 'mini-cart/mini-cart-contents',
|
||||
},
|
||||
};
|
||||
|
||||
// Returns the entries for each block given a relative path (ie: `index.js`,
|
||||
// `**/*.scss`...).
|
||||
// It also filters out elements with undefined props and experimental blocks.
|
||||
const getBlockEntries = ( relativePath ) => {
|
||||
const getBlockEntries = ( relativePath, blockEntries = blocks ) => {
|
||||
return Object.fromEntries(
|
||||
Object.entries( blocks )
|
||||
Object.entries( blockEntries )
|
||||
.map( ( [ blockCode, config ] ) => {
|
||||
const filePaths = glob.sync(
|
||||
`./assets/js/blocks/${ config.customDir || blockCode }/` +
|
||||
|
@ -206,7 +210,10 @@ const entries = {
|
|||
'./assets/js/atomic/blocks/product-elements/product-details/index.tsx',
|
||||
'add-to-cart-form':
|
||||
'./assets/js/atomic/blocks/product-elements/add-to-cart-form/index.tsx',
|
||||
...getBlockEntries( '{index,block,frontend}.{t,j}s{,x}' ),
|
||||
...getBlockEntries( '{index,block,frontend}.{t,j}s{,x}', {
|
||||
...blocks,
|
||||
...cartAndCheckoutBlocks,
|
||||
} ),
|
||||
|
||||
// Interactivity component styling
|
||||
'wc-interactivity-checkbox-list':
|
||||
|
@ -239,17 +246,14 @@ const entries = {
|
|||
'wc-blocks': './assets/js/index.js',
|
||||
|
||||
// Blocks
|
||||
...getBlockEntries( 'index.{t,j}s{,x}' ),
|
||||
...getBlockEntries( 'index.{t,j}s{,x}', {
|
||||
...blocks,
|
||||
...cartAndCheckoutBlocks,
|
||||
} ),
|
||||
},
|
||||
frontend: {
|
||||
reviews: './assets/js/blocks/reviews/frontend.ts',
|
||||
...getBlockEntries( 'frontend.{t,j}s{,x}' ),
|
||||
|
||||
blocksCheckout: './packages/checkout/index.js',
|
||||
blocksComponents: './packages/components/index.ts',
|
||||
|
||||
'mini-cart-component':
|
||||
'./assets/js/blocks/mini-cart/component-frontend.tsx',
|
||||
'product-button-interactivity':
|
||||
'./assets/js/atomic/blocks/product-elements/button/frontend.tsx',
|
||||
},
|
||||
|
@ -273,6 +277,13 @@ const entries = {
|
|||
'wc-blocks-classic-template-revert-button':
|
||||
'./assets/js/templates/revert-button/index.tsx',
|
||||
},
|
||||
cartAndCheckoutFrontend: {
|
||||
...getBlockEntries( 'frontend.{t,j}s{,x}', cartAndCheckoutBlocks ),
|
||||
blocksCheckout: './packages/checkout/index.js',
|
||||
blocksComponents: './packages/components/index.ts',
|
||||
'mini-cart-component':
|
||||
'./assets/js/blocks/mini-cart/component-frontend.tsx',
|
||||
},
|
||||
};
|
||||
|
||||
const getEntryConfig = ( type = 'main', exclude = [] ) => {
|
||||
|
|
|
@ -11,6 +11,7 @@ const {
|
|||
getSiteEditorConfig,
|
||||
getStylingConfig,
|
||||
getInteractivityAPIConfig,
|
||||
getCartAndCheckoutFrontendConfig,
|
||||
} = require( './bin/webpack-configs.js' );
|
||||
|
||||
// Only options shared between all configs should be defined here.
|
||||
|
@ -34,6 +35,11 @@ const sharedConfig = {
|
|||
devtool: NODE_ENV === 'development' ? 'source-map' : false,
|
||||
};
|
||||
|
||||
const CartAndCheckoutFrontendConfig = {
|
||||
...sharedConfig,
|
||||
...getCartAndCheckoutFrontendConfig( { alias: getAlias() } ),
|
||||
};
|
||||
|
||||
// Core config for shared libraries.
|
||||
const CoreConfig = {
|
||||
...sharedConfig,
|
||||
|
@ -95,6 +101,7 @@ const SiteEditorConfig = {
|
|||
};
|
||||
|
||||
module.exports = [
|
||||
CartAndCheckoutFrontendConfig,
|
||||
CoreConfig,
|
||||
MainConfig,
|
||||
FrontendConfig,
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Significance: minor
|
||||
Type: dev
|
||||
|
||||
Create a separate JS cart and checkout JavaScript bundle to improve performance.
|
|
@ -40,6 +40,7 @@ final class AssetsController {
|
|||
add_action( 'admin_enqueue_scripts', array( $this, 'update_block_style_dependencies' ), 20 );
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'update_block_settings_dependencies' ), 100 );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'update_block_settings_dependencies' ), 100 );
|
||||
add_filter( 'js_do_concat', array( $this, 'skip_boost_minification_for_cart_checkout' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,9 +63,14 @@ final class AssetsController {
|
|||
// The price package is shared externally so has no blocks prefix.
|
||||
$this->api->register_script( 'wc-price-format', 'assets/client/blocks/price-format.js', array(), false );
|
||||
|
||||
$this->api->register_script( 'wc-blocks-vendors-frontend', $this->api->get_block_asset_build_path( 'wc-blocks-vendors-frontend' ), array(), false );
|
||||
$this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js', array( 'wc-blocks-vendors-frontend' ) );
|
||||
$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js', array( 'wc-blocks-vendors-frontend' ) );
|
||||
// Vendor scripts for blocks frontends (not including cart and checkout).
|
||||
$this->api->register_script( 'wc-blocks-frontend-vendors', $this->api->get_block_asset_build_path( 'wc-blocks-frontend-vendors-frontend' ), array(), false );
|
||||
|
||||
// Cart and checkout frontend scripts.
|
||||
$this->api->register_script( 'wc-cart-checkout-vendors', $this->api->get_block_asset_build_path( 'wc-cart-checkout-vendors-frontend' ), array(), false );
|
||||
$this->api->register_script( 'wc-cart-checkout-base', $this->api->get_block_asset_build_path( 'wc-cart-checkout-base-frontend' ), array(), false );
|
||||
$this->api->register_script( 'wc-blocks-checkout', 'assets/client/blocks/blocks-checkout.js' );
|
||||
$this->api->register_script( 'wc-blocks-components', 'assets/client/blocks/blocks-components.js' );
|
||||
|
||||
// Register the interactivity components here for now.
|
||||
$this->api->register_script( 'wc-interactivity-dropdown', 'assets/client/blocks/wc-interactivity-dropdown.js', array() );
|
||||
|
@ -253,6 +259,23 @@ final class AssetsController {
|
|||
return $src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip Jetpack Boost minification on older versions of Jetpack Boost where it causes issues.
|
||||
*
|
||||
* @param mixed $do_concat Whether to concatenate the script or not.
|
||||
* @param mixed $handle The script handle.
|
||||
* @return mixed
|
||||
*/
|
||||
public function skip_boost_minification_for_cart_checkout( $do_concat, $handle ) {
|
||||
$boost_is_outdated = defined( 'JETPACK_BOOST_VERSION' ) && version_compare( JETPACK_BOOST_VERSION, '3.4.2', '<' );
|
||||
$scripts_to_ignore = [
|
||||
'wc-cart-checkout-vendors',
|
||||
'wc-cart-checkout-base',
|
||||
];
|
||||
|
||||
return $boost_is_outdated && in_array( $handle, $scripts_to_ignore, true ) ? false : $do_concat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add body classes to the frontend and within admin.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue