From 85d9ef73860109467d15c179e122cf73f03163c1 Mon Sep 17 00:00:00 2001 From: Hampus Date: Mon, 12 Oct 2026 13:58:28 +0200 Subject: [PATCH] fix(app): hash emitted license asset names (#3416) --- fluxer_app/index.html | 1 - .../scripts/build/rspack/static-files.mjs | 30 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/fluxer_app/index.html b/fluxer_app/index.html index 9b678b48e..929e5c684 100644 --- a/fluxer_app/index.html +++ b/fluxer_app/index.html @@ -21,7 +21,6 @@ - diff --git a/fluxer_app/scripts/build/rspack/static-files.mjs b/fluxer_app/scripts/build/rspack/static-files.mjs index e2351e4b0..099b43bb2 100644 --- a/fluxer_app/scripts/build/rspack/static-files.mjs +++ b/fluxer_app/scripts/build/rspack/static-files.mjs @@ -1,8 +1,9 @@ // SPDX-License-Identifier: AGPL-3.0-or-later +import {createHash} from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; -import {sources} from '@rspack/core'; +import {HtmlRspackPlugin, sources} from '@rspack/core'; const STATIC_CDN_ENDPOINT_PLACEHOLDER = '{{STATIC_CDN_ENDPOINT}}'; @@ -104,22 +105,41 @@ export class StaticFilesPlugin { } emitLicenseFiles(compilation, dir, files, subject) { + const assetNames = new Map(); if (!dir) { - return; + return assetNames; } for (const {source, asset} of files) { const sourcePath = path.join(dir, source); + compilation.fileDependencies.add(sourcePath); if (!fs.existsSync(sourcePath)) { throw new Error( `StaticFilesPlugin: ${sourcePath} is missing. The bundled ${subject} may not be redistributed without it.`, ); } - compilation.emitAsset(asset, new sources.RawSource(fs.readFileSync(sourcePath))); + const content = fs.readFileSync(sourcePath); + const extension = path.extname(asset); + const hash = createHash('sha256').update(content).digest('hex').slice(0, 16); + const name = `${asset.slice(0, -extension.length)}.${hash}${extension}`; + compilation.emitAsset(name, new sources.RawSource(content)); + assetNames.set(source, name); } + return assetNames; } apply(compiler) { compiler.hooks.thisCompilation.tap('StaticFilesPlugin', (compilation) => { + let fontNoticeAsset; + HtmlRspackPlugin.getCompilationHooks(compilation).alterAssetTagGroups.tap('StaticFilesPlugin', (data) => { + if (fontNoticeAsset) { + data.headTags.push({ + tagName: 'link', + voidTag: true, + attributes: {rel: 'license', href: `${data.publicPath}${fontNoticeAsset}`}, + }); + } + return data; + }); compilation.hooks.processAssets.tap( { name: 'StaticFilesPlugin', @@ -132,7 +152,9 @@ export class StaticFilesPlugin { new sources.RawSource(generateBrowserConfig(this.staticCdnEndpoint)), ); compilation.emitAsset('robots.txt', new sources.RawSource(generateRobotsTxt())); - this.emitLicenseFiles(compilation, this.fontsDir, FONT_LICENSE_FILES, 'fonts'); + fontNoticeAsset = this.emitLicenseFiles(compilation, this.fontsDir, FONT_LICENSE_FILES, 'fonts').get( + 'NOTICE.md', + ); this.emitLicenseFiles(compilation, this.wasmCratesDir, WASM_LICENSE_FILES, 'WebAssembly modules'); }, );