Improve webpack cache-busting version parameter by using file contents hash (#44838)

* Update webpack config to use file content hash for chunks and generate asset php for styles

* Use StyleAssetPlugin to generate style.asset.php

* Remove unneed ?ver=<version> code

* Use file hash from asset file when SCRIPT_DEBUG is off

- Use file hash to load scripts/styles ?ver=<file hash>
- Add register_style() method to WC_Admin_Assets

* Load payment method promotions in admin_enqueue_scripts

* Add changefile(s) from automation for the following project(s): @woocommerce/product-editor, woocommerce

* Add json2php

* Update doc

* Update pnpm-lock.yaml

* Fix add_print_shipping_label_script

* Add a comment to style-asset-plugin.js

* Change register_style to use WC_ADMIN_DIST_CSS_FOLDER

* Reset the outputNormal object to avoid duplicate files

* Fix type error

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Chi-Hsuan Huang 2024-02-27 16:07:53 +08:00 committed by GitHub
parent 707c555091
commit ec8bd31365
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 361 additions and 164 deletions

View File

@ -6,6 +6,7 @@ const path = require( 'path' );
const WebpackRTLPlugin = require( 'webpack-rtl-plugin' ); const WebpackRTLPlugin = require( 'webpack-rtl-plugin' );
const RemoveEmptyScriptsPlugin = require( 'webpack-remove-empty-scripts' ); const RemoveEmptyScriptsPlugin = require( 'webpack-remove-empty-scripts' );
const postcssPlugins = require( '@wordpress/postcss-plugins-preset' ); const postcssPlugins = require( '@wordpress/postcss-plugins-preset' );
const StyleAssetPlugin = require( './style-asset-plugin' );
const NODE_ENV = process.env.NODE_ENV || 'development'; const NODE_ENV = process.env.NODE_ENV || 'development';
@ -69,12 +70,14 @@ module.exports = {
new RemoveEmptyScriptsPlugin(), new RemoveEmptyScriptsPlugin(),
new MiniCssExtractPlugin( { new MiniCssExtractPlugin( {
filename: '[name]/style.css', filename: '[name]/style.css',
chunkFilename: 'chunks/[id].style.css', chunkFilename: 'chunks/[id].style.css?ver=[contenthash]',
} ), } ),
new WebpackRTLPlugin( { new WebpackRTLPlugin( {
filename: '[name]/style-rtl.css', filename: '[name]/style-rtl.css',
minify: NODE_ENV === 'development' ? false : { safe: true }, minify: NODE_ENV === 'development' ? false : { safe: true },
} ), } ),
new StyleAssetPlugin(),
], ],
}, },
StyleAssetPlugin,
}; };

View File

@ -41,6 +41,7 @@
"@wordpress/base-styles": "wp-6.0", "@wordpress/base-styles": "wp-6.0",
"@wordpress/postcss-plugins-preset": "wp-6.0", "@wordpress/postcss-plugins-preset": "wp-6.0",
"css-loader": "^3.6.0", "css-loader": "^3.6.0",
"json2php": "^0.0.7",
"mini-css-extract-plugin": "^2.7.6", "mini-css-extract-plugin": "^2.7.6",
"postcss-loader": "^4.3.0", "postcss-loader": "^4.3.0",
"sass-loader": "^10.5.0", "sass-loader": "^10.5.0",

View File

