diff --git a/config/plugins.js b/config/plugins.js
index 8cd63bb7..349fb033 100644
--- a/config/plugins.js
+++ b/config/plugins.js
@@ -10,10 +10,12 @@ const DependencyExtractionWebpackPlugin = require('@wordpress/dependency-extract
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const WebpackImageSizesPlugin = require('./webpack-image-sizes-plugin')
+const SpriteHashPlugin = require('./sprite-hash-plugin')
module.exports = {
get: function (mode) {
const plugins = [
+ new SpriteHashPlugin(),
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: ['**/*', '!images', '!images/**'],
}),
diff --git a/config/sprite-hash-plugin.js b/config/sprite-hash-plugin.js
new file mode 100644
index 00000000..eb2224c7
--- /dev/null
+++ b/config/sprite-hash-plugin.js
@@ -0,0 +1,73 @@
+const fs = require('fs');
+const path = require('path');
+const crypto = require('crypto');
+
+/**
+ * Webpack plugin to generate content hashes for SVG sprite files.
+ * Creates a sprite-hashes.json file in the dist folder.
+ */
+class SpriteHashPlugin {
+ constructor(options = {}) {
+ this.options = {
+ outputPath: options.outputPath || 'dist',
+ spritePath: options.spritePath || 'dist/icons',
+ outputFilename: options.outputFilename || 'sprite-hashes.json',
+ hashLength: options.hashLength || 8,
+ };
+ }
+
+ apply(compiler) {
+ compiler.hooks.afterEmit.tapAsync(
+ 'SpriteHashPlugin',
+ (compilation, callback) => {
+ const spriteDir = path.resolve(
+ compiler.options.context,
+ this.options.spritePath
+ );
+ const outputFile = path.resolve(
+ compiler.options.context,
+ this.options.outputPath,
+ this.options.outputFilename
+ );
+
+ if (!fs.existsSync(spriteDir)) {
+ console.warn(
+ `SpriteHashPlugin: Sprite directory not found: ${spriteDir}`
+ );
+ callback();
+ return;
+ }
+
+ const hashes = {};
+ const files = fs
+ .readdirSync(spriteDir)
+ .filter((file) => file.endsWith('.svg'));
+
+ files.forEach((file) => {
+ const filePath = path.join(spriteDir, file);
+ const content = fs.readFileSync(filePath);
+ const hash = crypto
+ .createHash('md5')
+ .update(content)
+ .digest('hex')
+ .substring(0, this.options.hashLength);
+
+ // Store with relative path as key
+ const relativePath = `icons/${file}`;
+ hashes[relativePath] = hash;
+ });
+
+ fs.writeFileSync(outputFile, JSON.stringify(hashes, null, 2));
+ console.log(
+ `SpriteHashPlugin: Generated ${
+ this.options.outputFilename
+ } with ${Object.keys(hashes).length} sprites`
+ );
+
+ callback();
+ }
+ );
+ }
+}
+
+module.exports = SpriteHashPlugin;
diff --git a/inc/Services/Svg.php b/inc/Services/Svg.php
index b25df908..14ad9c7c 100644
--- a/inc/Services/Svg.php
+++ b/inc/Services/Svg.php
@@ -55,12 +55,13 @@ public function get_the_icon( string $icon_class, array $additionnal_classes = [
$icon_class = substr( $icon_class, $slash_pos + 1 );
}
- $icon_slug = strpos( $icon_class, 'icon-' ) === 0 ? $icon_class : sprintf( 'icon-%s', $icon_class );
- $classes = [ 'icon', $icon_slug ];
- $classes = array_merge( $classes, $additionnal_classes );
- $classes = array_map( 'sanitize_html_class', $classes );
+ $icon_slug = strpos( $icon_class, 'icon-' ) === 0 ? $icon_class : sprintf( 'icon-%s', $icon_class );
+ $classes = [ 'icon', $icon_slug ];
+ $classes = array_merge( $classes, $additionnal_classes );
+ $classes = array_map( 'sanitize_html_class', $classes );
+ $hash_sprite = $this->get_sprite_hash( $sprite_name );
- return sprintf( '', implode( ' ', $classes ), \get_theme_file_uri( sprintf( '/dist/icons/%s.svg', $sprite_name ) ), $icon_slug ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ return sprintf( '', implode( ' ', $classes ), \get_theme_file_uri( sprintf( '/dist/icons/%s.svg', $sprite_name ) ), $hash_sprite, $icon_slug ); //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
@@ -104,4 +105,32 @@ public function allow_svg_tag( $tags ) {
return $tags;
}
+
+ /**
+ * Get the hash of the sprite
+ *
+ * @param string $sprite_name
+ *
+ * @return string
+ */
+ public function get_sprite_hash( $sprite_name ) {
+ $sprite_hash_file = \get_theme_file_path( '/dist/sprite-hashes.json' );
+
+ if ( ! is_readable( $sprite_hash_file ) ) {
+ return '';
+ }
+
+ $sprite_hash = file_get_contents( $sprite_hash_file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
+ $sprite_hash = json_decode( $sprite_hash, true );
+
+ if ( empty( $sprite_hash ) ) {
+ return '';
+ }
+
+ if ( ! isset( $sprite_hash[ sprintf( 'icons/%s.svg', $sprite_name ) ] ) ) {
+ return '';
+ }
+
+ return '?' . $sprite_hash[ sprintf( 'icons/%s.svg', $sprite_name ) ];
+ }
}