diff --git a/src/index.js b/src/index.js index 7c9d7e98..4834978c 100644 --- a/src/index.js +++ b/src/index.js @@ -123,18 +123,16 @@ class UglifyJsPlugin { let input; let inputSourceMap; - if (this.options.sourceMap) { - if (asset.sourceAndMap) { - const sourceAndMap = asset.sourceAndMap(); - inputSourceMap = sourceAndMap.map; - input = sourceAndMap.source; - } else { - inputSourceMap = asset.map(); - input = asset.source(); - } + if (this.options.sourceMap && asset.sourceAndMap) { + const { source, map } = asset.sourceAndMap(); + + input = source; + inputSourceMap = map; + sourceMap = new SourceMapConsumer(inputSourceMap); } else { input = asset.source(); + inputSourceMap = null; } // Handling comment extraction diff --git a/test/__snapshots__/source-map-options.test.js.snap b/test/__snapshots__/source-map-options.test.js.snap new file mode 100644 index 00000000..508996c2 --- /dev/null +++ b/test/__snapshots__/source-map-options.test.js.snap @@ -0,0 +1,539 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`source and parallel: asset main.0c220ec66316af2c1b24.js 1`] = `"webpackJsonp([0],[function(o,n){o.exports=function(){console.log(7)}}],[0]);"`; + +exports[`source and parallel: asset manifest.6afe1bc6685e9ab36c1c.js 1`] = `"!function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,a){for(var i,u,f,l=0,s=[];l { + const assets = { + 'test.js': { + source: () => 'function test(foo) { foo = 1; }', + }, + 'test1.js': { + source: () => 'function test1(foo) { foo = 1; }', + }, + 'test2.js': { + source: () => 'function test2(foo) { foo = 1; }', + }, + 'test3.js': { + source: () => 'function test3(foo) { foo = 1; }', + }, + }; + + describe('true', () => { + let eventBindings; + let eventBinding; + + beforeEach(() => { + const pluginEnvironment = new PluginEnvironment(); + const compilerEnv = pluginEnvironment.getEnvironmentStub(); + compilerEnv.context = ''; + compilerEnv.devtool = 'source-map'; + + const plugin = new UglifyJsPlugin({ + sourceMap: true, + }); + plugin.apply(compilerEnv); + eventBindings = pluginEnvironment.getEventBindings(); + }); + + it('binds one event handler', () => { + expect(eventBindings.length).toBe(1); + }); + + describe('compilation handler', () => { + beforeEach(() => { + [eventBinding] = eventBindings; + }); + + it('binds to compilation event', () => { + expect(eventBinding.name).toBe('compilation'); + }); + + describe('when called', () => { + let chunkPluginEnvironment; + let compilationEventBindings; + let compilationEventBinding; + let compilation; + let callback; + + beforeEach(() => { + chunkPluginEnvironment = new PluginEnvironment(); + compilation = chunkPluginEnvironment.getEnvironmentStub(); + compilation.assets = assets; + compilation.errors = []; + + eventBinding.handler(compilation); + compilationEventBindings = chunkPluginEnvironment.getEventBindings(); + }); + + it('binds two event handler', () => { + expect(compilationEventBindings[0].name).toBe('build-module'); + expect(compilationEventBindings[1].name).toBe('optimize-chunk-assets'); + }); + + describe('build-module handler', () => { + beforeEach(() => { + [compilationEventBinding] = compilationEventBindings; + }); + + it('binds to build-module event', () => { + expect(compilationEventBinding.name).toEqual('build-module'); + }); + + it('build-module handler', (done) => { + const moduleArgs = { useSourceMap: false }; + const mockBuildModuleEvent = jest.fn(() => compilationEventBinding.handler(moduleArgs)); + + mockBuildModuleEvent(); + + expect(mockBuildModuleEvent.mock.calls.length).toBe(1); + expect(moduleArgs.useSourceMap).toBe(true); + done(); + }); + }); + + describe('optimize-chunk-assets handler', () => { + beforeEach(() => { + [, compilationEventBinding] = compilationEventBindings; + }); + + it('binds to optimize-chunk-assets event', () => { + expect(compilationEventBinding.name).toEqual('optimize-chunk-assets'); + }); + + it('only calls callback once', (done) => { + callback = jest.fn(); + compilationEventBinding.handler([{ + files: ['test.js', 'test1.js', 'test2.js', 'test3.js'], + }], () => { + callback(); + expect(callback.mock.calls.length).toBe(1); + done(); + }); + }); + }); + }); + }); + + it('matches snapshot', () => { + const compiler = createCompiler(); + new UglifyJsPlugin({ sourceMap: true }).apply(compiler); + + return compile(compiler).then((stats) => { + const errors = stats.compilation.errors.map(cleanErrorStack); + const warnings = stats.compilation.warnings.map(cleanErrorStack); + + expect(errors).toMatchSnapshot('source map: errors'); + expect(warnings).toMatchSnapshot('source map: warnings'); + + for (const file in stats.compilation.assets) { + if (Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)) { + const asset = stats.compilation.assets[file].sourceAndMap(); + + asset.map.sources = []; + + expect(asset.source).toMatchSnapshot(`source: asset ${file}`); + expect(asset.map).toMatchSnapshot(`source map: asset ${file}`); + } + } + }); + }); + }); + + describe('true and options.parallel true', () => { + let eventBindings; + let eventBinding; + + beforeEach(() => { + const pluginEnvironment = new PluginEnvironment(); + const compilerEnv = pluginEnvironment.getEnvironmentStub(); + compilerEnv.context = ''; + + const plugin = new UglifyJsPlugin({ + parallel: true, + sourceMap: true, + }); + plugin.apply(compilerEnv); + eventBindings = pluginEnvironment.getEventBindings(); + }); + + it('binds one event handler', () => { + expect(eventBindings.length).toBe(1); + }); + + describe('compilation handler', () => { + beforeEach(() => { + [eventBinding] = eventBindings; + }); + + it('binds to compilation event', () => { + expect(eventBinding.name).toBe('compilation'); + }); + + describe('when called', () => { + let chunkPluginEnvironment; + let compilationEventBindings; + let compilationEventBinding; + let compilation; + let callback; + + beforeEach(() => { + chunkPluginEnvironment = new PluginEnvironment(); + compilation = chunkPluginEnvironment.getEnvironmentStub(); + compilation.assets = assets; + compilation.errors = []; + + eventBinding.handler(compilation); + compilationEventBindings = chunkPluginEnvironment.getEventBindings(); + }); + + it('binds two event handler', () => { + expect(compilationEventBindings[0].name).toBe('build-module'); + expect(compilationEventBindings[1].name).toBe('optimize-chunk-assets'); + }); + + describe('build-module handler', () => { + beforeEach(() => { + [compilationEventBinding] = compilationEventBindings; + }); + + it('binds to build-module event', () => { + expect(compilationEventBinding.name).toEqual('build-module'); + }); + + it('build-module handler', (done) => { + const moduleArgs = { useSourceMap: false }; + const mockBuildModuleEvent = jest.fn(() => compilationEventBinding.handler(moduleArgs)); + + mockBuildModuleEvent(); + + expect(mockBuildModuleEvent.mock.calls.length).toBe(1); + expect(moduleArgs.useSourceMap).toBe(true); + done(); + }); + }); + + describe('optimize-chunk-assets handler', () => { + beforeEach(() => { + [, compilationEventBinding] = compilationEventBindings; + }); + + it('binds to optimize-chunk-assets event', () => { + expect(compilationEventBinding.name).toEqual('optimize-chunk-assets'); + }); + + it('only calls callback once', (done) => { + callback = jest.fn(); + compilationEventBinding.handler([{ + files: ['test.js', 'test1.js', 'test2.js', 'test3.js'], + }], () => { + callback(); + expect(callback.mock.calls.length).toBe(1); + done(); + }); + }); + }); + }); + }); + + it('matches snapshot', () => { + const compiler = createCompiler(); + new UglifyJsPlugin({ parallel: true, sourceMap: true }).apply(compiler); + + return compile(compiler).then((stats) => { + const errors = stats.compilation.errors.map(cleanErrorStack); + const warnings = stats.compilation.warnings.map(cleanErrorStack); + + expect(errors).toMatchSnapshot('source map and parallel: errors'); + expect(warnings).toMatchSnapshot('source map and parallel: warnings'); + + for (const file in stats.compilation.assets) { + if (Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)) { + const asset = stats.compilation.assets[file].sourceAndMap(); + + asset.map.sources = []; + + expect(asset.source).toMatchSnapshot(`source and parallel: asset ${file}`); + expect(asset.map).toMatchSnapshot(`source map and parallel: asset ${file}`); + } + } + }); + }); + }); +});