@ -0,0 +1,219 @@
/**
* Add an asset file for each entry point that contains the current version calculated for the current source code.
*
* This is modified from WP dependency-extraction-webpack-plugin plugin:
* https://github.com/WordPress/gutenberg/tree/a04a8e94e8b93ba60441c6534e21f4c3c26ff1bc/packages/dependency-extraction-webpack-plugin
*
* We can contribute this back to the original plugin in the future and remove this file.
*/
/**
* External dependencies
*/
const path = require( 'path' );
const webpack = require( 'webpack' );
const json2php = require( 'json2php' );
const { createHash } = webpack.util;
const { RawSource } = webpack.sources;
const { AsyncDependenciesBlock } = webpack;
class AssetDataPlugin {
constructor( options ) {
this.options = Object.assign(
{
combineAssets: false,
combinedOutputFile: null,
outputFormat: 'php',
outputFilename: null,
},
options
);
}
/**
* @param {any} asset Asset Data
* @return {string} Stringified asset data suitable for output
*/
stringify( asset ) {
if ( this.options.outputFormat === 'php' ) {
return `<?php return ${ json2php(
JSON.parse( JSON.stringify( asset ) )
) };\n`;
}
return JSON.stringify( asset );
}
apply( compiler ) {
compiler.hooks.thisCompilation.tap(
this.constructor.name,
( compilation ) => {
compilation.hooks.processAssets.tap(
{
name: this.constructor.name,
stage: compiler.webpack.Compilation
.PROCESS_ASSETS_STAGE_ANALYSE,
},
() => this.addAssets( compilation )
);
}
);
}
/** @param {webpack.Compilation} compilation */
addAssets( compilation ) {
const {
combineAssets,
combinedOutputFile,
outputFormat,
outputFilename,
} = this.options;
const combinedAssetsData = {};
// Accumulate all entrypoint chunks, some of them shared
const entrypointChunks = new Set();
for ( const entrypoint of compilation.entrypoints.values() ) {
for ( const chunk of entrypoint.chunks ) {
entrypointChunks.add( chunk );
}
}
// Process each entrypoint chunk independently
for ( const chunk of entrypointChunks ) {
const chunkFiles = Array.from( chunk.files );
const styleExtensionRegExp = /\.s?css$/i;
const chunkStyleFile = chunkFiles.find( ( f ) =>
styleExtensionRegExp.test( f )
);
if ( ! chunkStyleFile ) {
// No style file, skip
continue;
}
// Go through the assets and hash the sources. We can't just use
// `chunk.contentHash` because that's not updated when
// assets are minified. In practice the hash is updated by
// `RealContentHashPlugin` after minification, but it only modifies
// already-produced asset filenames and the updated hash is not
// available to plugins.
const { hashFunction, hashDigest, hashDigestLength } =
compilation.outputOptions;
const contentHash = chunkFiles
.sort()
.reduce( ( hash, filename ) => {
const asset = compilation.getAsset( filename );
return hash.update( asset.source.buffer() );
}, createHash( hashFunction ) )
.digest( hashDigest )
.slice( 0, hashDigestLength );
const assetData = {
version: contentHash,
};
if ( combineAssets ) {
combinedAssetsData[ chunkStyleFile ] = assetData;
continue;
}
let assetFilename;
if ( outputFilename ) {
assetFilename = compilation.getPath( outputFilename, {
chunk,
filename: chunkStyleFile,
contentHash,
} );
} else {
const suffix =
'.asset.' + ( outputFormat === 'php' ? 'php' : 'json' );
assetFilename = compilation
.getPath( '[file]', { filename: chunkStyleFile } )
.replace( styleExtensionRegExp, suffix );
}
// Add source and file into compilation for webpack to output.
compilation.assets[ assetFilename ] = new RawSource(
this.stringify( assetData )
);
chunk.files.add( assetFilename );
}
if ( combineAssets ) {
const outputFolder = compilation.outputOptions.path;
const assetsFilePath = path.resolve(
outputFolder,
combinedOutputFile ||
'assets.' + ( outputFormat === 'php' ? 'php' : 'json' )
);
const assetsFilename = path.relative(
outputFolder,
assetsFilePath
);
// Add source into compilation for webpack to output.
compilation.assets[ assetsFilename ] = new RawSource(
this.stringify( combinedAssetsData )
);
}
}
/**
* Can we trace a line of static dependencies from an entry to a module
*
* @param {webpack.Compilation} compilation
* @param {webpack.DependenciesBlock} block
*
* @return {boolean} True if there is a static import path to the root
*/
static hasStaticDependencyPathToRoot( compilation, block ) {
const incomingConnections = [
...compilation.moduleGraph.getIncomingConnections( block ),
].filter(
( connection ) =>
// Library connections don't have a dependency, this is a root
connection.dependency &&
// Entry dependencies are another root
connection.dependency.constructor.name !== 'EntryDependency'
);
// If we don't have non-entry, non-library incoming connections,
// we've reached a root of
if ( ! incomingConnections.length ) {
return true;
}
const staticDependentModules = incomingConnections.flatMap(
( connection ) => {
const { dependency } = connection;
const parentBlock =
compilation.moduleGraph.getParentBlock( dependency );
return parentBlock.constructor.name !==
AsyncDependenciesBlock.name
? [ compilation.moduleGraph.getParentModule( dependency ) ]
: [];
}
);
// All the dependencies were Async, the module was reached via a dynamic import
if ( ! staticDependentModules.length ) {
return false;
}
// Continue to explore any static dependencies
return staticDependentModules.some( ( parentStaticDependentModule ) =>
AssetDataPlugin.hasStaticDependencyPathToRoot(
compilation,
parentStaticDependentModule
)
);
}
}
module.exports = AssetDataPlugin;

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Improve webpack cache-busting version parameter by using file contents hash

