UNPKG

webpack-subresource-integrity

Version:
203 lines 6.59 kB
/** * Copyright (c) 2015-present, Waysact Pty Ltd * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import { createHash } from "crypto"; import { sep } from "path"; export const sriHashVariableReference = "__webpack_require__.sriHashes"; export function assert(value, message) { if (!value) { throw new Error(message); } } export function getTagSrc(tag) { if (!["script", "link"].includes(tag.tagName) || !tag.attributes) { return undefined; } if (typeof tag.attributes["href"] === "string") { return tag.attributes["href"]; } if (typeof tag.attributes["src"] === "string") { return tag.attributes["src"]; } return undefined; } export const normalizePath = (p) => p.replace(/\?.*$/, "").split(sep).join("/"); export const placeholderPrefix = "*-*-*-CHUNK-SRI-HASH-"; export const placeholderRegex = new RegExp(`${placeholderPrefix.replace(/[-*/\\]/g, "\\$&")}[a-zA-Z0-9=/+]+(\\ssha\\d{3}-[a-zA-Z0-9=/+]+)*`, "g"); export const computeIntegrity = (hashFuncNames, source) => { const result = hashFuncNames .map((hashFuncName) => hashFuncName + "-" + createHash(hashFuncName) .update(typeof source === "string" ? Buffer.from(source, "utf-8") : source) .digest("base64")) .join(" "); return result; }; const placeholderCache = {}; export const makePlaceholder = (hashFuncNames, id) => { const cacheKey = hashFuncNames.join() + id; const cachedPlaceholder = placeholderCache[cacheKey]; if (cachedPlaceholder) return cachedPlaceholder; const placeholderSource = `${placeholderPrefix}${id}`; const filler = computeIntegrity(hashFuncNames, placeholderSource); const placeholder = placeholderPrefix + filler.substring(placeholderPrefix.length); placeholderCache[cacheKey] = placeholder; return placeholder; }; export function addIfNotExist(set, item) { if (set.has(item)) return true; set.add(item); return false; } export function findChunks(chunk) { const allChunks = new Set(); const groupsVisited = new Set(); (function recurseChunk(childChunk) { function recurseGroup(group) { if (addIfNotExist(groupsVisited, group.id)) return; group.chunks.forEach(recurseChunk); group.childrenIterable.forEach(recurseGroup); } if (addIfNotExist(allChunks, childChunk)) return; Array.from(childChunk.groupsIterable).forEach(recurseGroup); })(chunk); return allChunks; } export function notNil(value) { return value !== null && value !== undefined; } export function generateSriHashPlaceholders(chunks, hashFuncNames) { return Array.from(chunks).reduce((sriHashes, depChunk) => { if (depChunk.id) { sriHashes[depChunk.id] = makePlaceholder(hashFuncNames, depChunk.id); } return sriHashes; }, {}); } function allSetsHave(sets, item) { for (const set of sets) { if (!set.has(item)) { return false; } } return true; } export function* intersect(sets) { const { value: initialSet } = sets[Symbol.iterator]().next(); if (!initialSet) { return; } initialSetLoop: for (const item of initialSet) { if (!allSetsHave(sets, item)) { continue initialSetLoop; } yield item; } } export function intersectSets(setsToIntersect) { return new Set(intersect(setsToIntersect)); } export function unionSet(...sets) { const result = new Set(); for (const set of sets) { for (const item of set) { result.add(item); } } return result; } export function* map(items, fn) { for (const item of items) { yield fn(item); } } export function* flatMap(collections, fn) { for (const item of collections) { for (const result of fn(item)) { yield result; } } } export function* allChunksInGroupIterable(chunkGroup) { for (const childGroup of chunkGroup.childrenIterable) { for (const childChunk of childGroup.chunks) { yield childChunk; } } } export function* allChunksInChunkIterable(chunk) { for (const group of chunk.groupsIterable) { for (const childChunk of allChunksInGroupIterable(group)) { yield childChunk; } } } export function* allChunksInPrimaryChunkIterable(chunk) { for (const chunkGroup of chunk.groupsIterable) { if (chunkGroup.chunks[chunkGroup.chunks.length - 1] !== chunk) { // Only add sri hashes for one chunk per chunk group, // where the last chunk in the group is the primary chunk continue; } for (const childChunk of allChunksInGroupIterable(chunkGroup)) { yield childChunk; } } } export function updateAsset(compilation, assetPath, source, integrity, onUpdate) { compilation.updateAsset(assetPath, source, (assetInfo) => { if (!assetInfo) { return undefined; } onUpdate(assetInfo); return { ...assetInfo, contenthash: Array.isArray(assetInfo.contenthash) ? [...new Set([...assetInfo.contenthash, integrity])] : assetInfo.contenthash ? [assetInfo.contenthash, integrity] : integrity, }; }); } export function tryGetSource(source) { try { return source.source(); } catch (_) { return undefined; } } export function replaceInSource(compiler, source, path, replacements) { const oldSource = source.source(); if (typeof oldSource !== "string") { return source; } const newAsset = new compiler.webpack.sources.ReplaceSource(source, path); for (const match of oldSource.matchAll(placeholderRegex)) { const placeholder = match[0]; const position = match.index; if (placeholder && position !== undefined) { newAsset.replace(position, position + placeholder.length - 1, replacements.get(placeholder) || placeholder); } } return newAsset; } export function usesAnyHash(assetInfo) { return !!(assetInfo.fullhash || assetInfo.chunkhash || assetInfo.modulehash || assetInfo.contenthash); } export function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } //# sourceMappingURL=util.js.map