Repository: webpack-contrib/compression-webpack-plugin (js)

Issue:
Allow compressed assets to overwrite original assets with same filename
### Feature Proposal

Until the version 5.x.x was released, it was possible to set `filename` option to just `[path]` and it would overwrite the original assets with the compressed ones, keeping the same name.

Since the version 5.x.x this is not possible as you get a **webpack** error: `WARNING in Conflict: Multiple assets emit different content to the same filename XXX.X`.

It would be nice to have this ability again! This could be done by using a new config like `overwriteAssets: true`; or setting the `filename: [path]` and add the `deleteOriginalAssets: true` (today if you do that you end up with no assets in the dist dir); or even just allowing internally to _automagically_ replace the assets if the names collide, as it was before. 
This was discussed in this [issue](https://github.com/webpack-contrib/compression-webpack-plugin/issues/187).

### Feature Use Case

This is useful to test website dist locally in conjunction with the HtmlWebpackPlugin, as the plugin injects the references to the original assets, not the `.gzip` or `.br` versions of them.

Also, when building to distribute thru dumb CDNs like S3, if you files already have the right filename, you can upload them as they are, just adding the proper `Content-Enconding` header, without having to rename the file.

Thanks!

Pull request description:
feat: added `overwriteOriginalAssets` option
<!--
  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:

- [ ] **bugfix**
- [x] new **feature**
- [ ] **code refactor**
- [x] **test update** <!-- if bug or feature is checked, this should be too -->
- [ ] **typo fix**
- [x] **metadata update**

### Motivation / Use-Case

Fixes #211 

### Breaking Changes

No

### Additional Info

No


Patch:
diff --git a/src/index.js b/src/index.js
index 8363dd9..5c8f052 100644
--- a/src/index.js
+++ b/src/index.js
@@ -298,8 +298,6 @@ class CompressionPlugin {
             newInfo.immutable = true;
           }
 
-          CompressionPlugin.emitAsset(compilation, newName, output, newInfo);
-
           if (this.options.deleteOriginalAssets) {
             // eslint-disable-next-line no-param-reassign
             CompressionPlugin.deleteAsset(compilation, name);
@@ -317,6 +315,8 @@ class CompressionPlugin {
               newOriginalInfo
             );
           }
+
+          CompressionPlugin.emitAsset(compilation, newName, output, newInfo);
         })()
       );
     }


Test patch:
diff --git a/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack4 b/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack4
index 8f0ebf2..b822eab 100644
--- a/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack4
+++ b/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack4
@@ -1,14 +1,60 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): assets 1`] = `
+exports[`"deleteOriginalAssets" option should work and delete original assets: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png.gz",
+    73160,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg.gz",
+    393,
+  ],
+  Array [
+    "async.async.dcbdb1dd40763448b635.js.gz",
+    171,
+  ],
+  Array [
+    "main.82c6786e32f9c77b44ef.js.gz",
+    3000,
+  ],
+]
+`;
+
+exports[`"deleteOriginalAssets" option should work and delete original assets: errors 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and delete original assets: warnings 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and do not report errors on duplicate assets when original assets were removed: assets 1`] = `
 Array [
   Array [
     "09a1a1112c577c2794359715edfcb5ac.png",
     78117,
   ],
   Array [
-    "09a1a1112c577c2794359715edfcb5ac.png.gz",
-    73160,
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    393,
+  ],
+  Array [
+    "async.async.dcbdb1dd40763448b635.js",
+    171,
+  ],
+  Array [
+    "main.82c6786e32f9c77b44ef.js",
+    3000,
+  ],
+]
+`;
+
+exports[`"deleteOriginalAssets" option should work and do not report errors on duplicate assets when original assets were removed: errors 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and do not report errors on duplicate assets when original assets were removed: warnings 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and keep original assets by default: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
   ],
   Array [
     "23fc1d3ac606d117e05a140e0de79806.svg",
@@ -37,24 +83,40 @@ Array [
 ]
 `;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): errors 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and keep original assets by default: errors 1`] = `Array []`;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): warnings 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and keep original assets by default: warnings 1`] = `Array []`;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): assets 1`] = `