View File

@ -10,7 +10,10 @@ const WebpackRTLPlugin = require( 'webpack-rtl-plugin' );
/** /**
* Internal dependencies * Internal dependencies
*/ */
const { webpackConfig } = require( '@woocommerce/internal-style-build' ); const {
webpackConfig,
StyleAssetPlugin,
} = require( '@woocommerce/internal-style-build' );
const { const {
blockEntryPoints, blockEntryPoints,
getBlockMetaData, getBlockMetaData,
@ -69,5 +72,6 @@ module.exports = {
}, },
], ],
} ), } ),
new StyleAssetPlugin(),
], ],
}; };

View File

@ -21,29 +21,6 @@ import { WcAdminConflictErrorSlot } from './settings/conflict-error-slotfill.js'
import './xstate.js'; import './xstate.js';
import { deriveWpAdminBackgroundColours } from './utils/derive-wp-admin-background-colours'; import { deriveWpAdminBackgroundColours } from './utils/derive-wp-admin-background-colours';
// Modify webpack pubilcPath at runtime based on location of WordPress Plugin.
// eslint-disable-next-line no-undef,camelcase
__webpack_public_path__ = global.wcAdminAssets.path;
// Modify webpack to append the ?ver parameter to JS chunk
// https://webpack.js.org/api/module-variables/#__webpack_get_script_filename__-webpack-specific
// eslint-disable-next-line no-undef,camelcase
const oldGetScriptFileNameFn = __webpack_get_script_filename__;
// eslint-disable-next-line no-undef,camelcase
__webpack_get_script_filename__ = ( chunk ) => {
const filename = oldGetScriptFileNameFn( chunk );
return `${ filename }?ver=${ window.wcAdminAssets.version }`;
};
// Modify webpack to append the ?ver parameter to CSS chunk hrefs generated by mini-css-extract-plugin
// eslint-disable-next-line no-undef,camelcase
const oldMinCssFn = __webpack_require__.miniCssF;
// eslint-disable-next-line no-undef,camelcase
__webpack_require__.miniCssF = ( chunkId ) => {
const filename = oldMinCssFn( chunkId );
return `${ filename }?ver=${ window.wcAdminAssets.version }`;
};
const appRoot = document.getElementById( 'root' ); const appRoot = document.getElementById( 'root' );
const embeddedRoot = document.getElementById( 'woocommerce-embedded-root' ); const embeddedRoot = document.getElementById( 'woocommerce-embedded-root' );
const settingsGroup = 'wc_admin'; const settingsGroup = 'wc_admin';

View File

