193b454326c649b1113b5125fd2fffc31ecb3cfd4d90e95778c9e8a884f1ee0eea40cf7e2c7f29fa9bc4a4f5093811350b9ec1cc2b76536600b148e9ec1a9f 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _path = _interopRequireDefault(require("path"));
  7. var _webpack = _interopRequireWildcard(require("webpack"));
  8. var _schemaUtils = require("schema-utils");
  9. var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
  10. var _options = _interopRequireDefault(require("./options.json"));
  11. function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
  12. function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  13. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  14. /*
  15. MIT License http://www.opensource.org/licenses/mit-license.php
  16. Author Tobias Koppers @sokra
  17. */
  18. const internalCreateHash = algorithm => {
  19. try {
  20. // eslint-disable-next-line global-require import/no-unresolved
  21. const createHash = require("webpack/lib/util/createHash");
  22. return createHash(algorithm);
  23. } catch (err) {// Ignore
  24. }
  25. return require("crypto").createHash(algorithm);
  26. };
  27. const {
  28. RawSource
  29. } = // eslint-disable-next-line global-require
  30. _webpack.default.sources || require("webpack-sources");
  31. class CompressionPlugin {
  32. constructor(options = {}) {
  33. (0, _schemaUtils.validate)(_options.default, options, {
  34. name: "Compression Plugin",
  35. baseDataPath: "options"
  36. });
  37. const {
  38. test,
  39. include,
  40. exclude,
  41. cache = true,
  42. algorithm = "gzip",
  43. compressionOptions = {},
  44. filename = "[path][base].gz",
  45. threshold = 0,
  46. minRatio = 0.8,
  47. deleteOriginalAssets = false
  48. } = options;
  49. this.options = {
  50. test,
  51. include,
  52. exclude,
  53. cache,
  54. algorithm,
  55. compressionOptions,
  56. filename,
  57. threshold,
  58. minRatio,
  59. deleteOriginalAssets
  60. };
  61. this.algorithm = this.options.algorithm;
  62. if (typeof this.algorithm === "string") {
  63. // eslint-disable-next-line global-require
  64. const zlib = require("zlib");
  65. this.algorithm = zlib[this.algorithm];
  66. if (!this.algorithm) {
  67. throw new Error(`Algorithm "${this.options.algorithm}" is not found in "zlib"`);
  68. }
  69. const defaultCompressionOptions = {
  70. gzip: {
  71. level: zlib.constants.Z_BEST_COMPRESSION
  72. },
  73. deflate: {
  74. level: zlib.constants.Z_BEST_COMPRESSION
  75. },
  76. deflateRaw: {
  77. level: zlib.constants.Z_BEST_COMPRESSION
  78. },
  79. brotliCompress: {
  80. params: {
  81. [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY
  82. }
  83. }
  84. }[algorithm] || {};
  85. this.options.compressionOptions = { ...defaultCompressionOptions,
  86. ...this.options.compressionOptions
  87. };
  88. }
  89. } // eslint-disable-next-line consistent-return
  90. static getAsset(compilation, name) {
  91. // New API
  92. if (compilation.getAsset) {
  93. return compilation.getAsset(name);
  94. }
  95. if (compilation.assets[name]) {
  96. return {
  97. name,
  98. source: compilation.assets[name],
  99. info: {}
  100. };
  101. }
  102. }
  103. static emitAsset(compilation, name, source, assetInfo) {
  104. // New API
  105. if (compilation.emitAsset) {
  106. compilation.emitAsset(name, source, assetInfo);
  107. } // eslint-disable-next-line no-param-reassign
  108. compilation.assets[name] = source;
  109. }
  110. static updateAsset(compilation, name, newSource, assetInfo) {
  111. // New API
  112. if (compilation.updateAsset) {
  113. compilation.updateAsset(name, newSource, assetInfo);
  114. } // eslint-disable-next-line no-param-reassign
  115. compilation.assets[name] = newSource;
  116. }
  117. static deleteAsset(compilation, name) {
  118. // New API
  119. if (compilation.deleteAsset) {
  120. compilation.deleteAsset(name);
  121. } // eslint-disable-next-line no-param-reassign
  122. delete compilation.assets[name];
  123. }
  124. runCompressionAlgorithm(input) {
  125. return new Promise((resolve, reject) => {
  126. this.algorithm(input, this.options.compressionOptions, (error, result) => {
  127. if (error) {
  128. return reject(error);
  129. }
  130. if (!Buffer.isBuffer(result)) {
  131. // eslint-disable-next-line no-param-reassign
  132. result = Buffer.from(result);
  133. }
  134. return resolve(result);
  135. });
  136. });
  137. }
  138. async compress(compilation, assets, CacheEngine, weakCache) {
  139. const assetNames = Object.keys(typeof assets === "undefined" ? compilation.assets : assets).filter(assetName => // eslint-disable-next-line no-undefined
  140. _webpack.ModuleFilenameHelpers.matchObject.bind(undefined, this.options)(assetName));
  141. if (assetNames.length === 0) {
  142. return Promise.resolve();
  143. }
  144. const scheduledTasks = [];
  145. const cache = new CacheEngine(compilation, {
  146. cache: this.options.cache
  147. }, weakCache);
  148. for (const name of assetNames) {
  149. scheduledTasks.push((async () => {
  150. const {
  151. source: inputSource,
  152. info
  153. } = CompressionPlugin.getAsset(compilation, name);
  154. if (info.compressed) {
  155. return;
  156. }
  157. let relatedName;
  158. if (typeof this.options.algorithm === "function") {
  159. let filenameForRelatedName = this.options.filename;
  160. const index = filenameForRelatedName.lastIndexOf("?");
  161. if (index >= 0) {
  162. filenameForRelatedName = filenameForRelatedName.substr(0, index);
  163. }
  164. relatedName = `${_path.default.extname(filenameForRelatedName).slice(1)}ed`;
  165. } else if (this.options.algorithm === "gzip") {
  166. relatedName = "gzipped";
  167. } else {
  168. relatedName = `${this.options.algorithm}ed`;
  169. }
  170. if (info.related && info.related[relatedName]) {
  171. return;
  172. }
  173. let input = inputSource.source();
  174. if (!Buffer.isBuffer(input)) {
  175. input = Buffer.from(input);
  176. }
  177. if (input.length < this.options.threshold) {
  178. return;
  179. }
  180. const cacheData = {
  181. inputSource
  182. };
  183. if (CompressionPlugin.isWebpack4()) {
  184. cacheData.cacheKeys = {
  185. nodeVersion: process.version,
  186. // eslint-disable-next-line global-require
  187. "compression-webpack-plugin": require("../package.json").version,
  188. algorithm: this.algorithm,
  189. originalAlgorithm: this.options.algorithm,
  190. compressionOptions: this.options.compressionOptions,
  191. name,
  192. contentHash: internalCreateHash("md4").update(input).digest("hex")
  193. };
  194. } else {
  195. cacheData.name = (0, _serializeJavascript.default)({
  196. name,
  197. algorithm: this.options.algorithm,
  198. compressionOptions: this.options.compressionOptions
  199. });
  200. }
  201. let output = await cache.get(cacheData, {
  202. RawSource
  203. });
  204. if (!output) {
  205. try {
  206. output = new RawSource(await this.runCompressionAlgorithm(input));
  207. } catch (error) {
  208. compilation.errors.push(error);
  209. return;
  210. }
  211. cacheData.output = output;
  212. await cache.store(cacheData);
  213. }
  214. if (output.source().length / input.length > this.options.minRatio) {
  215. return;
  216. }
  217. const match = /^([^?#]*)(\?[^#]*)?(#.*)?$/.exec(name);
  218. const [, replacerFile] = match;
  219. const replacerQuery = match[2] || "";
  220. const replacerFragment = match[3] || "";
  221. const replacerExt = _path.default.extname(replacerFile);
  222. const replacerBase = _path.default.basename(replacerFile);
  223. const replacerName = replacerBase.slice(0, replacerBase.length - replacerExt.length);
  224. const replacerPath = replacerFile.slice(0, replacerFile.length - replacerBase.length);
  225. const pathData = {
  226. file: replacerFile,
  227. query: replacerQuery,
  228. fragment: replacerFragment,
  229. path: replacerPath,
  230. base: replacerBase,
  231. name: replacerName,
  232. ext: replacerExt || ""
  233. };
  234. let newFilename = this.options.filename;
  235. if (typeof newFilename === "function") {
  236. newFilename = newFilename(pathData);
  237. }
  238. const newName = newFilename.replace(/\[(file|query|fragment|path|base|name|ext)]/g, (p0, p1) => pathData[p1]);
  239. const newInfo = {
  240. compressed: true
  241. };
  242. if (info.immutable && /(\[name]|\[base]|\[file])/.test(newFilename)) {
  243. newInfo.immutable = true;
  244. }
  245. if (this.options.deleteOriginalAssets) {
  246. if (this.options.deleteOriginalAssets === "keep-source-map") {
  247. // TODO `...` required only for webpack@4
  248. const updatedAssetInfo = { ...info,
  249. related: { ...info.related,
  250. sourceMap: null
  251. }
  252. };
  253. CompressionPlugin.updateAsset(compilation, name, inputSource, updatedAssetInfo);
  254. }
  255. CompressionPlugin.deleteAsset(compilation, name);
  256. } else {
  257. // TODO `...` required only for webpack@4
  258. const newOriginalInfo = { ...info,
  259. related: {
  260. [relatedName]: newName,
  261. ...info.related
  262. }
  263. };
  264. CompressionPlugin.updateAsset(compilation, name, inputSource, newOriginalInfo);
  265. }
  266. CompressionPlugin.emitAsset(compilation, newName, output, newInfo);
  267. })());
  268. }
  269. return Promise.all(scheduledTasks);
  270. }
  271. static isWebpack4() {
  272. return _webpack.version[0] === "4";
  273. }
  274. apply(compiler) {
  275. const pluginName = this.constructor.name;
  276. if (CompressionPlugin.isWebpack4()) {
  277. // eslint-disable-next-line global-require
  278. const CacheEngine = require("./Webpack4Cache").default;
  279. const weakCache = new WeakMap();
  280. compiler.hooks.emit.tapPromise({
  281. name: pluginName
  282. }, compilation => // eslint-disable-next-line no-undefined
  283. this.compress(compilation, undefined, CacheEngine, weakCache));
  284. } else {
  285. // eslint-disable-next-line global-require
  286. const CacheEngine = require("./Webpack5Cache").default;
  287. compiler.hooks.thisCompilation.tap(pluginName, compilation => {
  288. // eslint-disable-next-line global-require
  289. const Compilation = require("webpack/lib/Compilation");
  290. compilation.hooks.processAssets.tapPromise({
  291. name: pluginName,
  292. stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER
  293. }, assets => this.compress(compilation, assets, CacheEngine));
  294. compilation.hooks.statsPrinter.tap(pluginName, stats => {
  295. stats.hooks.print.for("asset.info.compressed").tap("compression-webpack-plugin", (compressed, {
  296. green,
  297. formatFlag
  298. }) => // eslint-disable-next-line no-undefined
  299. compressed ? green(formatFlag("compressed")) : undefined);
  300. });
  301. });
  302. }
  303. }
  304. }
  305. var _default = CompressionPlugin;
  306. exports.default = _default;