From e8be043d3ad65fff587b358dd3441e400816ef76 Mon Sep 17 00:00:00 2001 From: Seghir Nadir Date: Mon, 23 May 2022 15:58:13 +0100 Subject: [PATCH] Refactor webpack splitting to fix missing translation (#6420) * don't splitChunk frontend scripts * simplfy output folders * reduce minimum chunk size to 10kb * add all inner blocks for loading * better splitChunks * update translation code * wait for scripts * try sleeping * fix loading translation * fix tests for mini cart * simplfy po logic * automate loading chunks * rename webpack group file * rename block.json path --- bin/webpack-configs.js | 52 +++++++++++++++---- bin/wp-env-config.sh | 14 ++--- bin/wp-env-pre-config.sh | 3 +- src/Assets/Api.php | 2 +- src/BlockTypes/AbstractBlock.php | 13 +++++ src/BlockTypes/Cart.php | 18 +++---- src/BlockTypes/Checkout.php | 23 +++----- src/BlockTypes/MiniCart.php | 45 ++++++---------- .../cart-checkout/translations.test.js | 9 ++-- tests/e2e/specs/shopper/mini-cart.test.js | 8 +-- 10 files changed, 106 insertions(+), 81 deletions(-) diff --git a/bin/webpack-configs.js b/bin/webpack-configs.js index df28508444d..8eb2b634add 100644 --- a/bin/webpack-configs.js +++ b/bin/webpack-configs.js @@ -122,9 +122,7 @@ woocommerce_blocks_env = ${ NODE_ENV } // Only concatenate modules in production, when not analyzing bundles. concatenateModules: isProduction && ! process.env.WP_BUNDLE_ANALYZER, - splitChunks: { - automaticNameDelimiter: '--', - }, + splitChunks: false, minimizer: [ new TerserPlugin( { cache: true, @@ -222,7 +220,7 @@ const getMainConfig = ( options = {} ) => { concatenateModules: isProduction && ! process.env.WP_BUNDLE_ANALYZER, splitChunks: { - minSize: 0, + minSize: 10000, // makes the smallest chunk file 10kbs, this doesn't affect lazy loaded chunks. automaticNameDelimiter: '--', cacheGroups: { commons: { @@ -261,31 +259,31 @@ const getMainConfig = ( options = {} ) => { patterns: [ { from: './assets/js/blocks/checkout/block.json', - to: './checkout/block.json', + to: './checkout.block.json', }, { from: './assets/js/blocks/featured-items/featured-category/block.json', - to: './featured-category/block.json', + to: './featured-category.block.json', }, { from: './assets/js/blocks/featured-items/featured-product/block.json', - to: './featured-product/block.json', + to: './featured-product.block.json', }, { from: './assets/js/blocks/handpicked-products/block.json', - to: './handpicked-products/block.json', + to: './handpicked-products.block.json', }, { from: './assets/js/blocks/product-tag/block.json', - to: './product-tag/block.json', + to: './product-tag.block.json', }, { from: './assets/js/blocks/products-by-attribute/block.json', - to: './products-by-attribute/block.json', + to: './products-by-attribute.block.json', }, ], } ), @@ -378,7 +376,39 @@ const getFrontConfig = ( options = {} ) => { concatenateModules: isProduction && ! process.env.WP_BUNDLE_ANALYZER, splitChunks: { - automaticNameDelimiter: '--', + cacheGroups: { + default: false, + vendors: { + test: /[\\/]node_modules[\\/]/, + name( module ) { + const moduleFileName = module + .identifier() + .split( '/' ) + .reduceRight( ( item ) => item ); + return `vendor/${ moduleFileName }`; + }, + automaticNameDelimiter: '--', + minSize: 20000, + priority: -20, + }, + 'wp-components': { + test: ( module ) => { + if ( + module?.resource?.match( + /wordpress-components/ + ) + ) { + return true; + } + }, + name: 'wp/wp-components.js', + automaticNameDelimiter: '--', + reuseExistingChunk: true, + enforce: true, + minSize: 20000, + priority: -10, + }, + }, }, minimizer: [ new TerserPlugin( { diff --git a/bin/wp-env-config.sh b/bin/wp-env-config.sh index 7b698c7ff06..2d0631cf725 100755 --- a/bin/wp-env-config.sh +++ b/bin/wp-env-config.sh @@ -19,13 +19,15 @@ wp core version --extra wp plugin list wp theme activate storefront wp wc customer update 1 --user=1 --billing='{"first_name":"John","last_name":"Doe","company":"Automattic","country":"US","address_1":"addr 1","address_2":"addr 2","city":"San Francisco","state":"CA","postcode":"94107","phone":"123456789"}' --shipping='{"first_name":"John","last_name":"Doe","company":"Automattic","country":"US","address_1":"addr 1","address_2":"addr 2","city":"San Francisco","state":"CA","postcode":"94107","phone":"123456789"}' - ## Prepare translation for the test suite wp language core install nl_NL wp language plugin install woocommerce nl_NL -wp language plugin install woo-gutenberg-products-block nl_NL -# Because we don't install the WooCommerce Blocks plugin, WP CLI uses the core version to install the language pack. -# To get the latest translations, we need to run an additional update command. -wp language plugin update woo-gutenberg-products-block nl_NL - +## We download a full version of .po (that has translation for js files as well). +curl https://translate.wordpress.org/projects/wp-plugins/woo-gutenberg-products-block/stable/nl/default/export-translations/ --output ./wp-content/languages/plugins/woo-gutenberg-products-block-nl_NL.po +sleep 5 +## We update our po file with updated file paths. +## This should account for when a file changes location between versions. +php -d memory_limit=2024M "$(which wp)" i18n make-pot ./wp-content/plugins/$1/ ./wp-content/languages/plugins/woo-gutenberg-products-block-nl_NL.po --merge --domain=woo-gutenberg-products-block --exclude=node_modules,vendor --skip-audit +## We regenerate json translation from the new po file we created. +php -d memory_limit=2024M "$(which wp)" i18n make-json ./wp-content/languages/plugins/woo-gutenberg-products-block-nl_NL.po exit $EXIT_CODE diff --git a/bin/wp-env-pre-config.sh b/bin/wp-env-pre-config.sh index b790b364c69..6a6ad11bd20 100755 --- a/bin/wp-env-pre-config.sh +++ b/bin/wp-env-pre-config.sh @@ -1,3 +1,4 @@ #!/bin/sh BASENAME=$(basename "`pwd`") -npm run wp-env run tests-cli './wp-content/plugins/'$BASENAME'/bin/wp-env-config.sh' +# We need to pass the blocks plugin folder name to the script, the name can change depending on your local env and we can't hardcode it. +npm run wp-env run tests-cli './wp-content/plugins/'$BASENAME'/bin/wp-env-config.sh' $BASENAME diff --git a/src/Assets/Api.php b/src/Assets/Api.php index 13e6958562e..c3b1bf8e746 100644 --- a/src/Assets/Api.php +++ b/src/Assets/Api.php @@ -68,7 +68,7 @@ protected function get_asset_url( $relative_path = '' ) { * @return string|boolean False if metadata file is not found for the block. */ public function get_block_metadata_path( $block_name ) { - $path_to_metadata_from_plugin_root = $this->package->get_path( 'build/' . $block_name . '/block.json' ); + $path_to_metadata_from_plugin_root = $this->package->get_path( 'build/' . $block_name . '.block.json' ); if ( ! file_exists( $path_to_metadata_from_plugin_root ) ) { return false; } diff --git a/src/BlockTypes/AbstractBlock.php b/src/BlockTypes/AbstractBlock.php index a61dcd2d8a5..01a2d576ad0 100644 --- a/src/BlockTypes/AbstractBlock.php +++ b/src/BlockTypes/AbstractBlock.php @@ -171,8 +171,21 @@ protected function register_chunk_translations( $chunks ) { } } + /** + * Generate an array of chunks paths for loading translation. + */ + protected function get_chunks_paths() { + foreach ( glob( \Automattic\WooCommerce\Blocks\Package::get_path() . "build/{$this->chunks_folder}/*-frontend.js" ) as $block_name ) { + $blocks[] = "{$this->chunks_folder}/" . basename( $block_name ); + } + + $chunks = preg_filter( '/.js/', '', $blocks ); + return $chunks; + } /** * Registers the block type with WordPress. + * + * @return string[] Chunks paths. */ protected function register_block_type() { $block_settings = [ diff --git a/src/BlockTypes/Cart.php b/src/BlockTypes/Cart.php index 395699250e0..ca67b059572 100644 --- a/src/BlockTypes/Cart.php +++ b/src/BlockTypes/Cart.php @@ -18,6 +18,13 @@ class Cart extends AbstractBlock { */ protected $block_name = 'cart'; + /** + * Chunks build folder. + * + * @var string + */ + protected $chunks_folder = 'cart-blocks'; + /** * Get the editor script handle for this block type. * @@ -227,7 +234,6 @@ protected function deep_sort_with_accents( $array ) { protected function hydrate_from_api() { $this->asset_data_registry->hydrate_api_request( '/wc/store/v1/cart' ); } - /** * Register script and style assets for the block type before it is registered. * @@ -235,15 +241,7 @@ protected function hydrate_from_api() { */ protected function register_block_type_assets() { parent::register_block_type_assets(); - $blocks = [ - 'cart-blocks/express-payment--checkout-blocks/express-payment--checkout-blocks/payment', - 'cart-blocks/line-items', - 'cart-blocks/order-summary', - 'cart-blocks/order-summary--checkout-blocks/billing-address--checkout-blocks/shipping-address', - 'cart-blocks/checkout-button', - 'cart-blocks/express-payment', - ]; - $chunks = preg_filter( '/$/', '-frontend', $blocks ); + $chunks = $this->get_chunks_paths(); $this->register_chunk_translations( $chunks ); } } diff --git a/src/BlockTypes/Checkout.php b/src/BlockTypes/Checkout.php index 6eb828b0791..7bd6192ab92 100644 --- a/src/BlockTypes/Checkout.php +++ b/src/BlockTypes/Checkout.php @@ -14,6 +14,13 @@ class Checkout extends AbstractBlock { */ protected $block_name = 'checkout'; + /** + * Chunks build folder. + * + * @var string + */ + protected $chunks_folder = 'checkout-blocks'; + /** * Get the editor script handle for this block type. * @@ -392,7 +399,6 @@ public static function include_token_id_with_payment_methods( $list_item, $token } return $list_item; } - /** * Register script and style assets for the block type before it is registered. * @@ -400,20 +406,7 @@ public static function include_token_id_with_payment_methods( $list_item, $token */ protected function register_block_type_assets() { parent::register_block_type_assets(); - $blocks = [ - 'checkout-blocks/express-payment', - 'checkout-blocks/contact-information', - 'checkout-blocks/shipping-address', - 'checkout-blocks/billing-address--checkout-blocks/shipping-address', - 'checkout-blocks/billing-address', - 'checkout-blocks/shipping-methods', - 'checkout-blocks/payment', - 'checkout-blocks/order-note', - 'checkout-blocks/actions', - 'checkout-blocks/terms', - 'checkout-blocks/order-summary', - ]; - $chunks = preg_filter( '/$/', '-frontend', $blocks ); + $chunks = $this->get_chunks_paths(); $this->register_chunk_translations( $chunks ); } } diff --git a/src/BlockTypes/MiniCart.php b/src/BlockTypes/MiniCart.php index 2364c4b2c6a..b6fd517f214 100644 --- a/src/BlockTypes/MiniCart.php +++ b/src/BlockTypes/MiniCart.php @@ -23,6 +23,13 @@ class MiniCart extends AbstractBlock { */ protected $block_name = 'mini-cart'; + /** + * Chunks build folder. + * + * @var string + */ + protected $chunks_folder = 'mini-cart-contents-block'; + /** * Array of scripts that will be lazy loaded when interacting with the block. * @@ -187,9 +194,8 @@ protected function enqueue_data( array $attributes = [] ) { } $this->scripts_to_lazy_load['wc-block-mini-cart-component-frontend'] = array( - 'src' => $script_data['src'], - 'version' => $script_data['version'], - 'translations' => $this->get_inner_blocks_translations(), + 'src' => $script_data['src'], + 'version' => $script_data['version'], ); // Re-add the filter. @@ -476,33 +482,14 @@ protected function get_cart_payload() { } /** - * Prepare translations for inner blocks and dependencies. + * Register script and style assets for the block type before it is registered. + * + * This registers the scripts; it does not enqueue them. */ - protected function get_inner_blocks_translations() { - $wp_scripts = wp_scripts(); - $translations = array(); - - $blocks = [ - 'mini-cart-contents-block/filled-cart', - 'mini-cart-contents-block/empty-cart', - 'mini-cart-contents-block/title', - 'mini-cart-contents-block/items', - 'mini-cart-contents-block/products-table', - 'mini-cart-contents-block/footer', - 'mini-cart-contents-block/shopping-button', - ]; - $chunks = preg_filter( '/$/', '-frontend', $blocks ); - - foreach ( $chunks as $chunk ) { - $handle = 'wc-blocks-' . $chunk . '-chunk'; - $this->asset_api->register_script( $handle, $this->asset_api->get_block_asset_build_path( $chunk ), [], true ); - $translations[] = $wp_scripts->print_translations( $handle, false ); - wp_deregister_script( $handle ); - } - - $translations = array_filter( $translations ); - - return implode( '', $translations ); + protected function register_block_type_assets() { + parent::register_block_type_assets(); + $chunks = $this->get_chunks_paths(); + $this->register_chunk_translations( $chunks ); } /** diff --git a/tests/e2e/specs/shopper/cart-checkout/translations.test.js b/tests/e2e/specs/shopper/cart-checkout/translations.test.js index a3ccbe1ed26..89bda1def70 100644 --- a/tests/e2e/specs/shopper/cart-checkout/translations.test.js +++ b/tests/e2e/specs/shopper/cart-checkout/translations.test.js @@ -35,12 +35,13 @@ describe( 'Shopper → Cart & Checkout → Translations', () => { const removeLink = await page.waitForSelector( '.wc-block-cart-item__remove-link' ); - await expect( removeLink ).toMatch( 'Artikel verwijderen' ); + await expect( removeLink ).toMatch( 'Verwijder item' ); const submitButton = await page.waitForSelector( '.wc-block-cart__submit-button' ); - await expect( submitButton ).toMatch( 'Doorgaan naar afrekenen' ); + + await expect( submitButton ).toMatch( 'Ga naar afrekenen' ); const orderSummary = await page.$( '.wp-block-woocommerce-cart-order-summary-block' @@ -51,7 +52,7 @@ describe( 'Shopper → Cart & Checkout → Translations', () => { await expect( orderSummary ).toMatch( 'Totaal' ); } ); - it( 'USer can view translated Checkout block', async () => { + it( 'User can view translated Checkout block', async () => { await shopper.block.goToCheckout(); const contactHeading = await page.$( @@ -90,7 +91,7 @@ describe( 'Shopper → Cart & Checkout → Translations', () => { await expect( orderSummary ).toMatch( 'Besteloverzicht' ); await expect( orderSummary ).toMatch( 'Subtotaal' ); await expect( orderSummary ).toMatch( 'Waardebon code' ); - await expect( orderSummary ).toMatch( 'Verzendmethoden' ); + await expect( orderSummary ).toMatch( 'Verzending' ); await expect( orderSummary ).toMatch( 'Totaal' ); } ); } ); diff --git a/tests/e2e/specs/shopper/mini-cart.test.js b/tests/e2e/specs/shopper/mini-cart.test.js index b9e8b5a7459..bd16a826de4 100644 --- a/tests/e2e/specs/shopper/mini-cart.test.js +++ b/tests/e2e/specs/shopper/mini-cart.test.js @@ -594,9 +594,9 @@ describe( 'Shopper → Mini Cart', () => { ); await expect( page ).toMatchElement( - '.wc-block-mini-cart__title', + '.wc-block-mini-cart__footer-cart', { - text: 'Je winkelwagen (1 stuk)', + text: 'Bekijk mijn winkelwagen', } ); } ); @@ -626,9 +626,9 @@ describe( 'Shopper → Mini Cart', () => { ); await expect( page ).toMatchElement( - '.wc-block-mini-cart__title', + '.wc-block-mini-cart__footer-cart', { - text: 'Je winkelwagen (1 stuk)', + text: 'Bekijk mijn winkelwagen', } ); } );