@ -8,7 +8,7 @@
* 2. Remove check for development mode - we always want unminified files. * 2. Remove check for development mode - we always want unminified files.
* 3. Remove BannerPlugin support - we don't use it. * 3. Remove BannerPlugin support - we don't use it.
* 4. Remove the 'min' suffix from the chunk loaded in the new `mainEntry` option. * 4. Remove the 'min' suffix from the chunk loaded in the new `mainEntry` option.
* 5. Hook into compilation later so we're running after Source Map generation. (https://webpack.js.org/api/compilation-hooks/: PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE) * 5. Hook into compilation later so we're running after Source Map generation. (https://webpack.js.org/api/compilation-hooks/: PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE)
*/ */
const path = require( 'path' ); const path = require( 'path' );
const ModuleFilenameHelpers = require( 'webpack/lib/ModuleFilenameHelpers' ); const ModuleFilenameHelpers = require( 'webpack/lib/ModuleFilenameHelpers' );
@ -39,7 +39,7 @@ class UnminifyWebpackPlugin {
apply( compiler ) { apply( compiler ) {
const options = this.options; const options = this.options;
const outputNormal = {}; let outputNormal = {};
compiler.hooks.compilation.tap( compiler.hooks.compilation.tap(
'UnminifyWebpackPlugin', 'UnminifyWebpackPlugin',
@ -107,6 +107,8 @@ class UnminifyWebpackPlugin {
value.filename, value.filename,
new webpack.sources.RawSource( value.content ) new webpack.sources.RawSource( value.content )
); );
// Reset the outputNormal object to avoid writing to file that only differs in casing or query string from already written file.
outputNormal = {};
} }
} }
); );

View File

