diff --git a/packages/cli/src/lib/hashing-utils.js b/packages/cli/src/lib/hashing-utils.js index c050d1c67..9e24ee00b 100644 --- a/packages/cli/src/lib/hashing-utils.js +++ b/packages/cli/src/lib/hashing-utils.js @@ -1,12 +1,15 @@ -// https://gist.github.com/hyamamoto/fd435505d29ebfa3d9716fd2be8d42f0#gistcomment-2775538 -function hashString(inputString) { - let h = 0; +import { createHash } from "node:crypto"; - for (let i = 0; i < inputString.length; i += 1) { - h = (Math.imul(31, h) + inputString.charCodeAt(i)) | 0; - } - - return Math.abs(h).toString(); +/** + * Generates a hash of the input string. + * @param {string} inputString - The string to hash. + * @param {number} [bytes=6] - Number of bytes to be generated by the hash (default: 6). + * Note: This specifies the number of bytes, not the length of the hash. + * The hash length in characters depends on the bytes and is approximately `(bytes * 8 / 6)` when encoded in base64url. + * @returns {string} A base64url-encoded hash of the input string. + */ +function hashString(inputString, bytes = 6) { + return createHash("shake256", { outputLength: bytes }).update(inputString).digest("base64url"); } export { hashString }; diff --git a/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js b/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js index d29e28248..63d99cb17 100644 --- a/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js +++ b/packages/cli/test/cases/build.config.optimization-default/build.config-optimization-default.spec.js @@ -31,7 +31,11 @@ import fs from "node:fs"; import glob from "glob-promise"; import { JSDOM } from "jsdom"; import path from "node:path"; -import { getDependencyFiles, getOutputTeardownFiles } from "../../../../../test/utils.js"; +import { + getDependencyFiles, + getOutputTeardownFiles, + HASH_REGEX, +} from "../../../../../test/utils.js"; import { Runner } from "gallinago"; import { fileURLToPath } from "node:url"; @@ -132,7 +136,19 @@ describe("Build Greenwood With: ", function () { const customCss = await fs.promises.readFile(cssFiles[0], "utf-8"); expect(cssFiles.length).to.be.equal(1); - expect(customCss).to.be.equal(expectedCss); + const regex = HASH_REGEX.replace("{8}", "{8,10}"); + const normalizeCss = (css) => + css + .replace( + new RegExp(`webcomponents\\.${regex}\\.jpg`, "g"), + "webcomponents.[HASH].jpg", + ) + .replace(new RegExp(`bar\\.${regex}\\.baz`, "g"), "bar.[HASH].baz"); + + const normalizedCustomCss = normalizeCss(customCss); + const normalizedExpectedCss = normalizeCss(expectedCss); + + expect(normalizedCustomCss).to.be.equal(normalizedExpectedCss); }); }); @@ -172,14 +188,14 @@ describe("Build Greenwood With: ", function () { it("should have the expected @font-face file bundle path in the referenced