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

Issue:
Legal, License, Copyright comments should not be removed by default
**Steps to reproduce:**

Version in `package.json`:
```json
    "grunt-webpack": "^3.0.2",
    "uglifyjs-webpack-plugin": "^1.1.8",
    "webpack": "^3.10.0",
    "webpack-bundle-analyzer": "^2.9.2"
```

My `webpack.config.js`:
```js
const path = require('path');
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
  .BundleAnalyzerPlugin;

const config = {
  // entry: ["babel-polyfill", "./app/js"]
  entry: {
    // Further entry points can be added here
    // NB: If you add further ones, then you probably also want to move
    // dependencies like jQuery into their own bundle
    // see https://webpack.js.org/guides/code-splitting/#prevent-duplication

    // the babel-polyfill and fetch polyfill needs to be loaded only once,
    // hence only added to app.js which is executed on every page
    app: ['babel-polyfill', 'whatwg-fetch', './src/components/app.js'],
    resizer: ['./src/prototype-assets/viewport-resizer/main.js'],
    fractalmenuenhancer: [
      './src/prototype-assets/fractal-menu-enhancer/main.js'
    ]
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  resolve: {
    extensions: ['*', '.js', '.jsx']
  },
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'public/js/')
  },
  plugins: [
    new webpack.EnvironmentPlugin({
      // pass 'development' unless process.env.NODE_ENV is defined to further tasks/loaders/babel
      NODE_ENV: 'development'
    })
  ]
};

// Extend config to use best, small build for prod and fast, big for dev
if (process.env.NODE_ENV === 'production') {
  config.devtool = 'source-map';
  config.plugins.push(
    new UglifyJSPlugin({
      sourceMap: true
    })
  );
  config.plugins.push(
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',
      reportFilename: '../webpack-bundle-size-analyser.html',
      openAnalyzer: false
    })
  );
} else {
  config.devtool = 'eval-source-map';
}

module.exports = config;
```

Now in the `./src/components/app.js` there are several 'vendor' libraries imported like: `import $ from 'jquery';`

**Actual results:**
When running `npm run build` which creates a uglified minimal bundle, then *all* comments are stripped.

For instance, `jquery` has this header which is not present any more in the uglified bundle:
```
/*!
 * jQuery JavaScript Library v3.3.1
 * https://jquery.com/
 *
 * Includes Sizzle.js
 * https://sizzlejs.com/
 *
 * Copyright JS Foundation and other contributors
 * Released under the MIT license
 * https://jquery.org/license
 *
 * Date: 2018-01-20T17:24Z
 */
``` 

**Expected results:**
Legal copyright comments should not be stripped by default. The comment at https://github.com/webpack/webpack/issues/324#issuecomment-157855568 points out, that

> it's intended that webpack preserves some comments. This is for legal reasons. By default comments with @license, @preserve or starting with /*! are preserved

At the moment this is not the case: By default *all* comments are stripped at the moment.

Pull request description:
fix(index): don't remove legal comments by default (`options.extractComments`)
### `Issues`

- Fixes #222

