Skip to content

Commit

Permalink
feat: enable the cache option by default (#132)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the `cache` option is `true` by default
  • Loading branch information
evilebottnawi committed Sep 4, 2019
1 parent 7b342af commit 960d249
Show file tree
Hide file tree
Showing 21 changed files with 334 additions and 128 deletions.
21 changes: 1 addition & 20 deletions README.md
Expand Up @@ -139,7 +139,7 @@ module.exports = {
### `cache`

Type: `Boolean|String`
Default: `false`
Default: `true`

Enable file caching.
Default path to cache directory: `node_modules/.cache/terser-webpack-plugin`.
Expand Down Expand Up @@ -599,25 +599,6 @@ module.exports = {

## Examples

### Cache And Parallel

Enable cache and multi-process parallel running.

**webpack.config.js**

```js
module.exports = {
optimization: {
minimizer: [
new TerserPlugin({
cache: true,
parallel: true,
}),
],
},
};
```

### Preserve Comments

Extract all legal comments (i.e. `/^\**!|@preserve|@license|@cc_on/i`) and preserve `/@license/i` comments.
Expand Down
17 changes: 12 additions & 5 deletions src/TaskRunner.js
Expand Up @@ -13,16 +13,23 @@ const worker = require.resolve('./worker');
export default class TaskRunner {
constructor(options = {}) {
const { cache, parallel } = options;
this.cacheDir =
cache === true
? findCacheDir({ name: 'terser-webpack-plugin' }) || os.tmpdir()
: cache;

this.cacheDir = cache === true ? TaskRunner.getCacheDirectory() : cache;
this.maxConcurrentWorkers = TaskRunner.getMaxConcurrentWorkers(parallel);
}

static getCacheDirectory() {
return findCacheDir({ name: 'terser-webpack-plugin' }) || os.tmpdir();
}

static getMaxConcurrentWorkers(parallel) {
// In some cases cpus() returns undefined
// https://github.com/nodejs/node/issues/19022
const cpus = os.cpus() || { length: 1 };

// WSL sometimes freezes, error seems to be on the WSL side
// https://github.com/webpack-contrib/terser-webpack-plugin/issues/21
this.maxConcurrentWorkers = isWsl
return isWsl
? 1
: parallel === true
? cpus.length - 1
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Expand Up @@ -29,7 +29,7 @@ class TerserPlugin {
warningsFilter = () => true,
extractComments = false,
sourceMap = false,
cache = false,
cache = true,
cacheKeys = (defaultCacheKeys) => defaultCacheKeys,
parallel = true,
include,
Expand Down
12 changes: 11 additions & 1 deletion test/TerserPlugin.test.js
Expand Up @@ -4,7 +4,13 @@ import ChunkTemplate from 'webpack/lib/ChunkTemplate';

import TerserPlugin from '../src/index';

import { cleanErrorStack, compile, createCompiler, getAssets } from './helpers';
import {
cleanErrorStack,
compile,
createCompiler,
getAssets,
removeCache,
} from './helpers';

describe('TerserPlugin', () => {
const rawSourceMap = {
Expand All @@ -22,6 +28,10 @@ describe('TerserPlugin', () => {
mappings: '',
};

beforeEach(() => Promise.all([removeCache()]));

afterEach(() => Promise.all([removeCache()]));

it('should work (without options)', async () => {
const compiler = createCompiler();

Expand Down
52 changes: 40 additions & 12 deletions test/__snapshots__/cache-option.test.js.snap

Large diffs are not rendered by default.

186 changes: 114 additions & 72 deletions test/cache-option.test.js
@@ -1,17 +1,34 @@
import cacache from 'cacache';
import findCacheDir from 'find-cache-dir';

import TaskRunner from '../src/TaskRunner';
import TerserPlugin from '../src/index';

import { createCompiler, compile, cleanErrorStack, getAssets } from './helpers';

const cacheDir = findCacheDir({ name: 'terser-webpack-plugin' });
import {
createCompiler,
compile,
cleanErrorStack,
getAssets,
removeCache,
} from './helpers';

const uniqueCacheDirectory = findCacheDir({ name: 'unique-cache-directory' });
const uniqueOtherDirectory = findCacheDir({
name: 'unique-other-cache-directory',
});
const otherCacheDir = findCacheDir({ name: 'other-cache-directory' });
const otherOtherCacheDir = findCacheDir({
name: 'other-other-cache-directory',
});

jest.setTimeout(30000);

describe('cache option', () => {
let compiler;

beforeEach(() => {
jest.clearAllMocks();

compiler = createCompiler({
entry: {
one: `${__dirname}/fixtures/cache.js`,
Expand All @@ -23,20 +40,33 @@ describe('cache option', () => {
});

return Promise.all([
cacache.rm.all(cacheDir),
cacache.rm.all(otherCacheDir),
removeCache(),
removeCache(uniqueCacheDirectory),
removeCache(uniqueOtherDirectory),
removeCache(otherCacheDir),
removeCache(otherOtherCacheDir),
]);
});

afterEach(() =>
Promise.all([cacache.rm.all(cacheDir), cacache.rm.all(otherCacheDir)])
);
afterEach(() => {
return Promise.all([
removeCache(),
removeCache(uniqueCacheDirectory),
removeCache(uniqueOtherDirectory),
removeCache(otherCacheDir),
removeCache(otherOtherCacheDir),
]);
});

it('should match snapshot for the "false" value', async () => {
new TerserPlugin({ cache: false }).apply(compiler);
it('should match snapshot when a value is not specify', async () => {
const cacacheGetSpy = jest.spyOn(cacache, 'get');
const cacachePutSpy = jest.spyOn(cacache, 'put');

cacache.get = jest.fn(cacache.get);
cacache.put = jest.fn(cacache.put);
jest.spyOn(TaskRunner, 'getCacheDirectory').mockImplementation(() => {
return uniqueCacheDirectory;
});

new TerserPlugin({ cache: true }).apply(compiler);

const stats = await compile(compiler);

Expand All @@ -47,21 +77,62 @@ describe('cache option', () => {
expect(warnings).toMatchSnapshot('warnings');
expect(getAssets(stats, compiler)).toMatchSnapshot('assets');

// Cache disabled so we don't run `get` or `put`
expect(cacache.get.mock.calls.length).toBe(0);
expect(cacache.put.mock.calls.length).toBe(0);
const countAssets = Object.keys(stats.compilation.assets).length;

// Try to found cached files, but we don't have their in cache
expect(cacacheGetSpy).toHaveBeenCalledTimes(countAssets);
// Put files in cache
expect(cacachePutSpy).toHaveBeenCalledTimes(countAssets);

cacache.get.mockClear();
cacache.put.mockClear();

const newStats = await compile(compiler);

const newErrors = newStats.compilation.errors.map(cleanErrorStack);
const newWarnings = newStats.compilation.warnings.map(cleanErrorStack);

expect(newErrors).toMatchSnapshot('errors');
expect(newWarnings).toMatchSnapshot('warnings');

expect(getAssets(newStats, compiler)).toMatchSnapshot('assets');

const newCountAssets = Object.keys(newStats.compilation.assets).length;

// Now we have cached files so we get them and don't put new
expect(cacacheGetSpy).toHaveBeenCalledTimes(newCountAssets);
expect(cacachePutSpy).toHaveBeenCalledTimes(0);
});

it('should match snapshot for the "false" value', async () => {
const cacacheGetSpy = jest.spyOn(cacache, 'get');
const cacachePutSpy = jest.spyOn(cacache, 'put');

new TerserPlugin({ cache: false }).apply(compiler);

const stats = await compile(compiler);

const errors = stats.compilation.errors.map(cleanErrorStack);
const warnings = stats.compilation.warnings.map(cleanErrorStack);

const cacheEntriesList = await cacache.ls(cacheDir);
const cacheKeys = Object.keys(cacheEntriesList);
expect(errors).toMatchSnapshot('errors');
expect(warnings).toMatchSnapshot('warnings');
expect(getAssets(stats, compiler)).toMatchSnapshot('assets');

expect(cacheKeys.length).toBe(0);
// Cache disabled so we don't run `get` or `put`
expect(cacacheGetSpy).toHaveBeenCalledTimes(0);
expect(cacachePutSpy).toHaveBeenCalledTimes(0);
});

it('should match snapshot for the "true" value', async () => {
new TerserPlugin({ cache: true }).apply(compiler);
const cacacheGetSpy = jest.spyOn(cacache, 'get');
const cacachePutSpy = jest.spyOn(cacache, 'put');

jest.spyOn(TaskRunner, 'getCacheDirectory').mockImplementation(() => {
return uniqueOtherDirectory;
});

cacache.get = jest.fn(cacache.get);
cacache.put = jest.fn(cacache.put);
new TerserPlugin({ cache: true }).apply(compiler);

const stats = await compile(compiler);

Expand All @@ -75,16 +146,9 @@ describe('cache option', () => {
const countAssets = Object.keys(stats.compilation.assets).length;

// Try to found cached files, but we don't have their in cache
expect(cacache.get.mock.calls.length).toBe(countAssets);
expect(cacacheGetSpy).toHaveBeenCalledTimes(countAssets);
// Put files in cache
expect(cacache.put.mock.calls.length).toBe(countAssets);

const cacheEntriesList = await cacache.ls(cacheDir);

const cacheKeys = Object.keys(cacheEntriesList);

// Make sure that we cached files
expect(cacheKeys.length).toBe(countAssets);
expect(cacachePutSpy).toHaveBeenCalledTimes(countAssets);

cacache.get.mockClear();
cacache.put.mockClear();
Expand All @@ -102,15 +166,15 @@ describe('cache option', () => {
const newCountAssets = Object.keys(newStats.compilation.assets).length;

// Now we have cached files so we get them and don't put new
expect(cacache.get.mock.calls.length).toBe(newCountAssets);
expect(cacache.put.mock.calls.length).toBe(0);
expect(cacacheGetSpy).toHaveBeenCalledTimes(newCountAssets);
expect(cacachePutSpy).toHaveBeenCalledTimes(0);
});

it('should match snapshot for the "other-cache-directory" value', async () => {
new TerserPlugin({ cache: otherCacheDir }).apply(compiler);
const cacacheGetSpy = jest.spyOn(cacache, 'get');
const cacachePutSpy = jest.spyOn(cacache, 'put');

cacache.get = jest.fn(cacache.get);
cacache.put = jest.fn(cacache.put);
new TerserPlugin({ cache: otherCacheDir }).apply(compiler);

const stats = await compile(compiler);

Expand All @@ -124,15 +188,9 @@ describe('cache option', () => {
const countAssets = Object.keys(stats.compilation.assets).length;

// Try to found cached files, but we don't have their in cache
expect(cacache.get.mock.calls.length).toBe(countAssets);
expect(cacacheGetSpy).toHaveBeenCalledTimes(countAssets);
// Put files in cache
expect(cacache.put.mock.calls.length).toBe(countAssets);

const cacheEntriesList = await cacache.ls(otherCacheDir);
const cacheKeys = Object.keys(cacheEntriesList);

// Make sure that we cached files
expect(cacheKeys.length).toBe(countAssets);
expect(cacachePutSpy).toHaveBeenCalledTimes(countAssets);

cacache.get.mockClear();
cacache.put.mockClear();
Expand All @@ -149,14 +207,17 @@ describe('cache option', () => {

const newCountAssets = Object.keys(newStats.compilation.assets).length;

// Now we have cached files so we get their and don't put
expect(cacache.get.mock.calls.length).toBe(newCountAssets);
expect(cacache.put.mock.calls.length).toBe(0);
// Now we have cached files so we get them and don't put new
expect(cacacheGetSpy).toHaveBeenCalledTimes(newCountAssets);
expect(cacachePutSpy).toHaveBeenCalledTimes(0);
});

it('should match snapshot for the "true" value when "cacheKey" is custom "function"', async () => {
it('should match snapshot when "cacheKey" is custom "function"', async () => {
const cacacheGetSpy = jest.spyOn(cacache, 'get');
const cacachePutSpy = jest.spyOn(cacache, 'put');

new TerserPlugin({
cache: true,
cache: otherOtherCacheDir,
cacheKeys: (defaultCacheKeys, file) => {
// eslint-disable-next-line no-param-reassign
defaultCacheKeys.myCacheKey = 1;
Expand All @@ -167,9 +228,6 @@ describe('cache option', () => {
},
}).apply(compiler);

cacache.get = jest.fn(cacache.get);
cacache.put = jest.fn(cacache.put);

const stats = await compile(compiler);

const errors = stats.compilation.errors.map(cleanErrorStack);
Expand All @@ -182,25 +240,9 @@ describe('cache option', () => {
const countAssets = Object.keys(stats.compilation.assets).length;

// Try to found cached files, but we don't have their in cache
expect(cacache.get.mock.calls.length).toBe(countAssets);
expect(cacacheGetSpy).toHaveBeenCalledTimes(countAssets);
// Put files in cache
expect(cacache.put.mock.calls.length).toBe(countAssets);

const cacheEntriesList = await cacache.ls(cacheDir);
const cacheKeys = Object.keys(cacheEntriesList);

// Make sure that we cached files
expect(cacheKeys.length).toBe(countAssets);

cacheKeys.forEach((cacheEntry) => {
// eslint-disable-next-line no-new-func
const cacheEntryOptions = new Function(
`'use strict'\nreturn ${cacheEntry}`
)();

expect(cacheEntryOptions.myCacheKey).toBe(1);
expect(cacheEntryOptions.myCacheKeyBasedOnFile).toMatch(/file-(.+)?\.js/);
});
expect(cacachePutSpy).toHaveBeenCalledTimes(countAssets);

cacache.get.mockClear();
cacache.put.mockClear();
Expand All @@ -216,9 +258,9 @@ describe('cache option', () => {

const newCountAssets = Object.keys(newStats.compilation.assets).length;

// Now we have cached files so we get their and don't put
expect(cacache.get.mock.calls.length).toBe(newCountAssets);
expect(cacache.put.mock.calls.length).toBe(0);
// Now we have cached files so we get them and don't put new
expect(cacacheGetSpy).toHaveBeenCalledTimes(newCountAssets);
expect(cacachePutSpy).toHaveBeenCalledTimes(0);
});

it('should match snapshot for errors into the "cacheKeys" option', async () => {
Expand Down

0 comments on commit 960d249

Please sign in to comment.