+exports[`"deleteOriginalAssets" option should work and keep original assets: assets 1`] = `
 Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
   Array [
     "09a1a1112c577c2794359715edfcb5ac.png.gz",
     73160,
   ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    672,
+  ],
   Array [
     "23fc1d3ac606d117e05a140e0de79806.svg.gz",
     393,
   ],
+  Array [
+    "async.async.dcbdb1dd40763448b635.js",
+    249,
+  ],
   Array [
     "async.async.dcbdb1dd40763448b635.js.gz",
     171,
   ],
+  Array [
+    "main.82c6786e32f9c77b44ef.js",
+    11645,
+  ],
   Array [
     "main.82c6786e32f9c77b44ef.js.gz",
     3000,
@@ -62,6 +124,37 @@ Array [
 ]
 `;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): errors 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and keep original assets: errors 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and keep original assets: warnings 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and report errors on duplicate assets: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    393,
+  ],
+  Array [
+    "async.async.dcbdb1dd40763448b635.js",
+    171,
+  ],
+  Array [
+    "main.82c6786e32f9c77b44ef.js",
+    3000,
+  ],
+]
+`;
+
+exports[`"deleteOriginalAssets" option should work and report errors on duplicate assets: errors 1`] = `Array []`;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): warnings 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and report errors on duplicate assets: warnings 1`] = `
+Array [
+  "Error: Conflict: Multiple assets emit different content to the same filename 23fc1d3ac606d117e05a140e0de79806.svg",
+  "Error: Conflict: Multiple assets emit different content to the same filename async.async.dcbdb1dd40763448b635.js",
+  "Error: Conflict: Multiple assets emit different content to the same filename main.82c6786e32f9c77b44ef.js",
+]
+`;
diff --git a/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack5 b/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack5
index b1a28ee..4c2564c 100644
--- a/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack5
+++ b/test/__snapshots__/deleteOriginalAssets.test.js.snap.webpack5
@@ -1,14 +1,60 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): assets 1`] = `
+exports[`"deleteOriginalAssets" option should work and delete original assets: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png.gz",
+    73160,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg.gz",
+    393,
+  ],
+  Array [
+    "async.async.9325220d444b0d653c03.js.gz",
+    179,
+  ],
+  Array [
+    "main.7cdd8fe3ccdaedc3726c.js.gz",
+    3933,
+  ],
+]
+`;
+
+exports[`"deleteOriginalAssets" option should work and delete original assets: errors 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and delete original assets: warnings 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and do not report errors on duplicate assets when original assets were removed: assets 1`] = `
 Array [
   Array [
     "09a1a1112c577c2794359715edfcb5ac.png",
     78117,
   ],
   Array [
-    "09a1a1112c577c2794359715edfcb5ac.png.gz",
-    73160,
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    393,
+  ],
+  Array [
+    "async.async.9325220d444b0d653c03.js",
+    179,
+  ],
+  Array [
+    "main.7cdd8fe3ccdaedc3726c.js",
+    3933,
+  ],
+]
+`;
+
+exports[`"deleteOriginalAssets" option should work and do not report errors on duplicate assets when original assets were removed: errors 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and do not report errors on duplicate assets when original assets were removed: warnings 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and keep original assets by default: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
   ],
   Array [
     "23fc1d3ac606d117e05a140e0de79806.svg",
@@ -37,24 +83,40 @@ Array [
 ]
 `;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): errors 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and keep original assets by default: errors 1`] = `Array []`;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): warnings 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and keep original assets by default: warnings 1`] = `Array []`;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): assets 1`] = `