Patch:
diff --git a/src/index.js b/src/index.js
index aa4b237..145a83c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -21,7 +21,7 @@ class UglifyJsPlugin {
 
     const {
       uglifyOptions = {},
-      test = /\.(js?)(\?.+)?$/i,
+      test = /\.js(\?.*)?$/i,
       warningsFilter = () => true,
       extractComments = false,
       sourceMap = false,
@@ -42,7 +42,7 @@ class UglifyJsPlugin {
       exclude,
       uglifyOptions: {
         output: {
-          comments: false,
+          comments: extractComments ? false : /^\**!|@preserve|@license|@cc_on/,
         },
         ...uglifyOptions,
       },


Test patch:
diff --git a/test/__snapshots__/extract-comments-options.test.js.snap b/test/__snapshots__/extract-comments-options.test.js.snap
index aa83de0..d7031d0 100644
--- a/test/__snapshots__/extract-comments-options.test.js.snap
+++ b/test/__snapshots__/extract-comments-options.test.js.snap
@@ -1,107 +1,130 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`errors 1`] = `Array []`;
+exports[`"options.extractComments" is "boolean" - "true": errors 1`] = `Array []`;
 
-exports[`errors 2`] = `Array []`;
+exports[`"options.extractComments" is "boolean" - "true": test.js 1`] = `"var foo=1;"`;
 
-exports[`errors 3`] = `Array []`;
-
-exports[`errors 4`] = `Array []`;
-
-exports[`errors 5`] = `Array []`;
-
-exports[`nested/nested/test1.js 1`] = `
+exports[`"options.extractComments" is "boolean" - "true": test1.js 1`] = `
 "/*! For license information please see test1.js.LICENSE */
 var foo=1;"
 `;
 
-exports[`nested/nested/test1.js.LICENSE 1`] = `
-"/* Comment */
+exports[`"options.extractComments" is "boolean" - "true": test1.js.LICENSE 1`] = `
+"/*! Legal Comment */
 "
 `;
 
-exports[`nested/test.js 1`] = `
+exports[`"options.extractComments" is "boolean" - "true": warnings 1`] = `Array []`;
+
+exports[`"options.extractComments" is "function": errors 1`] = `Array []`;
+
+exports[`"options.extractComments" is "function": test.js 1`] = `
 "/*! For license information please see test.js.LICENSE */
 var foo=1;"
 `;
 
-exports[`nested/test1.js.LICENSE 1`] = `
+exports[`"options.extractComments" is "function": test.js.LICENSE 1`] = `
 "// Comment
 "
 `;
 
-exports[`test.js 1`] = `"var foo=1;"`;
-
-exports[`test.js 2`] = `"var foo=1;"`;
-
-exports[`test.js 3`] = `
-"/*! For license information please see test.js.LICENSE */
+exports[`"options.extractComments" is "function": test1.js 1`] = `
+"/*! For license information please see test1.js.LICENSE */
 var foo=1;"
 `;
 
-exports[`test.js 4`] = `
-"/*! For license information please see test.js.LICENSE */
-var foo=1;"
+exports[`"options.extractComments" is "function": test1.js.LICENSE 1`] = `
+"/* Comment */
+"
 `;
 
-exports[`test.js 5`] = `
-"/*! For license information please see test1.js.LICENSE */
-var foo=1;"
-`;
+exports[`"options.extractComments" is "function": warnings 1`] = `Array []`;
+
+exports[`"options.extractComments" is "object": errors 1`] = `Array []`;
 
-exports[`test.js 6`] = `
+exports[`"options.extractComments" is "object": test.js 1`] = `
 "/*! License information can be found in test.license.js */
 var foo=1;"
 `;
 
-exports[`test.js.LICENSE 1`] = `
+exports[`"options.extractComments" is "object": test.license.js 1`] = `
 "// Comment
 "
 `;
 
-exports[`test.js.LICENSE 2`] = `
-"// Comment
-"
+exports[`"options.extractComments" is "object": warnings 1`] = `Array []`;
+
+exports[`"options.extractComments" is "regex": errors 1`] = `Array []`;
+
+exports[`"options.extractComments" is "regex": test.js 1`] = `"var foo=1;"`;
+
+exports[`"options.extractComments" is "regex": test1.js 1`] = `
+"/*! For license information please see test1.js.LICENSE */
+var foo=1;"
 `;
 
-exports[`test.license.js 1`] = `
-"// Comment
+exports[`"options.extractComments" is "regex": test1.js.LICENSE 1`] = `
+"// foo
 "
 `;
 
-exports[`test1.js 1`] = `"var foo=1;"`;
+exports[`"options.extractComments" is "regex": warnings 1`] = `Array []`;
 
-exports[`test1.js 2`] = `
+exports[`"options.extractComments" is "string" - "all" and license file should be relative source file: errors 1`] = `Array []`;
+
+exports[`"options.extractComments" is "string" - "all" and license file should be relative source file: nested/nested/test1.js 1`] = `
 "/*! For license information please see test1.js.LICENSE */
 var foo=1;"
 `;
 
-exports[`test1.js 3`] = `
-"/*! For license information please see test1.js.LICENSE */
+exports[`"options.extractComments" is "string" - "all" and license file should be relative source file: nested/nested/test1.js.LICENSE 1`] = `
+"/* Comment */
+"
+`;
+
+exports[`"options.extractComments" is "string" - "all" and license file should be relative source file: nested/test.js 1`] = `
+"/*! For license information please see test.js.LICENSE */
 var foo=1;"
 `;
 
-exports[`test1.js.LICENSE 1`] = `
-"// foo
+exports[`"options.extractComments" is "string" - "all" and license file should be relative source file: nested/test.js.LICENSE 1`] = `
+"// Comment
 "
 `;
 
-exports[`test1.js.LICENSE 2`] = `
-"/* Comment */
+exports[`"options.extractComments" is "string" - "all" and license file should be relative source file: warnings 1`] = `Array []`;
+
+exports[`"options.extractComments" is "string": errors 1`] = `Array []`;
+
+exports[`"options.extractComments" is "string": test.js 1`] = `
+"/*! For license information please see test.js.LICENSE */
+var foo=1;"
+`;
+
+exports[`"options.extractComments" is "string": test.js.LICENSE 1`] = `
+"// Comment
 "
 `;
 
-exports[`test1.js.LICENSE 3`] = `
+exports[`"options.extractComments" is "string": test1.js 1`] = `
+"/*! For license information please see test1.js.LICENSE */
+var foo=1;"
+`;
+
+exports[`"options.extractComments" is "string": test1.js.LICENSE 1`] = `
 "/* Comment */
 "
 `;
 
-exports[`warnings 1`] = `Array []`;
+exports[`"options.extractComments" is "string": warnings 1`] = `Array []`;
 
-exports[`warnings 2`] = `Array []`;
+exports[`"options.extractComments" is not specify: errors 1`] = `Array []`;
 
-exports[`warnings 3`] = `Array []`;
+exports[`"options.extractComments" is not specify: test.js 1`] = `"var foo=1;"`;
 
-exports[`warnings 4`] = `Array []`;
+exports[`"options.extractComments" is not specify: test1.js 1`] = `
+"/*! Legal Comment */
+var foo=1;"
+`;
 
-exports[`warnings 5`] = `Array []`;
+exports[`"options.extractComments" is not specify: warnings 1`] = `Array []`;
diff --git a/test/__snapshots__/invalid-options.test.js.snap b/test/__snapshots__/invalid-options.test.js.snap
index 2dfff78..d6889a9 100644
--- a/test/__snapshots__/invalid-options.test.js.snap
+++ b/test/__snapshots__/invalid-options.test.js.snap
@@ -187,7 +187,7 @@ exports[`warnings 1`] = `Array []`;
 exports[`when applied with invalid options throws validation errors 1`] = `
 "UglifyJs Plugin Invalid Options
 
-options['doesntExist'] is an invalid additional property
+options['doesntExist'] should NOT have additional properties
 "
 `;
 
diff --git a/test/extract-comments-options.test.js b/test/extract-comments-options.test.js
index 2dfa09d..7fb6b02 100644
--- a/test/extract-comments-options.test.js
+++ b/test/extract-comments-options.test.js
@@ -2,10 +2,7 @@ import UglifyJsPlugin from '../src/index';
 import { PluginEnvironment } from './helpers';
 
 describe('when options.extractComments', () => {
-  let compilation;
-  let compilationEventBinding;
-
-  beforeEach(() => {
+  it('normalizes extractConmments', () => {
     const pluginEnvironment = new PluginEnvironment();
     const compilerEnv = pluginEnvironment.getEnvironmentStub();
     compilerEnv.context = '';
@@ -26,7 +23,7 @@ describe('when options.extractComments', () => {
     const [eventBinding] = pluginEnvironment.getEventBindings();
     const chunkPluginEnvironment = new PluginEnvironment();
 
-    compilation = chunkPluginEnvironment.getEnvironmentStub();
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
     compilation.assets = {
       'test.js': {
         source: () => 'var foo = 1;',
@@ -47,10 +44,8 @@ describe('when options.extractComments', () => {
     compilation.errors = [];
 
     eventBinding.handler(compilation);
-    [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
-  });
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
 
-  it('normalizes extractConmments', () => {
     compilationEventBinding.handler([{
       files: ['test.js'],
     }], () => {
@@ -59,6 +54,49 @@ describe('when options.extractComments', () => {
   });
 
   it('outputs warnings for unreachable code', () => {
+    const pluginEnvironment = new PluginEnvironment();
+    const compilerEnv = pluginEnvironment.getEnvironmentStub();
+    compilerEnv.context = '';
+
+    const plugin = new UglifyJsPlugin({
+      uglifyOptions: {
+        warnings: true,
+        mangle: {
+          properties: {
+            builtins: true,
+          },
+        },
+      },
+      extractComments: true,
+    });
+    plugin.apply(compilerEnv);
+
+    const [eventBinding] = pluginEnvironment.getEventBindings();
+    const chunkPluginEnvironment = new PluginEnvironment();
+
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
+      'test.js': {
+        source: () => 'var foo = 1;',
+      },
+      'test1.js': {
+        source: () => 'function foo(x) { if (x) { return bar(); not_called1(); } }',
+        map: () => {
+          return {
+            version: 3,
+            sources: ['test1.js'],
+            names: ['foo', 'x', 'bar', 'not_called1'],
+            mappings: 'AAAA,QAASA,KAAIC,GACT,GAAIA,EAAG,CACH,MAAOC,MACPC',
+          };
+        },
+      },
+    };
+    compilation.warnings = [];
+    compilation.errors = [];
+
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+
     compilationEventBinding.handler([{
       files: ['test.js', 'test1.js'],
     }], () => {
@@ -68,7 +106,44 @@ describe('when options.extractComments', () => {
     });
   });
 
-  it('normalizes when options.extractComments is boolean', () => {
+  it('normalizes when options.extractComments is not specify', () => {
+    const pluginEnvironment = new PluginEnvironment();
+    const compilerEnv = pluginEnvironment.getEnvironmentStub();
+    compilerEnv.context = '';
+
+    const plugin = new UglifyJsPlugin();
+    plugin.apply(compilerEnv);
+    const [eventBinding] = pluginEnvironment.getEventBindings();
+    const chunkPluginEnvironment = new PluginEnvironment();
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
+      'test.js': {
+        source: () => '// Comment\nvar foo = 1;',
+      },
+      'test1.js': {
+        source: () => '/*! Legal Comment */\nvar foo = 1;',
+      },
+    };
+    compilation.warnings = [];
+    compilation.errors = [];
+
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+
+    compilationEventBinding.handler([{
+      files: ['test.js', 'test1.js'],
+    }], () => {
+      for (const file in compilation.assets) {
+        if (Object.prototype.hasOwnProperty.call(compilation.assets, file)) {
+          expect(compilation.assets[file].source()).toMatchSnapshot(`"options.extractComments" is not specify: ${file}`);
+        }
+      }
+      expect(compilation.errors).toMatchSnapshot('"options.extractComments" is not specify: errors');
+      expect(compilation.warnings).toMatchSnapshot('"options.extractComments" is not specify: warnings');
+    });
+  });
+
+  it('normalizes when options.extractComments is boolean - "true"', () => {
     const pluginEnvironment = new PluginEnvironment();
     const compilerEnv = pluginEnvironment.getEnvironmentStub();
     compilerEnv.context = '';
@@ -79,29 +154,31 @@ describe('when options.extractComments', () => {
     plugin.apply(compilerEnv);
     const [eventBinding] = pluginEnvironment.getEventBindings();
     const chunkPluginEnvironment = new PluginEnvironment();
-    const compilation2 = chunkPluginEnvironment.getEnvironmentStub();
-    compilation2.assets = {
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
       'test.js': {
         source: () => '// Comment\nvar foo = 1;',
       },
       'test1.js': {
-        source: () => '/* Comment */\nvar foo = 1;',
+        source: () => '/*! Legal Comment */\nvar foo = 1;',
       },
     };
-    compilation2.warnings = [];
-    compilation2.errors = [];
+    compilation.warnings = [];
+    compilation.errors = [];
 
-    eventBinding.handler(compilation2);
-    [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
 
     compilationEventBinding.handler([{
       files: ['test.js', 'test1.js'],
     }], () => {
-      expect(compilation2.assets['test.js'].source()).toMatchSnapshot('test.js');
-      expect(compilation2.assets['test1.js'].source()).toMatchSnapshot('test1.js');
-      expect(compilation2.assets['test1.js.LICENSE'].source()).toMatchSnapshot('test1.js.LICENSE');
-      expect(compilation2.errors).toMatchSnapshot('errors');
-      expect(compilation2.warnings).toMatchSnapshot('warnings');
+      for (const file in compilation.assets) {
+        if (Object.prototype.hasOwnProperty.call(compilation.assets, file)) {
+          expect(compilation.assets[file].source()).toMatchSnapshot(`"options.extractComments" is "boolean" - "true": ${file}`);
+        }
+      }
+      expect(compilation.errors).toMatchSnapshot('"options.extractComments" is "boolean" - "true": errors');
+      expect(compilation.warnings).toMatchSnapshot('"options.extractComments" is "boolean" - "true": warnings');
     });
   });
 
@@ -116,8 +193,8 @@ describe('when options.extractComments', () => {
     plugin.apply(compilerEnv);
     const [eventBinding] = pluginEnvironment.getEventBindings();
     const chunkPluginEnvironment = new PluginEnvironment();
-    const compilation2 = chunkPluginEnvironment.getEnvironmentStub();
-    compilation2.assets = {
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
       'test.js': {
         source: () => '// Comment\nvar foo = 1;',
       },
@@ -125,20 +202,22 @@ describe('when options.extractComments', () => {
         source: () => '// foo\nvar foo = 1;',
       },
     };
-    compilation2.warnings = [];
-    compilation2.errors = [];
+    compilation.warnings = [];
+    compilation.errors = [];
 
-    eventBinding.handler(compilation2);
-    [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
 
     compilationEventBinding.handler([{
       files: ['test.js', 'test1.js'],
     }], () => {
-      expect(compilation2.assets['test.js'].source()).toMatchSnapshot('test.js');
-      expect(compilation2.assets['test1.js'].source()).toMatchSnapshot('test1.js');
-      expect(compilation2.assets['test1.js.LICENSE'].source()).toMatchSnapshot('test1.js.LICENSE');
-      expect(compilation2.errors).toMatchSnapshot('errors');
-      expect(compilation2.warnings).toMatchSnapshot('warnings');
+      for (const file in compilation.assets) {
+        if (Object.prototype.hasOwnProperty.call(compilation.assets, file)) {
+          expect(compilation.assets[file].source()).toMatchSnapshot(`"options.extractComments" is "regex": ${file}`);
+        }
+      }
+      expect(compilation.errors).toMatchSnapshot('"options.extractComments" is "regex": errors');
+      expect(compilation.warnings).toMatchSnapshot('"options.extractComments" is "regex": warnings');
     });
   });
 
@@ -153,8 +232,8 @@ describe('when options.extractComments', () => {
     plugin.apply(compilerEnv);
     const [eventBinding] = pluginEnvironment.getEventBindings();
     const chunkPluginEnvironment = new PluginEnvironment();
-    const compilation2 = chunkPluginEnvironment.getEnvironmentStub();
-    compilation2.assets = {
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
       'test.js': {
         source: () => '// Comment\nvar foo = 1;',
       },
@@ -162,21 +241,22 @@ describe('when options.extractComments', () => {
         source: () => '/* Comment */\nvar foo = 1;',
       },
     };
-    compilation2.warnings = [];
-    compilation2.errors = [];
+    compilation.warnings = [];
+    compilation.errors = [];
 
-    eventBinding.handler(compilation2);
-    [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
 
     compilationEventBinding.handler([{
       files: ['test.js', 'test1.js'],
     }], () => {
-      expect(compilation2.assets['test.js'].source()).toMatchSnapshot('test.js');
-      expect(compilation2.assets['test.js.LICENSE'].source()).toMatchSnapshot('test.js.LICENSE');
-      expect(compilation2.assets['test1.js'].source()).toMatchSnapshot('test1.js');
-      expect(compilation2.assets['test1.js.LICENSE'].source()).toMatchSnapshot('test1.js.LICENSE');
-      expect(compilation2.errors).toMatchSnapshot('errors');
-      expect(compilation2.warnings).toMatchSnapshot('warnings');
+      for (const file in compilation.assets) {
+        if (Object.prototype.hasOwnProperty.call(compilation.assets, file)) {
+          expect(compilation.assets[file].source()).toMatchSnapshot(`"options.extractComments" is "string": ${file}`);
+        }
+      }
+      expect(compilation.errors).toMatchSnapshot('"options.extractComments" is "string": errors');
+      expect(compilation.warnings).toMatchSnapshot('"options.extractComments" is "string": warnings');
     });
   });
 
@@ -191,8 +271,8 @@ describe('when options.extractComments', () => {
     plugin.apply(compilerEnv);
     const [eventBinding] = pluginEnvironment.getEventBindings();
     const chunkPluginEnvironment = new PluginEnvironment();
-    const compilation2 = chunkPluginEnvironment.getEnvironmentStub();
-    compilation2.assets = {
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
       'test.js': {
         source: () => '// Comment\nvar foo = 1;',
       },
@@ -200,21 +280,22 @@ describe('when options.extractComments', () => {
         source: () => '/* Comment */\nvar foo = 1;',
       },
     };
-    compilation2.warnings = [];
-    compilation2.errors = [];
+    compilation.warnings = [];
+    compilation.errors = [];
 
-    eventBinding.handler(compilation2);
-    [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
 
     compilationEventBinding.handler([{
       files: ['test.js', 'test1.js'],
     }], () => {
-      expect(compilation2.assets['test.js'].source()).toMatchSnapshot('test.js');
-      expect(compilation2.assets['test1.js'].source()).toMatchSnapshot('test.js');
-      expect(compilation2.assets['test.js.LICENSE'].source()).toMatchSnapshot('test.js.LICENSE');
-      expect(compilation2.assets['test1.js.LICENSE'].source()).toMatchSnapshot('test1.js.LICENSE');
-      expect(compilation2.errors).toMatchSnapshot('errors');
-      expect(compilation2.warnings).toMatchSnapshot('warnings');
+      for (const file in compilation.assets) {
+        if (Object.prototype.hasOwnProperty.call(compilation.assets, file)) {
+          expect(compilation.assets[file].source()).toMatchSnapshot(`"options.extractComments" is "function": ${file}`);
+        }
+      }
+      expect(compilation.errors).toMatchSnapshot('"options.extractComments" is "function": errors');
+      expect(compilation.warnings).toMatchSnapshot('"options.extractComments" is "function": warnings');
     });
   });
 
@@ -237,29 +318,32 @@ describe('when options.extractComments', () => {
     plugin.apply(compilerEnv);
     const [eventBinding] = pluginEnvironment.getEventBindings();
     const chunkPluginEnvironment = new PluginEnvironment();
-    const compilation2 = chunkPluginEnvironment.getEnvironmentStub();
-    compilation2.assets = {
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
       'test.js': {
         source: () => '// Comment\nvar foo = 1;',
       },
     };
-    compilation2.warnings = [];
-    compilation2.errors = [];
+    compilation.warnings = [];
+    compilation.errors = [];
 
-    eventBinding.handler(compilation2);
-    [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
 
     compilationEventBinding.handler([{
       files: ['test.js'],
     }], () => {
-      expect(compilation2.assets['test.js'].source()).toMatchSnapshot('test.js');
-      expect(compilation2.assets['test.license.js'].source()).toMatchSnapshot('test.license.js');
-      expect(compilation2.errors).toMatchSnapshot('errors');
-      expect(compilation2.warnings).toMatchSnapshot('warnings');
+      for (const file in compilation.assets) {
+        if (Object.prototype.hasOwnProperty.call(compilation.assets, file)) {
+          expect(compilation.assets[file].source()).toMatchSnapshot(`"options.extractComments" is "object": ${file}`);
+        }
+      }
+      expect(compilation.errors).toMatchSnapshot('"options.extractComments" is "object": errors');
+      expect(compilation.warnings).toMatchSnapshot('"options.extractComments" is "object": warnings');
     });
   });
 
-  it('output respect nested directories', () => {
+  it('normalizes when options.extractComments is string - "all" && license file should be relative source file', () => {
     const pluginEnvironment = new PluginEnvironment();
     const compilerEnv = pluginEnvironment.getEnvironmentStub();
     compilerEnv.context = '';
@@ -270,8 +354,8 @@ describe('when options.extractComments', () => {
     plugin.apply(compilerEnv);
     const [eventBinding] = pluginEnvironment.getEventBindings();
     const chunkPluginEnvironment = new PluginEnvironment();
-    const compilation2 = chunkPluginEnvironment.getEnvironmentStub();
-    compilation2.assets = {
+    const compilation = chunkPluginEnvironment.getEnvironmentStub();
+    compilation.assets = {
       'nested/test.js': {
         source: () => '// Comment\nvar foo = 1;',
       },
@@ -279,21 +363,22 @@ describe('when options.extractComments', () => {
         source: () => '/* Comment */\nvar foo = 1;',
       },
     };
-    compilation2.warnings = [];
-    compilation2.errors = [];
+    compilation.warnings = [];
+    compilation.errors = [];
 
-    eventBinding.handler(compilation2);
-    [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
+    eventBinding.handler(compilation);
+    const [compilationEventBinding] = chunkPluginEnvironment.getEventBindings();
 
     compilationEventBinding.handler([{
       files: ['nested/test.js', 'nested/nested/test1.js'],
     }], () => {
-      expect(compilation2.assets['nested/test.js'].source()).toMatchSnapshot('nested/test.js');
-      expect(compilation2.assets['nested/test.js.LICENSE'].source()).toMatchSnapshot('nested/test1.js.LICENSE');
-      expect(compilation2.assets['nested/nested/test1.js'].source()).toMatchSnapshot('nested/nested/test1.js');
-      expect(compilation2.assets['nested/nested/test1.js.LICENSE'].source()).toMatchSnapshot('nested/nested/test1.js.LICENSE');
-      expect(compilation2.errors).toMatchSnapshot('errors');
-      expect(compilation2.warnings).toMatchSnapshot('warnings');
+      for (const file in compilation.assets) {
+        if (Object.prototype.hasOwnProperty.call(compilation.assets, file)) {
+          expect(compilation.assets[file].source()).toMatchSnapshot(`"options.extractComments" is "string" - "all" and license file should be relative source file: ${file}`);
+        }
+      }
+      expect(compilation.errors).toMatchSnapshot('"options.extractComments" is "string" - "all" and license file should be relative source file: errors');
+      expect(compilation.warnings).toMatchSnapshot('"options.extractComments" is "string" - "all" and license file should be relative source file: warnings');
     });
   });
 });