UNPKG

webpack-subresource-integrity

Version:
220 lines 7.35 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 { relative, join } from "path"; import { readFileSync } from "fs"; import { assert, computeIntegrity, makePlaceholder, findChunks, normalizePath, getTagSrc, notNil, sriHashVariableReference, updateAsset, tryGetSource, replaceInSource, usesAnyHash, } from "./util"; import { getChunkToManifestMap } from "./manifest"; import { AssetIntegrity } from "./integrity"; const assetTypeIntegrityKeys = [ ["js", "jsIntegrity"], ["css", "cssIntegrity"], ]; export class Plugin { /** * @internal */ compilation; /** * @internal */ options; /** * @internal */ reporter; /** * @internal */ assetIntegrity; /** * @internal */ hwpPublicPath = null; /** * @internal */ sortedSccChunks = []; /** * @internal */ chunkManifest = new Map(); /** * @internal */ hashByPlaceholder = new Map(); constructor(compilation, options, reporter) { this.compilation = compilation; this.options = options; this.reporter = reporter; this.assetIntegrity = new AssetIntegrity(this.options.hashFuncNames); } /** * @internal */ warnIfHotUpdate(source) { if (source.indexOf("webpackHotUpdate") >= 0) { this.reporter.warnHotReloading(); } } /** * @internal */ addMissingIntegrityHashes = (assets) => { Object.entries(assets).forEach(([assetKey, asset]) => { const source = tryGetSource(asset); if (source && !this.assetIntegrity.has(assetKey)) { this.assetIntegrity.updateFromSource(assetKey, source); } }); }; /** * @internal */ replaceAsset = (compiler, assets, hashByPlaceholder, chunkFile) => { const asset = assets[chunkFile]; assert(asset, `Missing asset for file ${chunkFile}`); return replaceInSource(compiler, asset, chunkFile, hashByPlaceholder); }; warnAboutLongTermCaching = (assetInfo) => { if (usesAnyHash(assetInfo) && !(assetInfo.contenthash && this.compilation.compiler.options.optimization.realContentHash)) { this.reporter.warnContentHash(); } }; /** * @internal */ processChunk = (chunk, assets) => { Array.from(findChunks(chunk)) .reverse() .forEach((chunk) => this.processChunkAssets(chunk, assets)); }; processChunkAssets = (childChunk, assets) => { Array.from(childChunk.files).forEach((sourcePath) => { const asset = assets[sourcePath]; if (asset) { this.warnIfHotUpdate(asset.source()); const newAsset = this.replaceAsset(this.compilation.compiler, assets, this.hashByPlaceholder, sourcePath); const integrity = this.assetIntegrity.updateFromSource(sourcePath, newAsset.source()); if (childChunk.id !== null) { this.hashByPlaceholder.set(makePlaceholder(this.options.hashFuncNames, childChunk.id), integrity); } updateAsset(this.compilation, sourcePath, newAsset, integrity, this.warnAboutLongTermCaching); } else { this.reporter.warnNoAssetsFound(sourcePath, Object.keys(assets)); } }); }; /** * @internal */ addAttribute = (elName, source) => { if (!this.compilation.outputOptions.crossOriginLoading) { this.reporter.errorCrossOriginLoadingNotSet(); } return this.compilation.compiler.webpack.Template.asString([ source, elName + `.integrity = ${sriHashVariableReference}[chunkId];`, elName + ".crossOrigin = " + JSON.stringify(this.compilation.outputOptions.crossOriginLoading) + ";", ]); }; /** * @internal */ processAssets = (assets) => { if (this.options.hashLoading === "lazy") { for (const scc of this.sortedSccChunks) { for (const chunk of scc.nodes) { this.processChunkAssets(chunk, assets); } } } else { Array.from(this.compilation.chunks) .filter((chunk) => chunk.hasRuntime()) .forEach((chunk) => { this.processChunk(chunk, assets); }); } this.addMissingIntegrityHashes(assets); }; /** * @internal */ hwpAssetPath = (src) => { assert(this.hwpPublicPath !== null, "Missing HtmlWebpackPlugin publicPath"); return relative(this.hwpPublicPath, src); }; /** * @internal */ getIntegrityChecksumForAsset = (assets, src) => { if (this.assetIntegrity.has(src)) { return this.assetIntegrity.get(src); } const normalizedSrc = normalizePath(src); const normalizedKey = Object.keys(assets).find((assetKey) => normalizePath(assetKey) === normalizedSrc); return normalizedKey ? this.assetIntegrity.get(normalizedKey) : undefined; }; /** * @internal */ processTag = (tag) => { if (tag.attributes && Object.prototype.hasOwnProperty.call(tag.attributes, "integrity")) { return; } const tagSrc = getTagSrc(tag); if (!tagSrc) { return; } const src = this.hwpAssetPath(tagSrc); tag.attributes["integrity"] = this.getIntegrityChecksumForAsset(this.compilation.assets, src) || computeIntegrity(this.options.hashFuncNames, readFileSync(join(this.compilation.compiler.outputPath, src))); tag.attributes["crossorigin"] = this.compilation.compiler.options.output.crossOriginLoading || "anonymous"; }; /** * @internal */ beforeRuntimeRequirements = () => { if (this.options.hashLoading === "lazy") { const [sortedSccChunks, chunkManifest] = getChunkToManifestMap(this.compilation.chunks); this.sortedSccChunks = sortedSccChunks; this.chunkManifest = chunkManifest; } this.hashByPlaceholder.clear(); }; getChildChunksToAddToChunkManifest(chunk) { return this.chunkManifest.get(chunk) ?? new Set(); } handleHwpPluginArgs = ({ assets }) => { this.hwpPublicPath = assets.publicPath; assetTypeIntegrityKeys.forEach(([a, b]) => { if (b) { assets[b] = assets[a] .map((filePath) => this.getIntegrityChecksumForAsset(this.compilation.assets, this.hwpAssetPath(filePath))) .filter(notNil); } }); }; handleHwpBodyTags = ({ headTags, bodyTags, }) => { this.addMissingIntegrityHashes(this.compilation.assets); headTags.concat(bodyTags).forEach(this.processTag); }; updateHash(input, oldHash) { return this.assetIntegrity.updateHash(input, oldHash); } } //# sourceMappingURL=plugin.js.map