From 0e42f2e3e1fe9b6663f0afc0750b6cf7aadb5e2f Mon Sep 17 00:00:00 2001 From: Rohit-L Date: Mon, 28 May 2018 10:27:22 -0700 Subject: [PATCH] feature: add support for additional cache keys option --- README.md | 30 ++++ src/index.js | 3 + src/options.json | 4 + test/__snapshots__/cache-options.test.js.snap | 36 +++++ .../invalid-options.test.js.snap | 32 ++-- test/cache-options.test.js | 148 ++++++++++++++++++ test/invalid-options.test.js | 12 ++ 7 files changed, 256 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 32ab2ea0..d0586bd3 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ module.exports = { |**`include`**|`{RegExp\|Array}`|`undefined`|Files to `include`| |**`exclude`**|`{RegExp\|Array}`|`undefined`|Files to `exclude`| |**`cache`**|`{Boolean\|String}`|`false`|Enable file caching| +|**`additionalCacheKeys`**|`{Object}`|`{}`|Add additional keys to the default cache key generator| |**`parallel`**|`{Boolean\|Number}`|`false`|Use multi-process parallel running to improve the build speed| |**`sourceMap`**|`{Boolean}`|`false`|Use source maps to map error message locations to modules (This slows down the compilation) ⚠️ **`cheap-source-map` options don't work with this plugin**| |**`uglifyOptions`**|`{Object}`|[`{...defaults}`](https://github.com/webpack-contrib/uglifyjs-webpack-plugin/tree/master#uglifyoptions)|`uglify` [Options](https://github.com/mishoo/UglifyJS2/tree/harmony#minify-options)| @@ -112,6 +113,35 @@ Default path to cache directory: `node_modules/.cache/uglifyjs-webpack-plugin`. Path to cache directory. +### `additionalCacheKeys` + +#### `{Object}` + +**webpack.config.js** +```js +[ + new UglifyJsPlugin({ + cache: true, + additionalCacheKeys: { + myCustomKey: 'myCustomValue', + } + }) +] +``` + +Add additional keys to the default cache key generator. + +Default keys: +```js +{ + 'uglify-es': versions.uglify, // uglify version + 'uglifyjs-webpack-plugin': versions.plugin, // plugin version + 'uglifyjs-webpack-plugin-options': this.options, // plugin options + path: compiler.outputPath ? `${compiler.outputPath}/${file}` : file, // asset path + hash: crypto.createHash('md4').update(input).digest('hex'), // source file hash +} +``` + ### `parallel` #### `{Boolean}` diff --git a/src/index.js b/src/index.js index a604dc19..a6440704 100644 --- a/src/index.js +++ b/src/index.js @@ -27,6 +27,7 @@ class UglifyJsPlugin { extractComments = false, sourceMap = false, cache = false, + additionalCacheKeys = {}, parallel = false, include, exclude, @@ -38,6 +39,7 @@ class UglifyJsPlugin { extractComments, sourceMap, cache, + additionalCacheKeys, parallel, include, exclude, @@ -182,6 +184,7 @@ class UglifyJsPlugin { 'uglifyjs-webpack-plugin-options': this.options, path: compiler.outputPath ? `${compiler.outputPath}/${file}` : file, hash: crypto.createHash('md4').update(input).digest('hex'), + ...this.options.additionalCacheKeys, }); } diff --git a/src/options.json b/src/options.json index e73aa639..3b74471a 100644 --- a/src/options.json +++ b/src/options.json @@ -10,6 +10,10 @@ { "type": "string" } ] }, + "additionalCacheKeys": { + "type": "object", + "additionalProperties": true + }, "parallel": { "oneOf": [ { "type": "boolean" }, diff --git a/test/__snapshots__/cache-options.test.js.snap b/test/__snapshots__/cache-options.test.js.snap index cf265a1a..d954eddb 100644 --- a/test/__snapshots__/cache-options.test.js.snap +++ b/test/__snapshots__/cache-options.test.js.snap @@ -79,3 +79,39 @@ exports[`when options.cache true matches snapshot: main.0c220ec66316af2c1b24.js exports[`when options.cache true matches snapshot: manifest.d6857f782c13a99b5917.js 1`] = `"!function(r){var n=window.webpackJsonp;window.webpackJsonp=function(e,u,c){for(var f,i,p,a=0,l=[];a= 5 " `; -exports[`when applied with invalid options throws validation errors 12`] = ` +exports[`when applied with invalid options throws validation errors 14`] = ` "UglifyJs Plugin Invalid Options options.uglifyOptions.ecma should be <= 8 diff --git a/test/cache-options.test.js b/test/cache-options.test.js index 7ce62499..f2823e20 100644 --- a/test/cache-options.test.js +++ b/test/cache-options.test.js @@ -436,4 +436,152 @@ describe('when options.cache', () => { }); }); }); + + describe('when options.additionalCacheKeys', () => { + let eventBindings; + let eventBinding; + + beforeAll(() => cacache.rm.all(cacheDir)); + + afterAll(() => cacache.rm.all(cacheDir)); + + beforeEach(() => { + const pluginEnvironment = new PluginEnvironment(); + const compilerEnv = pluginEnvironment.getEnvironmentStub(); + compilerEnv.context = ''; + + const plugin = new UglifyJsPlugin({ + cache: true, + additionalCacheKeys: { + myCustomKey: 'myCustomValue', + }, + }); + 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 = Object.assign({}, assets); + compilation.errors = []; + + eventBinding.handler(compilation); + compilationEventBindings = chunkPluginEnvironment.getEventBindings(); + }); + + it('binds one event handler', () => { + expect(compilationEventBindings.length).toBe(1); + }); + + 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([''], () => { + callback(); + expect(callback.mock.calls.length).toBe(1); + done(); + }); + }); + + it('cache files', (done) => { + const files = ['test.js', 'test1.js', 'test2.js', 'test3.js']; + + cacache.get = jest.fn(cacache.get); + cacache.put = jest.fn(cacache.put); + + compilationEventBinding.handler([{ + files, + }], () => { + // Try to found cached files, but we don't have their in cache + expect(cacache.get.mock.calls.length).toBe(4); + // Put files in cache + expect(cacache.put.mock.calls.length).toBe(4); + + cacache + .ls(cacheDir) + .then((cacheEntriesList) => { + const cacheKeys = Object.keys(cacheEntriesList); + + // Make sure that we cached files + expect(cacheKeys.length).toBe(files.length); + cacheKeys.forEach((cacheEntry) => { + // eslint-disable-next-line no-new-func + const cacheEntryOptions = new Function(`'use strict'\nreturn ${cacheEntry}`)(); + + expect([cacheEntryOptions.path, cacheEntryOptions.hash]) + .toMatchSnapshot(cacheEntryOptions.path); + }); + + // Reset compilation assets and mocks + compilation.assets = Object.assign({}, assets); + compilation.errors = []; + + cacache.get.mockClear(); + cacache.put.mockClear(); + + compilationEventBinding.handler([{ + files, + }], () => { + // Now we have cached files so we get their and don't put + expect(cacache.get.mock.calls.length).toBe(4); + expect(cacache.put.mock.calls.length).toBe(0); + + done(); + }); + }); + }); + }); + }); + }); + }); + + it('matches snapshot', () => { + const compiler = createCompiler(); + new UglifyJsPlugin({ cache: 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('errors'); + expect(warnings).toMatchSnapshot('warnings'); + + for (const file in stats.compilation.assets) { + if (Object.prototype.hasOwnProperty.call(stats.compilation.assets, file)) { + expect(stats.compilation.assets[file].source()).toMatchSnapshot(file); + } + } + }); + }); + }); }); diff --git a/test/invalid-options.test.js b/test/invalid-options.test.js index 7cb092c9..982f5943 100644 --- a/test/invalid-options.test.js +++ b/test/invalid-options.test.js @@ -54,6 +54,18 @@ describe('when applied with invalid options', () => { new UglifyJsPlugin({ cache: {} }); }).toThrowErrorMatchingSnapshot(); + expect(() => { + new UglifyJsPlugin({ additionalCacheKeys: false }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new UglifyJsPlugin({ additionalCacheKeys: 'additional-cache-key' }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new UglifyJsPlugin({ additionalCacheKeys: { path: 'path/to/output' } }); + }).not.toThrow('Validation Error'); + expect(() => { new UglifyJsPlugin({ parallel: true }); }).not.toThrow('Validation Error');