@ -101,7 +101,7 @@ const webpackConfig = {
? `wp-admin-scripts/[name]${ outputSuffix }.js` ? `wp-admin-scripts/[name]${ outputSuffix }.js`
: `[name]/index${ outputSuffix }.js`; : `[name]/index${ outputSuffix }.js`;
}, },
chunkFilename: `chunks/[name]${ outputSuffix }.js`, chunkFilename: `chunks/[name]${ outputSuffix }.js?ver=[contenthash]`,
path: path.join( __dirname, '/build' ), path: path.join( __dirname, '/build' ),
library: { library: {
// Expose the exports of entry points so we can consume the libraries in window.wc.[modulename] with WooCommerceDependencyExtractionWebpackPlugin. // Expose the exports of entry points so we can consume the libraries in window.wc.[modulename] with WooCommerceDependencyExtractionWebpackPlugin.
@ -200,7 +200,8 @@ const webpackConfig = {
// The package build process doesn't handle extracting CSS from JS files, so we copy them separately. // The package build process doesn't handle extracting CSS from JS files, so we copy them separately.
new CopyWebpackPlugin( { new CopyWebpackPlugin( {
patterns: wcAdminPackages.map( ( packageName ) => ( { patterns: wcAdminPackages.map( ( packageName ) => ( {
from: `../../packages/js/${ packageName }/build-style/*.css`, // Copy css and style.asset.php files.
from: `../../packages/js/${ packageName }/build-style/*.{css,php}`,
to: `./${ packageName }/[name][ext]`, to: `./${ packageName }/[name][ext]`,
noErrorOnMissing: true, noErrorOnMissing: true,
// Overwrites files already in compilation.assets to ensure we use the assets from the build-style. // Overwrites files already in compilation.assets to ensure we use the assets from the build-style.

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Improve webpack cache-busting version parameter by using file contents hash

View File

@ -586,7 +586,7 @@ if ( ! class_exists( 'WC_Admin_Assets', false ) ) :
'wc-admin-' . $script_name, 'wc-admin-' . $script_name,
WCAdminAssets::get_url( $script_path_name . '/' . $script_name, 'js' ), WCAdminAssets::get_url( $script_path_name . '/' . $script_name, 'js' ),
$script_assets['dependencies'], $script_assets['dependencies'],
WCAdminAssets::get_file_version( 'js' ), WCAdminAssets::get_file_version( 'js', $script_assets['version'] ),
true true
); );
} }

View File

@ -81,7 +81,7 @@ class Features {
* @return array * @return array
*/ */
public static function get_optional_feature_options() { public static function get_optional_feature_options() {
$features = []; $features = array();
foreach ( array_keys( self::$optional_features ) as $optional_feature_key ) { foreach ( array_keys( self::$optional_features ) as $optional_feature_key ) {
$feature_class = self::get_feature_class( $optional_feature_key ); $feature_class = self::get_feature_class( $optional_feature_key );
@ -148,7 +148,7 @@ class Features {
public static function get_available_features() { public static function get_available_features() {
$features = self::get_features(); $features = self::get_features();
$optional_feature_keys = array_keys( self::$optional_features ); $optional_feature_keys = array_keys( self::$optional_features );
$optional_features_unavailable = []; $optional_features_unavailable = array();
/** /**
* Filter allowing WooCommerce Admin optional features to be disabled. * Filter allowing WooCommerce Admin optional features to be disabled.
@ -292,22 +292,8 @@ class Features {
return; return;
} }
$rtl = is_rtl() ? '.rtl' : ''; WCAdminAssets::register_style( 'beta-features-tracking-modal', 'style', array( 'wp-components' ) );
WCAdminAssets::register_script( 'wp-admin-scripts', 'beta-features-tracking-modal', array( 'wp-i18n', 'wp-element', WC_ADMIN_APP ) );
wp_enqueue_style(
'wc-admin-beta-features-tracking-modal',
WCAdminAssets::get_url( "beta-features-tracking-modal/style{$rtl}", 'css' ),
array( 'wp-components' ),
WCAdminAssets::get_file_version( 'css' )
);
wp_enqueue_script(
'wc-admin-beta-features-tracking-modal',
WCAdminAssets::get_url( 'wp-admin-scripts/beta-features-tracking-modal', 'js' ),
array( 'wp-i18n', 'wp-element', WC_ADMIN_APP ),
WCAdminAssets::get_file_version( 'js' ),
true
);
} }
/** /**

View File

@ -123,14 +123,7 @@ class Init {
return; return;
} }
$rtl = is_rtl() ? '.rtl' : ''; WCAdminAssets::register_style( 'navigation-opt-out', 'style', array( 'wp-components' ) );
wp_enqueue_style(
'wc-admin-navigation-opt-out',
WCAdminAssets::get_url( "navigation-opt-out/style{$rtl}", 'css' ),
array( 'wp-components' ),
WCAdminAssets::get_file_version( 'css' )
);
WCAdminAssets::register_script( 'wp-admin-scripts', 'navigation-opt-out', true ); WCAdminAssets::register_script( 'wp-admin-scripts', 'navigation-opt-out', true );
wp_localize_script( wp_localize_script(
'wc-admin-navigation-opt-out', 'wc-admin-navigation-opt-out',

View File

@ -73,7 +73,7 @@ class Coupons {
__( 'Coupons', 'woocommerce' ), __( 'Coupons', 'woocommerce' ),
'manage_options', 'manage_options',
'coupons-moved', 'coupons-moved',
[ $this, 'coupon_menu_moved' ] array( $this, 'coupon_menu_moved' )
); );
} }
@ -117,15 +117,7 @@ class Coupons {
return; return;
} }
$rtl = is_rtl() ? '-rtl' : ''; WCAdminAssets::register_style( 'marketing-coupons', 'style' );
wp_enqueue_style(
'wc-admin-marketing-coupons',
WCAdminAssets::get_url( "marketing-coupons/style{$rtl}", 'css' ),
array(),
WCAdminAssets::get_file_version( 'css' )
);
WCAdminAssets::register_script( 'wp-admin-scripts', 'marketing-coupons', true ); WCAdminAssets::register_script( 'wp-admin-scripts', 'marketing-coupons', true );
} }
} }

View File

@ -128,14 +128,7 @@ class ShippingLabelBanner {
* @param string $hook current page hook. * @param string $hook current page hook.
*/ */
public function add_print_shipping_label_script( $hook ) { public function add_print_shipping_label_script( $hook ) {
$rtl = is_rtl() ? '.rtl' : ''; WCAdminAssets::register_style( 'print-shipping-label-banner', 'style', array( 'wp-components' ) );
wp_enqueue_style(
'print-shipping-label-banner-style',
WCAdminAssets::get_url( "print-shipping-label-banner/style{$rtl}", 'css' ),
array( 'wp-components' ),
WCAdminAssets::get_file_version( 'css' )
);
WCAdminAssets::register_script( 'wp-admin-scripts', 'print-shipping-label-banner', true ); WCAdminAssets::register_script( 'wp-admin-scripts', 'print-shipping-label-banner', true );
$payload = array( $payload = array(

View File

@ -98,15 +98,22 @@ class WCAdminAssets {
} }
/** /**
* Gets the file modified time as a cache buster if we're in dev mode, or the plugin version otherwise. * Gets the file modified time as a cache buster if we're in dev mode,
* or the asset version (file content hash) if exists, or the WooCommerce version.
* *
* @param string $ext File extension. * @param string $ext File extension.
* @param string|null $asset_version Optional. The version from the asset file.
* @return string The cache buster value to use for the given file. * @return string The cache buster value to use for the given file.
*/ */
public static function get_file_version( $ext ) { public static function get_file_version( $ext, $asset_version = null ) {
if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
return filemtime( WC_ADMIN_ABSPATH . self::get_path( $ext ) ); return filemtime( WC_ADMIN_ABSPATH . self::get_path( $ext ) );
} }
if ( ! empty( $asset_version ) ) {
return $asset_version;
}
return WC_VERSION; return WC_VERSION;
} }
@ -253,9 +260,7 @@ class WCAdminAssets {
return; return;
} }
$js_file_version = self::get_file_version( 'js' ); // Register the JS scripts.
$css_file_version = self::get_file_version( 'css' );
$scripts = array( $scripts = array(
'wc-admin-layout', 'wc-admin-layout',
'wc-explat', 'wc-explat',
@ -299,6 +304,7 @@ class WCAdminAssets {
try { try {
$script_assets_filename = self::get_script_asset_filename( $script_path_name, 'index' ); $script_assets_filename = self::get_script_asset_filename( $script_path_name, 'index' );
$script_assets = require WC_ADMIN_ABSPATH . WC_ADMIN_DIST_JS_FOLDER . $script_path_name . '/' . $script_assets_filename; $script_assets = require WC_ADMIN_ABSPATH . WC_ADMIN_DIST_JS_FOLDER . $script_path_name . '/' . $script_assets_filename;
$script_version = self::get_file_version( 'js', $script_assets['version'] );
global $wp_version; global $wp_version;
if ( 'app' === $script_path_name && version_compare( $wp_version, '6.3', '<' ) ) { if ( 'app' === $script_path_name && version_compare( $wp_version, '6.3', '<' ) ) {
@ -320,92 +326,83 @@ class WCAdminAssets {
wp_register_script( wp_register_script(
$script, $script,
self::get_url( $script_path_name . '/index', 'js' ), self::get_url( $script_path_name . '/index', 'js' ),
$script_assets ['dependencies'], $script_assets['dependencies'],
$js_file_version, $script_version,
true true
); );
if ( in_array( $script, $translated_scripts, true ) ) { if ( in_array( $script, $translated_scripts, true ) ) {
wp_set_script_translations( $script, 'woocommerce' ); wp_set_script_translations( $script, 'woocommerce' );
} }
if ( WC_ADMIN_APP === $script ) {
wp_localize_script(
WC_ADMIN_APP,
'wcAdminAssets',
array(
'path' => plugins_url( self::get_path( 'js' ), WC_ADMIN_PLUGIN_FILE ),
'version' => $script_version,
)
);
}
} catch ( \Exception $e ) { } catch ( \Exception $e ) {
// Avoid crashing WordPress if an asset file could not be loaded. // Avoid crashing WordPress if an asset file could not be loaded.
wc_caught_exception( $e, __CLASS__ . '::' . __FUNCTION__, $script_path_name ); wc_caught_exception( $e, __CLASS__ . '::' . __FUNCTION__, $script_path_name );
} }
} }
wp_register_style( // Register the CSS styles.
'wc-admin-layout', $styles = array(
self::get_url( 'admin-layout/style', 'css' ),
array(),
$css_file_version
);
wp_style_add_data( 'wc-admin-layout', 'rtl', 'replace' );
wp_register_style(
'wc-components',
self::get_url( 'components/style', 'css' ),
array(),
$css_file_version
);
wp_style_add_data( 'wc-components', 'rtl', 'replace' );
wp_register_style(
'wc-block-templates',
self::get_url( 'block-templates/style', 'css' ),
array(),
$css_file_version
);
wp_style_add_data( 'wc-block-templates', 'rtl', 'replace' );
wp_register_style(
'wc-product-editor',
self::get_url( 'product-editor/style', 'css' ),
array(),
$css_file_version
);
wp_style_add_data( 'wc-product-editor', 'rtl', 'replace' );
wp_register_style(
'wc-customer-effort-score',
self::get_url( 'customer-effort-score/style', 'css' ),
array(),
$css_file_version
);
wp_style_add_data( 'wc-customer-effort-score', 'rtl', 'replace' );
wp_register_style(
'wc-experimental',
self::get_url( 'experimental/style', 'css' ),
array(),
$css_file_version
);
wp_style_add_data( 'wc-experimental', 'rtl', 'replace' );
wp_localize_script(
WC_ADMIN_APP,
'wcAdminAssets',
array( array(
'path' => plugins_url( self::get_path( 'js' ), WC_ADMIN_PLUGIN_FILE ), 'handle' => 'wc-admin-layout',
'version' => $js_file_version, ),
) array(
'handle' => 'wc-components',
),
array(
'handle' => 'wc-block-templates',
),
array(
'handle' => 'wc-product-editor',
),
array(
'handle' => 'wc-customer-effort-score',
),
array(
'handle' => 'wc-experimental',
),
array(
'handle' => WC_ADMIN_APP,
'dependencies' => array( 'wc-components', 'wc-admin-layout', 'wc-customer-effort-score', 'wc-product-editor', 'wp-components', 'wc-experimental' ),
),
array(
'handle' => 'wc-onboarding',
),
); );
wp_register_style( $css_file_version = self::get_file_version( 'css' );
WC_ADMIN_APP, foreach ( $styles as $style ) {
self::get_url( 'app/style', 'css' ), $handle = $style['handle'];
array( 'wc-components', 'wc-admin-layout', 'wc-customer-effort-score', 'wc-product-editor', 'wp-components', 'wc-experimental' ), $style_path_name = isset( $scripts_map[ $handle ] ) ? $scripts_map[ $handle ] : str_replace( 'wc-', '', $handle );
$css_file_version
);
wp_style_add_data( WC_ADMIN_APP, 'rtl', 'replace' );
wp_register_style( try {
'wc-onboarding', $style_assets_filename = self::get_script_asset_filename( $style_path_name, 'style' );
self::get_url( 'onboarding/style', 'css' ), $style_assets = require WC_ADMIN_ABSPATH . WC_ADMIN_DIST_JS_FOLDER . $style_path_name . '/' . $style_assets_filename;
array(), $version = $style_assets['version'];
$css_file_version } catch ( \Throwable $e ) {
); // Use the default version if the asset file could not be loaded.
wp_style_add_data( 'wc-onboarding', 'rtl', 'replace' ); $version = $css_file_version;
}
$dependencies = isset( $style['dependencies'] ) ? $style['dependencies'] : array();
wp_register_style(
$handle,
self::get_url( $style_path_name . '/style', 'css' ),
$dependencies,
self::get_file_version( 'css', $version ),
);
wp_style_add_data( $handle, 'rtl', 'replace' );
}
} }
/** /**
@ -475,11 +472,32 @@ class WCAdminAssets {
'wc-admin-' . $script_name, 'wc-admin-' . $script_name,
self::get_url( $script_path_name . '/' . $script_name, 'js' ), self::get_url( $script_path_name . '/' . $script_name, 'js' ),
array_merge( array( WC_ADMIN_APP ), $script_assets ['dependencies'], $dependencies ), array_merge( array( WC_ADMIN_APP ), $script_assets ['dependencies'], $dependencies ),
self::get_file_version( 'js' ), self::get_file_version( 'js', $script_assets['version'] ),
true true
); );
if ( $need_translation ) { if ( $need_translation ) {
wp_set_script_translations( 'wc-admin-' . $script_name, 'woocommerce' ); wp_set_script_translations( 'wc-admin-' . $script_name, 'woocommerce' );
} }
} }
/**
* Loads a style
*
* @param string $style_path_name The style path name.
* @param string $style_name Filename of the style to load.
* @param array $dependencies Array of any extra dependencies.
*/
public static function register_style( $style_path_name, $style_name, $dependencies = array() ) {
$style_assets_filename = self::get_script_asset_filename( $style_path_name, $style_name );
$style_assets = require WC_ADMIN_ABSPATH . WC_ADMIN_DIST_CSS_FOLDER . $style_path_name . '/' . $style_assets_filename;
$handle = 'wc-admin-' . $style_name;
wp_enqueue_style(
$handle,
self::get_url( $style_path_name . '/' . $style_name, 'css' ),
$dependencies,
self::get_file_version( 'css', $style_assets['version'] ),
);
wp_style_add_data( $handle, 'rtl', 'replace' );
}
} }

View File

@ -31,17 +31,7 @@ class Init extends RemoteSpecsEngine {
add_filter( 'woocommerce_payment_gateways', array( __CLASS__, 'possibly_register_pre_install_wc_pay_promotion_gateway' ) ); add_filter( 'woocommerce_payment_gateways', array( __CLASS__, 'possibly_register_pre_install_wc_pay_promotion_gateway' ) );
add_filter( 'option_woocommerce_gateway_order', array( __CLASS__, 'set_gateway_top_of_list' ) ); add_filter( 'option_woocommerce_gateway_order', array( __CLASS__, 'set_gateway_top_of_list' ) );
add_filter( 'default_option_woocommerce_gateway_order', array( __CLASS__, 'set_gateway_top_of_list' ) ); add_filter( 'default_option_woocommerce_gateway_order', array( __CLASS__, 'set_gateway_top_of_list' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'load_payment_method_promotions' ) );
$rtl = is_rtl() ? '.rtl' : '';
wp_enqueue_style(
'wc-admin-payment-method-promotions',
WCAdminAssets::get_url( "payment-method-promotions/style{$rtl}", 'css' ),
array( 'wp-components' ),
WCAdminAssets::get_file_version( 'css' )
);
WCAdminAssets::register_script( 'wp-admin-scripts', 'payment-method-promotions', true );
} }
/** /**
@ -161,5 +151,13 @@ class Init extends RemoteSpecsEngine {
} }
return WCPayPromotionDataSourcePoller::get_instance()->get_specs_from_data_sources(); return WCPayPromotionDataSourcePoller::get_instance()->get_specs_from_data_sources();
} }
/**
* Loads the payment method promotions scripts and styles.
*/
public static function load_payment_method_promotions() {
WCAdminAssets::register_style( 'payment-method-promotions', 'style', array( 'wp-components' ) );
WCAdminAssets::register_script( 'wp-admin-scripts', 'payment-method-promotions', true );
}
} }

View File

@ -2163,6 +2163,9 @@ importers:
css-loader: css-loader:
specifier: ^3.6.0 specifier: ^3.6.0
version: 3.6.0(webpack@5.89.0) version: 3.6.0(webpack@5.89.0)
json2php:
specifier: ^0.0.7
version: 0.0.7
mini-css-extract-plugin: mini-css-extract-plugin:
specifier: ^2.7.6 specifier: ^2.7.6
version: 2.7.6(webpack@5.89.0) version: 2.7.6(webpack@5.89.0)
@ -39280,7 +39283,6 @@ packages:
/json2php@0.0.7: /json2php@0.0.7:
resolution: {integrity: sha512-dnSoUiLAoVaMXxFsVi4CrPVYMKOuDBXTghXSmMINX44RZ8WM9cXlY7UqrQnlAcODCVO7FV3+8t/5nDKAjimLfg==} resolution: {integrity: sha512-dnSoUiLAoVaMXxFsVi4CrPVYMKOuDBXTghXSmMINX44RZ8WM9cXlY7UqrQnlAcODCVO7FV3+8t/5nDKAjimLfg==}
dev: true
/json5@1.0.2: /json5@1.0.2:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}

View File

@ -116,7 +116,7 @@ export const scanForChanges = async (
base: string, base: string,
outputStyle: 'cli' | 'github', outputStyle: 'cli' | 'github',
clonedPath?: string, clonedPath?: string,
exclude?: string[] exclude: string[] = []
) => { ) => {
Logger.startTask( `Making temporary clone of ${ source }...` ); Logger.startTask( `Making temporary clone of ${ source }...` );