[6a3a178] | 1 | /**
|
---|
| 2 | * Copyright (c) 2015-present, Waysact Pty Ltd
|
---|
| 3 | *
|
---|
| 4 | * This source code is licensed under the MIT license found in the
|
---|
| 5 | * LICENSE file in the root directory of this source tree.
|
---|
| 6 | */
|
---|
| 7 |
|
---|
| 8 | var crypto = require('crypto');
|
---|
| 9 | var path = require('path');
|
---|
| 10 | var webpack = require('webpack');
|
---|
| 11 | // eslint-disable-next-line global-require
|
---|
| 12 | var ReplaceSource = (webpack.sources || require('webpack-sources')).ReplaceSource;
|
---|
| 13 | var util = require('./util');
|
---|
| 14 | var WebIntegrityJsonpMainTemplatePlugin = require('./jmtp');
|
---|
| 15 | var HtmlWebpackPlugin;
|
---|
| 16 |
|
---|
| 17 | // https://www.w3.org/TR/2016/REC-SRI-20160623/#cryptographic-hash-functions
|
---|
| 18 | var standardHashFuncNames = ['sha256', 'sha384', 'sha512'];
|
---|
| 19 |
|
---|
| 20 | try {
|
---|
| 21 | // eslint-disable-next-line global-require
|
---|
| 22 | HtmlWebpackPlugin = require('html-webpack-plugin');
|
---|
| 23 | } catch (e) {
|
---|
| 24 | if (!(e instanceof Error) || e.code !== 'MODULE_NOT_FOUND') {
|
---|
| 25 | throw e;
|
---|
| 26 | }
|
---|
| 27 | }
|
---|
| 28 |
|
---|
| 29 | function SubresourceIntegrityPlugin(options) {
|
---|
| 30 | var useOptions;
|
---|
| 31 | if (options === null || typeof options === 'undefined') {
|
---|
| 32 | useOptions = {};
|
---|
| 33 | } else if (typeof options === 'object') {
|
---|
| 34 | useOptions = options;
|
---|
| 35 | } else {
|
---|
| 36 | throw new Error('webpack-subresource-integrity: argument must be an object');
|
---|
| 37 | }
|
---|
| 38 |
|
---|
| 39 | this.options = {
|
---|
| 40 | enabled: true
|
---|
| 41 | };
|
---|
| 42 |
|
---|
| 43 | Object.assign(this.options, useOptions);
|
---|
| 44 |
|
---|
| 45 | this.emittedMessages = {};
|
---|
| 46 |
|
---|
| 47 | this.assetIntegrity = new Map();
|
---|
| 48 | }
|
---|
| 49 |
|
---|
| 50 | SubresourceIntegrityPlugin.prototype.emitMessage = function emitMessage(messages, message) {
|
---|
| 51 | messages.push(new Error('webpack-subresource-integrity: ' + message));
|
---|
| 52 | };
|
---|
| 53 |
|
---|
| 54 | SubresourceIntegrityPlugin.prototype.emitMessageOnce = function emitMessageOnce(messages, message) {
|
---|
| 55 | if (!this.emittedMessages[message]) {
|
---|
| 56 | this.emittedMessages[message] = true;
|
---|
| 57 | this.emitMessage(messages, message);
|
---|
| 58 | }
|
---|
| 59 | };
|
---|
| 60 |
|
---|
| 61 | SubresourceIntegrityPlugin.prototype.warnOnce = function warn(compilation, message) {
|
---|
| 62 | this.emitMessageOnce(compilation.warnings, message);
|
---|
| 63 | };
|
---|
| 64 |
|
---|
| 65 | SubresourceIntegrityPlugin.prototype.error = function error(compilation, message) {
|
---|
| 66 | this.emitMessage(compilation.errors, message);
|
---|
| 67 | };
|
---|
| 68 |
|
---|
| 69 | SubresourceIntegrityPlugin.prototype.errorOnce = function error(compilation, message) {
|
---|
| 70 | this.emitMessageOnce(compilation.errors, message);
|
---|
| 71 | };
|
---|
| 72 |
|
---|
| 73 | SubresourceIntegrityPlugin.prototype.validateOptions = function validateOptions(compilation) {
|
---|
| 74 | if (this.optionsValidated) {
|
---|
| 75 | return;
|
---|
| 76 | }
|
---|
| 77 | this.optionsValidated = true;
|
---|
| 78 |
|
---|
| 79 | if (this.options.enabled && !compilation.compiler.options.output.crossOriginLoading) {
|
---|
| 80 | this.warnOnce(
|
---|
| 81 | compilation,
|
---|
| 82 | 'SRI requires a cross-origin policy, defaulting to "anonymous". ' +
|
---|
| 83 | 'Set webpack option output.crossOriginLoading to a value other than false ' +
|
---|
| 84 | 'to make this warning go away. ' +
|
---|
| 85 | 'See https://w3c.github.io/webappsec-subresource-integrity/#cross-origin-data-leakage'
|
---|
| 86 | );
|
---|
| 87 | }
|
---|
| 88 | this.validateHashFuncNames(compilation);
|
---|
| 89 | };
|
---|
| 90 |
|
---|
| 91 | SubresourceIntegrityPlugin.prototype.validateHashFuncNames =
|
---|
| 92 | function validateHashFuncNames(compilation) {
|
---|
| 93 | if (!Array.isArray(this.options.hashFuncNames)) {
|
---|
| 94 | this.error(
|
---|
| 95 | compilation,
|
---|
| 96 | 'options.hashFuncNames must be an array of hash function names, ' +
|
---|
| 97 | 'instead got \'' + this.options.hashFuncNames + '\'.');
|
---|
| 98 | this.options.enabled = false;
|
---|
| 99 | } else if (
|
---|
| 100 | !this.options.hashFuncNames.every(this.validateHashFuncName.bind(this, compilation))
|
---|
| 101 | ) {
|
---|
| 102 | this.options.enabled = false;
|
---|
| 103 | } else {
|
---|
| 104 | this.warnStandardHashFunc(compilation);
|
---|
| 105 | }
|
---|
| 106 | };
|
---|
| 107 |
|
---|
| 108 | SubresourceIntegrityPlugin.prototype.warnStandardHashFunc =
|
---|
| 109 | function warnStandardHashFunc(compilation) {
|
---|
| 110 | var foundStandardHashFunc = false;
|
---|
| 111 | var i;
|
---|
| 112 | for (i = 0; i < this.options.hashFuncNames.length; i += 1) {
|
---|
| 113 | if (standardHashFuncNames.indexOf(this.options.hashFuncNames[i]) >= 0) {
|
---|
| 114 | foundStandardHashFunc = true;
|
---|
| 115 | }
|
---|
| 116 | }
|
---|
| 117 | if (!foundStandardHashFunc) {
|
---|
| 118 | this.warnOnce(
|
---|
| 119 | compilation,
|
---|
| 120 | 'It is recommended that at least one hash function is part of the set ' +
|
---|
| 121 | 'for which support is mandated by the specification. ' +
|
---|
| 122 | 'These are: ' + standardHashFuncNames.join(', ') + '. ' +
|
---|
| 123 | 'See http://www.w3.org/TR/SRI/#cryptographic-hash-functions for more information.');
|
---|
| 124 | }
|
---|
| 125 | };
|
---|
| 126 |
|
---|
| 127 | SubresourceIntegrityPlugin.prototype.validateHashFuncName =
|
---|
| 128 | function validateHashFuncName(compilation, hashFuncName) {
|
---|
| 129 | if (typeof hashFuncName !== 'string' &&
|
---|
| 130 | !(hashFuncName instanceof String)) {
|
---|
| 131 | this.error(
|
---|
| 132 | compilation,
|
---|
| 133 | 'options.hashFuncNames must be an array of hash function names, ' +
|
---|
| 134 | 'but contained ' + hashFuncName + '.');
|
---|
| 135 | return false;
|
---|
| 136 | }
|
---|
| 137 | try {
|
---|
| 138 | crypto.createHash(hashFuncName);
|
---|
| 139 | } catch (error) {
|
---|
| 140 | this.error(
|
---|
| 141 | compilation,
|
---|
| 142 | 'Cannot use hash function \'' + hashFuncName + '\': ' +
|
---|
| 143 | error.message);
|
---|
| 144 | return false;
|
---|
| 145 | }
|
---|
| 146 | return true;
|
---|
| 147 | };
|
---|
| 148 |
|
---|
| 149 | /* Given a public URL path to an asset, as generated by
|
---|
| 150 | * HtmlWebpackPlugin for use as a `<script src>` or `<link href`> URL
|
---|
| 151 | * in `index.html`, return the path to the asset, suitable as a key
|
---|
| 152 | * into `compilation.assets`.
|
---|
| 153 | */
|
---|
| 154 | SubresourceIntegrityPlugin.prototype.hwpAssetPath = function hwpAssetPath(src) {
|
---|
| 155 | return path.relative(this.hwpPublicPath, src);
|
---|
| 156 | };
|
---|
| 157 |
|
---|
| 158 | SubresourceIntegrityPlugin.prototype.warnIfHotUpdate = function warnIfHotUpdate(
|
---|
| 159 | compilation, source
|
---|
| 160 | ) {
|
---|
| 161 | if (source.indexOf('webpackHotUpdate') >= 0) {
|
---|
| 162 | this.warnOnce(
|
---|
| 163 | compilation,
|
---|
| 164 | 'webpack-subresource-integrity may interfere with hot reloading. ' +
|
---|
| 165 | 'Consider disabling this plugin in development mode.'
|
---|
| 166 | );
|
---|
| 167 | }
|
---|
| 168 | };
|
---|
| 169 |
|
---|
| 170 | SubresourceIntegrityPlugin.prototype.replaceAsset = function replaceAsset(
|
---|
| 171 | assets,
|
---|
| 172 | hashByChunkId,
|
---|
| 173 | chunkFile
|
---|
| 174 | ) {
|
---|
| 175 | var oldSource = assets[chunkFile].source();
|
---|
| 176 | var newAsset;
|
---|
| 177 | var magicMarker;
|
---|
| 178 | var magicMarkerPos;
|
---|
| 179 | var hashFuncNames = this.options.hashFuncNames;
|
---|
| 180 |
|
---|
| 181 | newAsset = new ReplaceSource(assets[chunkFile]);
|
---|
| 182 |
|
---|
| 183 | Array.from(hashByChunkId.entries()).forEach(function replaceMagicMarkers(idAndHash) {
|
---|
| 184 | magicMarker = util.makePlaceholder(hashFuncNames, idAndHash[0]);
|
---|
| 185 | magicMarkerPos = oldSource.indexOf(magicMarker);
|
---|
| 186 | if (magicMarkerPos >= 0) {
|
---|
| 187 | newAsset.replace(
|
---|
| 188 | magicMarkerPos,
|
---|
| 189 | (magicMarkerPos + magicMarker.length) - 1,
|
---|
| 190 | idAndHash[1]);
|
---|
| 191 | }
|
---|
| 192 | });
|
---|
| 193 |
|
---|
| 194 | // eslint-disable-next-line no-param-reassign
|
---|
| 195 | assets[chunkFile] = newAsset;
|
---|
| 196 |
|
---|
| 197 | newAsset.integrity = util.computeIntegrity(hashFuncNames, newAsset.source());
|
---|
| 198 | return newAsset;
|
---|
| 199 | };
|
---|
| 200 |
|
---|
| 201 | SubresourceIntegrityPlugin.prototype.processChunk = function processChunk(
|
---|
| 202 | chunk, compilation, assets
|
---|
| 203 | ) {
|
---|
| 204 | var self = this;
|
---|
| 205 | var newAsset;
|
---|
| 206 | var hashByChunkId = new Map();
|
---|
| 207 |
|
---|
| 208 | Array.from(util.findChunks(chunk)).reverse().forEach(childChunk => {
|
---|
| 209 | var sourcePath;
|
---|
| 210 | var files = Array.isArray(childChunk.files) ? new Set(childChunk.files) : childChunk.files;
|
---|
| 211 |
|
---|
| 212 | // This can happen with invalid Webpack configurations
|
---|
| 213 | if (files.size === 0) return;
|
---|
| 214 |
|
---|
| 215 | sourcePath = compilation.sriChunkAssets[childChunk.id];
|
---|
| 216 |
|
---|
| 217 | if (!files.has(sourcePath)) {
|
---|
| 218 | self.warnOnce(
|
---|
| 219 | compilation,
|
---|
| 220 | 'Cannot determine asset for chunk ' + childChunk.id + ', computed="' + sourcePath +
|
---|
| 221 | '", available=' + Array.from(childChunk.files)[0] + '. Please report this full error message ' +
|
---|
| 222 | 'along with your Webpack configuration at ' +
|
---|
| 223 | 'https://github.com/waysact/webpack-subresource-integrity/issues/new'
|
---|
| 224 | );
|
---|
| 225 | sourcePath = Array.from(files)[0];
|
---|
| 226 | }
|
---|
| 227 |
|
---|
| 228 | self.warnIfHotUpdate(compilation, assets[sourcePath].source());
|
---|
| 229 | newAsset = self.replaceAsset(assets, hashByChunkId, sourcePath);
|
---|
| 230 | hashByChunkId.set(childChunk.id, newAsset.integrity);
|
---|
| 231 | this.assetIntegrity.set(sourcePath, newAsset.integrity);
|
---|
| 232 | });
|
---|
| 233 | };
|
---|
| 234 |
|
---|
| 235 | SubresourceIntegrityPlugin.prototype.chunkAsset =
|
---|
| 236 | function chunkAsset(compilation, chunk, asset) {
|
---|
| 237 | if (compilation.assets[asset]) {
|
---|
| 238 | // eslint-disable-next-line no-param-reassign
|
---|
| 239 | compilation.sriChunkAssets[chunk.id] = asset;
|
---|
| 240 | }
|
---|
| 241 | };
|
---|
| 242 |
|
---|
| 243 | SubresourceIntegrityPlugin.prototype.statsFactory =
|
---|
| 244 | function stats(compilation, statsFactory) {
|
---|
| 245 | statsFactory.hooks.extract.for("asset").tap('SriPlugin', (object, asset) => {
|
---|
| 246 | if (this.assetIntegrity.has(asset.name)) {
|
---|
| 247 | // eslint-disable-next-line no-param-reassign
|
---|
| 248 | object.integrity = String(this.assetIntegrity.get(asset.name));
|
---|
| 249 | }
|
---|
| 250 | });
|
---|
| 251 | };
|
---|
| 252 |
|
---|
| 253 | SubresourceIntegrityPlugin.prototype.addMissingIntegrityHashes =
|
---|
| 254 | function addMissingIntegrityHashes(assets) {
|
---|
| 255 | var self = this;
|
---|
| 256 | Object.keys(assets).forEach(function loop(assetKey) {
|
---|
| 257 | var asset = assets[assetKey];
|
---|
| 258 | if (!asset.integrity) {
|
---|
| 259 | asset.integrity = util.computeIntegrity(self.options.hashFuncNames, asset.source());
|
---|
| 260 | }
|
---|
| 261 | });
|
---|
| 262 | };
|
---|
| 263 |
|
---|
| 264 | /*
|
---|
| 265 | * Calculate SRI values for each chunk and replace the magic
|
---|
| 266 | * placeholders by the actual values.
|
---|
| 267 | */
|
---|
| 268 | SubresourceIntegrityPlugin.prototype.afterOptimizeAssets =
|
---|
| 269 | function afterOptimizeAssets(compilation, assets) {
|
---|
| 270 | var self = this;
|
---|
| 271 |
|
---|
| 272 | Array.from(compilation.chunks).filter(util.isRuntimeChunk).forEach(function forEachChunk(chunk) {
|
---|
| 273 | self.processChunk(chunk, compilation, assets);
|
---|
| 274 | });
|
---|
| 275 |
|
---|
| 276 | this.addMissingIntegrityHashes(assets);
|
---|
| 277 | };
|
---|
| 278 |
|
---|
| 279 | SubresourceIntegrityPlugin.prototype.processTag =
|
---|
| 280 | function processTag(compilation, tag) {
|
---|
| 281 | var src = this.hwpAssetPath(util.getTagSrc(tag));
|
---|
| 282 | /* eslint-disable no-param-reassign */
|
---|
| 283 | var integrity = util.getIntegrityChecksumForAsset(compilation.assets, src);
|
---|
| 284 | if (!Object.prototype.hasOwnProperty.call(tag.attributes, "integrity")) {
|
---|
| 285 | tag.attributes.integrity = integrity;
|
---|
| 286 | tag.attributes.crossorigin = compilation.compiler.options.output.crossOriginLoading || 'anonymous';
|
---|
| 287 | }
|
---|
| 288 | /* eslint-enable no-param-reassign */
|
---|
| 289 | };
|
---|
| 290 |
|
---|
| 291 | SubresourceIntegrityPlugin.prototype.alterAssetTags =
|
---|
| 292 | function alterAssetTags(compilation, pluginArgs, callback) {
|
---|
| 293 | /* html-webpack-plugin has added an event so we can pre-process the html tags before they
|
---|
| 294 | inject them. This does the work.
|
---|
| 295 | */
|
---|
| 296 | var processTag = this.processTag.bind(this, compilation);
|
---|
| 297 | pluginArgs.head.filter(util.filterTag).forEach(processTag);
|
---|
| 298 | pluginArgs.body.filter(util.filterTag).forEach(processTag);
|
---|
| 299 | callback(null, pluginArgs);
|
---|
| 300 | };
|
---|
| 301 |
|
---|
| 302 |
|
---|
| 303 | /* Add jsIntegrity and cssIntegrity properties to pluginArgs, to
|
---|
| 304 | * go along with js and css properties. These are later
|
---|
| 305 | * accessible on `htmlWebpackPlugin.files`.
|
---|
| 306 | */
|
---|
| 307 | SubresourceIntegrityPlugin.prototype.beforeHtmlGeneration =
|
---|
| 308 | function beforeHtmlGeneration(compilation, pluginArgs, callback) {
|
---|
| 309 | var self = this;
|
---|
| 310 | this.hwpPublicPath = pluginArgs.assets.publicPath;
|
---|
| 311 | this.addMissingIntegrityHashes(compilation.assets);
|
---|
| 312 |
|
---|
| 313 | ['js', 'css'].forEach(function addIntegrity(fileType) {
|
---|
| 314 | // eslint-disable-next-line no-param-reassign
|
---|
| 315 | pluginArgs.assets[fileType + 'Integrity'] =
|
---|
| 316 | pluginArgs.assets[fileType].map(function assetIntegrity(filePath) {
|
---|
| 317 | return util.getIntegrityChecksumForAsset(compilation.assets, self.hwpAssetPath(filePath));
|
---|
| 318 | });
|
---|
| 319 | });
|
---|
| 320 | callback(null, pluginArgs);
|
---|
| 321 | };
|
---|
| 322 |
|
---|
| 323 | SubresourceIntegrityPlugin.prototype.registerJMTP = function registerJMTP(compilation) {
|
---|
| 324 | var plugin = new WebIntegrityJsonpMainTemplatePlugin(this, compilation);
|
---|
| 325 | if (plugin.apply) {
|
---|
| 326 | plugin.apply(compilation.mainTemplate);
|
---|
| 327 | } else {
|
---|
| 328 | compilation.mainTemplate.apply(plugin);
|
---|
| 329 | }
|
---|
| 330 | };
|
---|
| 331 |
|
---|
| 332 | SubresourceIntegrityPlugin.prototype.registerHwpHooks =
|
---|
| 333 | function registerHwpHooks(alterAssetTags, beforeHtmlGeneration, hwpCompilation) {
|
---|
| 334 | var self = this;
|
---|
| 335 | if (HtmlWebpackPlugin && HtmlWebpackPlugin.getHooks) {
|
---|
| 336 | // HtmlWebpackPlugin >= 4
|
---|
| 337 | HtmlWebpackPlugin.getHooks(hwpCompilation).beforeAssetTagGeneration.tapAsync(
|
---|
| 338 | 'sri',
|
---|
| 339 | this.beforeHtmlGeneration.bind(this, hwpCompilation)
|
---|
| 340 | );
|
---|
| 341 |
|
---|
| 342 | HtmlWebpackPlugin.getHooks(hwpCompilation).alterAssetTags.tapAsync(
|
---|
| 343 | 'sri',
|
---|
| 344 | function cb(data, callback) {
|
---|
| 345 | var processTag = self.processTag.bind(self, hwpCompilation);
|
---|
| 346 | data.assetTags.scripts.filter(util.filterTag).forEach(processTag);
|
---|
| 347 | data.assetTags.styles.filter(util.filterTag).forEach(processTag);
|
---|
| 348 | callback(null, data);
|
---|
| 349 | }
|
---|
| 350 | );
|
---|
| 351 | } else if (hwpCompilation.hooks.htmlWebpackPluginAlterAssetTags &&
|
---|
| 352 | hwpCompilation.hooks.htmlWebpackPluginBeforeHtmlGeneration) {
|
---|
| 353 | // HtmlWebpackPlugin 3
|
---|
| 354 | hwpCompilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync('SriPlugin', alterAssetTags);
|
---|
| 355 | hwpCompilation.hooks.htmlWebpackPluginBeforeHtmlGeneration.tapAsync('SriPlugin', beforeHtmlGeneration);
|
---|
| 356 | }
|
---|
| 357 | };
|
---|
| 358 |
|
---|
| 359 | SubresourceIntegrityPlugin.prototype.thisCompilation = function thisCompilation(
|
---|
| 360 | compiler,
|
---|
| 361 | compilation
|
---|
| 362 | ) {
|
---|
| 363 | this.validateOptions(compilation);
|
---|
| 364 |
|
---|
| 365 | if (!this.options.enabled) {
|
---|
| 366 | return;
|
---|
| 367 | }
|
---|
| 368 |
|
---|
| 369 | this.registerJMTP(compilation);
|
---|
| 370 |
|
---|
| 371 | // FIXME: refactor into separate per-compilation state
|
---|
| 372 | // eslint-disable-next-line no-param-reassign
|
---|
| 373 | compilation.sriChunkAssets = {};
|
---|
| 374 |
|
---|
| 375 | /*
|
---|
| 376 | * html-webpack support:
|
---|
| 377 | * Modify the asset tags before webpack injects them for anything with an integrity value.
|
---|
| 378 | */
|
---|
| 379 | if (compiler.hooks) {
|
---|
| 380 | this.setupHooks(compiler, compilation);
|
---|
| 381 | } else {
|
---|
| 382 | this.setupLegacyHooks(compiler, compilation);
|
---|
| 383 | }
|
---|
| 384 | };
|
---|
| 385 |
|
---|
| 386 | SubresourceIntegrityPlugin.prototype.setupHooks = function setupHooks(
|
---|
| 387 | compiler,
|
---|
| 388 | compilation
|
---|
| 389 | ) {
|
---|
| 390 | var afterOptimizeAssets = this.afterOptimizeAssets.bind(this, compilation);
|
---|
| 391 | var beforeChunkAssets = this.beforeChunkAssets.bind(this, compilation);
|
---|
| 392 | var alterAssetTags = this.alterAssetTags.bind(this, compilation);
|
---|
| 393 | var beforeHtmlGeneration = this.beforeHtmlGeneration.bind(this, compilation);
|
---|
| 394 |
|
---|
| 395 | if (compilation.hooks.processAssets) {
|
---|
| 396 | compilation.hooks.processAssets.tap(
|
---|
| 397 | {
|
---|
| 398 | name: 'SriPlugin',
|
---|
| 399 | stage: webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH
|
---|
| 400 | },
|
---|
| 401 | afterOptimizeAssets
|
---|
| 402 | );
|
---|
| 403 | } else {
|
---|
| 404 | compilation.hooks.afterOptimizeAssets.tap('SriPlugin', afterOptimizeAssets);
|
---|
| 405 | }
|
---|
| 406 | compilation.hooks.beforeChunkAssets.tap('SriPlugin', beforeChunkAssets);
|
---|
| 407 | compiler.hooks.compilation.tap(
|
---|
| 408 | 'HtmlWebpackPluginHooks',
|
---|
| 409 | this.registerHwpHooks.bind(this, alterAssetTags, beforeHtmlGeneration)
|
---|
| 410 | );
|
---|
| 411 | };
|
---|
| 412 |
|
---|
| 413 | SubresourceIntegrityPlugin.prototype.setupLegacyHooks = function setupLegacyHooks(
|
---|
| 414 | compiler,
|
---|
| 415 | compilation
|
---|
| 416 | ) {
|
---|
| 417 | var afterOptimizeAssets = this.afterOptimizeAssets.bind(this, compilation);
|
---|
| 418 | var beforeChunkAssets = this.beforeChunkAssets.bind(this, compilation);
|
---|
| 419 | var alterAssetTags = this.alterAssetTags.bind(this, compilation);
|
---|
| 420 | var beforeHtmlGeneration = this.beforeHtmlGeneration.bind(this, compilation);
|
---|
| 421 |
|
---|
| 422 | compilation.plugin('after-optimize-assets', afterOptimizeAssets);
|
---|
| 423 | compilation.plugin('before-chunk-assets', beforeChunkAssets);
|
---|
| 424 | compilation.plugin('html-webpack-plugin-alter-asset-tags', alterAssetTags);
|
---|
| 425 | compilation.plugin(
|
---|
| 426 | 'html-webpack-plugin-before-html-generation',
|
---|
| 427 | beforeHtmlGeneration
|
---|
| 428 | );
|
---|
| 429 | };
|
---|
| 430 |
|
---|
| 431 | SubresourceIntegrityPlugin.prototype.beforeChunkAssets = function afterPlugins(compilation) {
|
---|
| 432 | var chunkAsset = this.chunkAsset.bind(this, compilation);
|
---|
| 433 | if (compilation.hooks) {
|
---|
| 434 | compilation.hooks.chunkAsset.tap('SriPlugin', chunkAsset);
|
---|
| 435 | } else {
|
---|
| 436 | compilation.plugin('chunk-asset', chunkAsset);
|
---|
| 437 | }
|
---|
| 438 | };
|
---|
| 439 |
|
---|
| 440 | SubresourceIntegrityPlugin.prototype.afterPlugins = function afterPlugins(compiler) {
|
---|
| 441 | if (compiler.hooks) {
|
---|
| 442 | compiler.hooks.thisCompilation.tap('SriPlugin', this.thisCompilation.bind(this, compiler));
|
---|
| 443 |
|
---|
| 444 | compiler.hooks.compilation.tap("DefaultStatsFactoryPlugin", compilation => {
|
---|
| 445 | if (compilation.hooks.statsFactory) {
|
---|
| 446 | compilation.hooks.statsFactory.tap('SriPlugin', this.statsFactory.bind(this, compilation));
|
---|
| 447 | }
|
---|
| 448 | });
|
---|
| 449 |
|
---|
| 450 | } else {
|
---|
| 451 | compiler.plugin('this-compilation', this.thisCompilation.bind(this, compiler));
|
---|
| 452 | }
|
---|
| 453 | };
|
---|
| 454 |
|
---|
| 455 | SubresourceIntegrityPlugin.prototype.apply = function apply(compiler) {
|
---|
| 456 | if (compiler.hooks) {
|
---|
| 457 | compiler.hooks.afterPlugins.tap('SriPlugin', this.afterPlugins.bind(this));
|
---|
| 458 | } else {
|
---|
| 459 | compiler.plugin('after-plugins', this.afterPlugins.bind(this));
|
---|
| 460 | }
|
---|
| 461 | };
|
---|
| 462 |
|
---|
| 463 | module.exports = SubresourceIntegrityPlugin;
|
---|