Repository: webpack-contrib/compression-webpack-plugin
Pull request #235 (2020-12-18 16:13:02)

Perf
<!--
  HOLY CRAP a Pull Request. We ❤️ those!

  If you remove or skip this template, you'll make the 🐼 sad and the mighty god
  of Github will appear and pile-drive the close button from a great height
  while making animal noises.

  Please place an x (no spaces!) in all [ ] that apply
-->

This PR contains a:

- [x] **bugfix**
- [ ] new **feature**
- [ ] **code refactor**
- [x] **test update** <!-- if bug or feature is checked, this should be too -->
- [ ] **typo fix**
- [ ] **metadata update**

### Motivation / Use-Case

perf

### Breaking Changes

No

### Additional Info

No


codecov[bot]: # [Codecov](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235?src=pr&el=h1) Report
> Merging [#235](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235?src=pr&el=desc) (bc4a537) into [master](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/commit/5d54128682b7e43432e95e6b05b6d9fa3d69fc4c?el=desc) (5d54128) will **decrease** coverage by `0.05%`.
> The diff coverage is `97.43%`.

[![Impacted file tree graph](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235/graphs/tree.svg?width=650&height=150&src=pr&token=wLGv0aBxEE)](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #235      +/-   ##
==========================================
- Coverage   97.24%   97.19%   -0.06%     
==========================================
  Files           2        2              
  Lines         109      107       -2     
  Branches       31       33       +2     
==========================================
- Hits          106      104       -2     
  Misses          3        3              
```


| [Impacted Files](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/index.js](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235/diff?src=pr&el=tree#diff-c3JjL2luZGV4Lmpz) | `97.16% <97.43%> (-0.06%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235?src=pr&el=footer). Last update [5d54128...bc4a537](https://codecov.io/gh/webpack-contrib/compression-webpack-plugin/pull/235?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/src/index.js b/src/index.js
index d70be1f..666e7b1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -104,86 +104,72 @@ class CompressionPlugin {
 
   async compress(compiler, compilation, assets) {
     const cache = compilation.getCache("CompressionWebpackPlugin");
-    const assetsForMinify = await Promise.all(
-      Object.keys(assets).reduce((accumulator, name) => {
-        const { info, source } = compilation.getAsset(name);
+    const assetsForMinify = (
+      await Promise.all(
+        Object.keys(assets).map(async (name) => {
+          const { info, source } = compilation.getAsset(name);
 
-        // Skip double minimize assets from child compilation
-        if (info.compressed) {
-          return accumulator;
-        }
-
-        if (
-          !compiler.webpack.ModuleFilenameHelpers.matchObject.bind(
-            // eslint-disable-next-line no-undefined
-            undefined,
-            this.options
-          )(name)
-        ) {
-          return accumulator;
-        }
-
-        let input = source.source();
+          if (info.compressed) {
+            return false;
+          }
 
-        if (!Buffer.isBuffer(input)) {
-          input = Buffer.from(input);
-        }
+          if (
+            !compiler.webpack.ModuleFilenameHelpers.matchObject.bind(
+              // eslint-disable-next-line no-undefined
+              undefined,
+              this.options
+            )(name)
+          ) {
+            return false;
+          }
 
-        if (input.length < this.options.threshold) {
-          return accumulator;
-        }
+          let relatedName;
 
-        let relatedName;
+          if (typeof this.options.algorithm === "function") {
+            let filenameForRelatedName = this.options.filename;
 
-        if (typeof this.options.algorithm === "function") {
-          let filenameForRelatedName = this.options.filename;
+            const index = filenameForRelatedName.indexOf("?");
 
-          const index = filenameForRelatedName.lastIndexOf("?");
+            if (index >= 0) {
+              filenameForRelatedName = filenameForRelatedName.substr(0, index);
+            }
 
-          if (index >= 0) {
-            filenameForRelatedName = filenameForRelatedName.substr(0, index);
+            relatedName = `${path.extname(filenameForRelatedName).slice(1)}ed`;
+          } else if (this.options.algorithm === "gzip") {
+            relatedName = "gzipped";
+          } else {
+            relatedName = `${this.options.algorithm}ed`;
           }
 
-          relatedName = `${path.extname(filenameForRelatedName).slice(1)}ed`;
-        } else if (this.options.algorithm === "gzip") {
-          relatedName = "gzipped";
-        } else {
-          relatedName = `${this.options.algorithm}ed`;
-        }
+          if (info.related && info.related[relatedName]) {
+            return false;
+          }
 
-        if (info.related && info.related[relatedName]) {
-          return accumulator;
-        }
+          const cacheItem = cache.getItemCache(
+            serialize({
+              name,
+              algorithm: this.options.algorithm,
+              compressionOptions: this.options.compressionOptions,
+            }),
+            cache.getLazyHashedEtag(source)
+          );
+          const output = (await cacheItem.getPromise()) || {};
 
-        const eTag = cache.getLazyHashedEtag(source);
-        const cacheItem = cache.getItemCache(
-          serialize({
-            name,
-            algorithm: this.options.algorithm,
-            compressionOptions: this.options.compressionOptions,
-          }),
-          eTag
-        );
+          let buffer;
 
-        accumulator.push(
-          (async () => {
-            const output = await cacheItem.getPromise();
+          // No need original buffer for cached files
+          if (!output.source) {
+            buffer = source.buffer();
 
-            return {
-              name,
-              inputSource: source,
-              info,
-              input,
-              output,
-              cacheItem,
-              relatedName,
-            };
-          })()
-        );
+            if (buffer.length < this.options.threshold) {
+              return false;
+            }
+          }
 
-        return accumulator;
-      }, [])
-    );
+          return { name, source, info, buffer, output, cacheItem, relatedName };
+        })
+      )
+    ).filter((assetForMinify) => Boolean(assetForMinify));
 
     const { RawSource } = compiler.webpack.sources;
     const scheduledTasks = [];
@@ -193,28 +179,37 @@ class CompressionPlugin {
         (async () => {
           const {
             name,
-            inputSource,
-            input,
+            source,
+            buffer,
+            output,
             cacheItem,
             info,
             relatedName,
           } = asset;
-          let { output } = asset;
 
-          if (!output) {
-            try {
-              output = new RawSource(await this.runCompressionAlgorithm(input));
-            } catch (error) {
-              compilation.errors.push(error);
+          if (!output.source) {
+            if (!output.compressed) {
+              try {
+                output.compressed = await this.runCompressionAlgorithm(buffer);
+              } catch (error) {
+                compilation.errors.push(error);
+
+                return;
+              }
+            }
+
+            if (
+              output.compressed.length / buffer.length >
+              this.options.minRatio
+            ) {
+              await cacheItem.storePromise({ compressed: output.compressed });
 
               return;
             }
 
-            await cacheItem.storePromise(output);
-          }
+            output.source = new RawSource(output.compressed);
 
-          if (output.source().length / input.length > this.options.minRatio) {
-            return;
+            await cacheItem.storePromise(output);
           }
 
           const match = /^([^?#]*)(\?[^#]*)?(#.*)?$/.exec(name);
@@ -260,19 +255,19 @@ class CompressionPlugin {
 
           if (this.options.deleteOriginalAssets) {
             if (this.options.deleteOriginalAssets === "keep-source-map") {
-              compilation.updateAsset(name, inputSource, {
+              compilation.updateAsset(name, source, {
                 related: { sourceMap: null },
               });
             }
 
             compilation.deleteAsset(name);
           } else {
-            compilation.updateAsset(name, inputSource, {
+            compilation.updateAsset(name, source, {
               related: { [relatedName]: newName },
             });
           }
 
-          compilation.emitAsset(newName, output, newInfo);
+          compilation.emitAsset(newName, output.source, newInfo);
         })()
       );
     }


Test patch:
diff --git a/test/CompressionPlugin.test.js b/test/CompressionPlugin.test.js
index b91c0f2..22f34ec 100644
--- a/test/CompressionPlugin.test.js
+++ b/test/CompressionPlugin.test.js
@@ -294,7 +294,56 @@ describe("CompressionPlugin", () => {
     expect(getWarnings(stats)).toMatchSnapshot("warnings");
     expect(getErrors(stats)).toMatchSnapshot("errors");
 
-    new ModifyExistingAsset({ name: "main.js" }).apply(compiler);
+    new ModifyExistingAsset({
+      name: "main.js",
+      content: "function changed() { /*! CHANGED */ }",
+    }).apply(compiler);
+
+    await new Promise(async (resolve) => {
+      const newStats = await compile(compiler);
+
+      expect(newStats.compilation.emittedAssets.size).toBe(2);
+
+      expect(getAssetsNameAndSize(newStats, compiler)).toMatchSnapshot(
+        "assets"
+      );
+      expect(getWarnings(newStats)).toMatchSnapshot("errors");
+      expect(getErrors(newStats)).toMatchSnapshot("warnings");
+
+      resolve();
+    });
+  });
+
+  it('should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option', async () => {
+    const compiler = getCompiler(
+      "./entry.js",
+      {
+        name: "[name].[ext]",
+      },
+      {
+        cache: true,
+        output: {
+          path: path.resolve(__dirname, "./outputs"),
+          filename: "[name].js",
+          chunkFilename: "[id].js",
+        },
+      }
+    );
+
+    new CompressionPlugin().apply(compiler);
+
+    const stats = await compile(compiler);
+
+    expect(stats.compilation.emittedAssets.size).toBe(7);
+
+    expect(getAssetsNameAndSize(stats, compiler)).toMatchSnapshot("assets");
+    expect(getWarnings(stats)).toMatchSnapshot("warnings");
+    expect(getErrors(stats)).toMatchSnapshot("errors");
+
+    new ModifyExistingAsset({
+      name: "icon.png",
+      content: `1q!Q2w@W3e#e4r$r`.repeat(1000),
+    }).apply(compiler);
 
     await new Promise(async (resolve) => {
       const newStats = await compile(compiler);
diff --git a/test/__snapshots__/CompressionPlugin.test.js.snap b/test/__snapshots__/CompressionPlugin.test.js.snap
index 29e4e3d..2b77922 100644
--- a/test/__snapshots__/CompressionPlugin.test.js.snap
+++ b/test/__snapshots__/CompressionPlugin.test.js.snap
@@ -354,6 +354,165 @@ exports[`CompressionPlugin should work and show compress assets in stats: errors
 
 exports[`CompressionPlugin should work and show compress assets in stats: warnings 1`] = `Array []`;
 
+exports[`CompressionPlugin should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option: assets 1`] = `
+Array [
+  Array [
+    "async.js",
+    194,
+    Object {
+      "javascriptModule": false,
+      "related": Object {
+        "gzipped": "async.js.gz",
+      },
+      "size": 194,
+    },
+  ],
+  Array [
+    "async.js.gz",
+    134,
+    Object {
+      "compressed": true,
+      "size": 134,
+    },
+  ],
+  Array [
+    "icon.png",
+    78117,
+    Object {
+      "size": 78117,
+      "sourceFilename": "icon.png",
+    },
+  ],
+  Array [
+    "icon.svg",
+    672,
+    Object {
+      "related": Object {
+        "gzipped": "icon.svg.gz",
+      },
+      "size": 672,
+      "sourceFilename": "icon.svg",
+    },
+  ],
+  Array [
+    "icon.svg.gz",
+    393,
+    Object {
+      "compressed": true,
+      "size": 393,
+    },
+  ],
+  Array [
+    "main.js",
+    15877,
+    Object {
+      "javascriptModule": false,
+      "related": Object {
+        "gzipped": "main.js.gz",
+      },
+      "size": 15877,
+    },
+  ],
+  Array [
+    "main.js.gz",
+    3886,
+    Object {
+      "compressed": true,
+      "size": 3886,
+    },
+  ],
+]
+`;
+
+exports[`CompressionPlugin should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option: assets 2`] = `
+Array [
+  Array [
+    "async.js",
+    194,
+    Object {
+      "javascriptModule": false,
+      "related": Object {
+        "gzipped": "async.js.gz",
+      },
+      "size": 194,
+    },
+  ],
+  Array [
+    "async.js.gz",
+    134,
+    Object {
+      "compressed": true,
+      "size": 134,
+    },
+  ],
+  Array [
+    "icon.png",
+    94117,
+    Object {
+      "related": Object {
+        "gzipped": "icon.png.gz",
+      },
+      "size": 94117,
+      "sourceFilename": "icon.png",
+    },
+  ],
+  Array [
+    "icon.png.gz",
+    73309,
+    Object {
+      "compressed": true,
+      "size": 73309,
+    },
+  ],
+  Array [
+    "icon.svg",
+    672,
+    Object {
+      "related": Object {
+        "gzipped": "icon.svg.gz",
+      },
+      "size": 672,
+      "sourceFilename": "icon.svg",
+    },
+  ],
+  Array [
+    "icon.svg.gz",
+    393,
+    Object {
+      "compressed": true,
+      "size": 393,
+    },
+  ],
+  Array [
+    "main.js",
+    15877,
+    Object {
+      "javascriptModule": false,
+      "related": Object {
+        "gzipped": "main.js.gz",
+      },
+      "size": 15877,
+    },
+  ],
+  Array [
+    "main.js.gz",
+    3886,
+    Object {
+      "compressed": true,
+      "size": 3886,
+    },
+  ],
+]
+`;
+
+exports[`CompressionPlugin should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option: errors 1`] = `Array []`;
+
+exports[`CompressionPlugin should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option: errors 2`] = `Array []`;
+
+exports[`CompressionPlugin should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option: warnings 1`] = `Array []`;
+
+exports[`CompressionPlugin should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option: warnings 2`] = `Array []`;
+
 exports[`CompressionPlugin should work and use memory cache when the "cache" option is "true" and the asset has been changed: assets 1`] = `
 Array [
   Array [
@@ -480,21 +639,21 @@ Array [
   ],
   Array [
     "main.js",
-    15955,
+    15970,
     Object {
       "javascriptModule": false,
       "related": Object {
         "gzipped": "main.js.gz",
       },
-      "size": 15955,
+      "size": 15970,
     },
   ],
   Array [
     "main.js.gz",
-    3960,
+    3972,
     Object {
       "compressed": true,
-      "size": 3960,
+      "size": 3972,
     },
   ],
 ]
diff --git a/test/helpers/ModifyExistingAsset.js b/test/helpers/ModifyExistingAsset.js
index 08104ed..9fd2064 100644
--- a/test/helpers/ModifyExistingAsset.js
+++ b/test/helpers/ModifyExistingAsset.js
@@ -5,15 +5,13 @@ export default class ExistingCommentsFile {
 
   apply(compiler) {
     const plugin = { name: this.constructor.name };
-    const { ConcatSource } = compiler.webpack.sources;
+    const { ConcatSource, RawSource } = compiler.webpack.sources;
 
     compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
       compilation.hooks.additionalAssets.tap(plugin, () => {
         // eslint-disable-next-line no-param-reassign
         compilation.assets[this.options.name] = new ConcatSource(
-          `function changed() {} ${
-            this.options.comment ? "/*! CHANGED */" : ""
-          }`,
+          new RawSource(this.options.content),
           compilation.assets[this.options.name]
         );
       });