index.js 9.7 KB

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