+exports[`"deleteOriginalAssets" option should work and keep original assets: assets 1`] = `
 Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
   Array [
     "09a1a1112c577c2794359715edfcb5ac.png.gz",
     73160,
   ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    672,
+  ],
   Array [
     "23fc1d3ac606d117e05a140e0de79806.svg.gz",
     393,
   ],
+  Array [
+    "async.async.9325220d444b0d653c03.js",
+    265,
+  ],
   Array [
     "async.async.9325220d444b0d653c03.js.gz",
     179,
   ],
+  Array [
+    "main.7cdd8fe3ccdaedc3726c.js",
+    16494,
+  ],
   Array [
     "main.7cdd8fe3ccdaedc3726c.js.gz",
     3933,
@@ -62,6 +124,37 @@ Array [
 ]
 `;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): errors 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and keep original assets: errors 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and keep original assets: warnings 1`] = `Array []`;
+
+exports[`"deleteOriginalAssets" option should work and report errors on duplicate assets: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    393,
+  ],
+  Array [
+    "async.async.9325220d444b0d653c03.js",
+    179,
+  ],
+  Array [
+    "main.7cdd8fe3ccdaedc3726c.js",
+    3933,
+  ],
+]
+`;
+
+exports[`"deleteOriginalAssets" option should work and report errors on duplicate assets: errors 1`] = `
+Array [
+  "Error: Conflict: Multiple assets emit different content to the same filename 23fc1d3ac606d117e05a140e0de79806.svg",
+  "Error: Conflict: Multiple assets emit different content to the same filename async.async.9325220d444b0d653c03.js",
+  "Error: Conflict: Multiple assets emit different content to the same filename main.7cdd8fe3ccdaedc3726c.js",
+]
+`;
 
-exports[`"deleteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): warnings 1`] = `Array []`;
+exports[`"deleteOriginalAssets" option should work and report errors on duplicate assets: warnings 1`] = `Array []`;
diff --git a/test/__snapshots__/overwriteOriginalAssets.test.js.snap.webpack4 b/test/__snapshots__/overwriteOriginalAssets.test.js.snap.webpack4
new file mode 100644
index 0000000..54fa1af
--- /dev/null
+++ b/test/__snapshots__/overwriteOriginalAssets.test.js.snap.webpack4
@@ -0,0 +1,88 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    672,
+  ],
+  Array [
+    "async.js",
+    249,
+  ],
+  Array [
+    "main.js",
+    11579,
+  ],
+]
+`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): errors 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): warnings 1`] = `
+Array [
+  "Error: Conflict: Multiple assets emit different content to the same filename 23fc1d3ac606d117e05a140e0de79806.svg",
+  "Error: Conflict: Multiple assets emit different content to the same filename async.js",
+  "Error: Conflict: Multiple assets emit different content to the same filename main.js",
+]
+`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    672,
+  ],
+  Array [
+    "async.js",
+    249,
+  ],
+  Array [
+    "main.js",
+    11579,
+  ],
+]
+`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): errors 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): warnings 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for not specify value: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    672,
+  ],
+  Array [
+    "async.js",
+    249,
+  ],
+  Array [
+    "main.js",
+    11579,
+  ],
+]
+`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for not specify value: errors 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for not specify value: warnings 1`] = `
+Array [
+  "Error: Conflict: Multiple assets emit different content to the same filename 23fc1d3ac606d117e05a140e0de79806.svg",
+  "Error: Conflict: Multiple assets emit different content to the same filename async.js",
+  "Error: Conflict: Multiple assets emit different content to the same filename main.js",
+]
+`;
diff --git a/test/__snapshots__/overwriteOriginalAssets.test.js.snap.webpack5 b/test/__snapshots__/overwriteOriginalAssets.test.js.snap.webpack5
new file mode 100644
index 0000000..a5516ba
--- /dev/null
+++ b/test/__snapshots__/overwriteOriginalAssets.test.js.snap.webpack5
@@ -0,0 +1,76 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    393,
+  ],
+  Array [
+    "async.js",
+    179,
+  ],
+  Array [
+    "main.js",
+    3906,
+  ],
+]
+`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): errors 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`false\` value ({Boolean}): warnings 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    393,
+  ],
+  Array [
+    "async.js",
+    179,
+  ],
+  Array [
+    "main.js",
+    3906,
+  ],
+]
+`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): errors 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for \`true\` value ({Boolean}): warnings 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for not specify value: assets 1`] = `
+Array [
+  Array [
+    "09a1a1112c577c2794359715edfcb5ac.png",
+    78117,
+  ],
+  Array [
+    "23fc1d3ac606d117e05a140e0de79806.svg",
+    393,
+  ],
+  Array [
+    "async.js",
+    179,
+  ],
+  Array [
+    "main.js",
+    3906,
+  ],
+]
+`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for not specify value: errors 1`] = `Array []`;
+
+exports[`"overwriteOriginalAssets" option matches snapshot for not specify value: warnings 1`] = `Array []`;
diff --git a/test/deleteOriginalAssets.test.js b/test/deleteOriginalAssets.test.js
index 5da1f5e..e0792e4 100644
--- a/test/deleteOriginalAssets.test.js
+++ b/test/deleteOriginalAssets.test.js
@@ -18,10 +18,22 @@ describe('"deleteOriginalAssets" option', () => {
     return removeCache();
   });
 
-  it('matches snapshot for `true` value ({Boolean})', async () => {
+  it('should work and keep original assets by default', async () => {
+    compiler = getCompiler('./entry.js');
+
+    new CompressionPlugin().apply(compiler);
+
+    const stats = await compile(compiler);
+
+    expect(getAssetsNameAndSize(stats)).toMatchSnapshot('assets');
+    expect(getWarnings(stats)).toMatchSnapshot('warnings');
+    expect(getErrors(stats)).toMatchSnapshot('errors');
+  });
+
+  it('should work and keep original assets', async () => {
     new CompressionPlugin({
       minRatio: 1,
-      deleteOriginalAssets: true,
+      deleteOriginalAssets: false,
     }).apply(compiler);
 
     const stats = await compile(compiler);
@@ -31,10 +43,39 @@ describe('"deleteOriginalAssets" option', () => {
     expect(getErrors(stats)).toMatchSnapshot('errors');
   });
 
-  it('matches snapshot for `false` value ({Boolean})', async () => {
+  it('should work and delete original assets', async () => {
     new CompressionPlugin({
       minRatio: 1,
-      deleteOriginalAssets: false,
+      deleteOriginalAssets: true,
+    }).apply(compiler);
+
+    const stats = await compile(compiler);
+
+    expect(getAssetsNameAndSize(stats)).toMatchSnapshot('assets');
+    expect(getWarnings(stats)).toMatchSnapshot('warnings');
+    expect(getErrors(stats)).toMatchSnapshot('errors');
+  });
+
+  it('should work and report errors on duplicate assets', async () => {
+    compiler = getCompiler('./entry.js');
+
+    new CompressionPlugin({
+      filename: '[path][base]',
+    }).apply(compiler);
+
+    const stats = await compile(compiler);
+
+    expect(getAssetsNameAndSize(stats)).toMatchSnapshot('assets');
+    expect(getWarnings(stats)).toMatchSnapshot('warnings');
+    expect(getErrors(stats)).toMatchSnapshot('errors');
+  });
+
+  it('should work and do not report errors on duplicate assets when original assets were removed', async () => {
+    compiler = getCompiler('./entry.js');
+
+    new CompressionPlugin({
+      filename: '[path][base]',
+      deleteOriginalAssets: true,
     }).apply(compiler);
 
     const stats = await compile(compiler);
diff --git a/test/helpers/getErrors.js b/test/helpers/getErrors.js
index 716fbbb..085bae5 100644
--- a/test/helpers/getErrors.js
+++ b/test/helpers/getErrors.js
@@ -1,5 +1,5 @@
 import normalizeErrors from './normalizeErrors';
 
 export default (stats) => {
-  return normalizeErrors(stats.compilation.errors);
+  return normalizeErrors(stats.compilation.errors).sort();
 };
diff --git a/test/helpers/getWarnings.js b/test/helpers/getWarnings.js
index c8a09d6..9e09c82 100644
--- a/test/helpers/getWarnings.js
+++ b/test/helpers/getWarnings.js
@@ -1,5 +1,5 @@
 import normalizeErrors from './normalizeErrors';
 
 export default (stats) => {
-  return normalizeErrors(stats.compilation.warnings);
+  return normalizeErrors(stats.compilation.warnings).sort();
 };