Skip to content

Commit

Permalink
feat: support webpack@5 cache
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergey Melyukov authored and evilebottnawi committed Dec 12, 2019
1 parent 9f8c812 commit 3649b3d
Show file tree
Hide file tree
Showing 11 changed files with 438 additions and 491 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ module.exports = {

### `cache`

> ⚠ Doesn't work with webpack 5!
Type: `Boolean|String`
Default: `true`

Expand Down Expand Up @@ -189,6 +191,8 @@ module.exports = {

### `cacheKeys`

> ⚠ Doesn't work with webpack 5!
Type: `Function<(defaultCacheKeys, file) -> Object>`
Default: `defaultCacheKeys => defaultCacheKeys`

Expand Down Expand Up @@ -491,6 +495,8 @@ module.exports = {
extractComments: {
condition: /^\**!|@preserve|@license|@cc_on/i,
filename: (file, fileData) => {
// ⚠ webpack 5: there is only fileData parameter

// A file can contain a query string (for example when you have `output.filename: '[name].js?[chunkhash]'`)
// You must consider this
// The "fileData" argument contains object with "filename", "basename", "query"
Expand Down Expand Up @@ -523,6 +529,8 @@ module.exports = {
extractComments: {
condition: 'some',
filename: (file, fileData) => {
// ⚠ webpack 5: there is only fileData parameter

// A file can contain a query string (for example when you have `output.filename: '[name].js?[chunkhash]'`)
// You must consider this
return file.replace(/\.(\w+)($|\?)/, '.$1.LICENSE$2');
Expand All @@ -542,7 +550,7 @@ module.exports = {
Type: `String|Function<(string) -> String>`
Default: `[file].LICENSE[query]`

Available placeholders: `[file]`, `[query]` and `[filebase]`.
Available placeholders: `[file]`, `[query]` and `[filebase]` (`[base]` for webpack 5).

The file where the extracted comments will be stored.
Default is to append the suffix `.LICENSE` to the original filename.
Expand Down Expand Up @@ -588,6 +596,8 @@ module.exports = {
extractComments: {
condition: true,
filename: (file, fileData) => {
// ⚠ webpack 5: there is only fileData parameter

// A file can contain a query string (for example when you have `output.filename: '[name].js?[chunkhash]'`)
// You must consider this
return file.replace(/\.(\w+)($|\?)/, '.$1.LICENSE$2');
Expand Down
35 changes: 9 additions & 26 deletions src/TaskRunner.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import os from 'os';

import cacache from 'cacache';
import findCacheDir from 'find-cache-dir';
import Worker from 'jest-worker';
import serialize from 'serialize-javascript';

Expand All @@ -11,15 +9,8 @@ const workerPath = require.resolve('./worker');

export default class TaskRunner {
constructor(options = {}) {
this.options = options;
this.cacheDir = TaskRunner.getCacheDirectory(this.options.cache);
this.numberWorkers = TaskRunner.getNumberWorkers(this.options.parallel);
}

static getCacheDirectory(cache) {
return cache === true
? findCacheDir({ name: 'terser-webpack-plugin' }) || os.tmpdir()
: cache;
this.cache = options.cache;
this.numberWorkers = TaskRunner.getNumberWorkers(options.parallel);
}

static getNumberWorkers(parallel) {
Expand Down Expand Up @@ -56,26 +47,18 @@ export default class TaskRunner {
result = { error };
}

if (this.cacheDir && !result.error) {
return cacache
.put(
this.cacheDir,
serialize(task.cacheKeys),
JSON.stringify(result)
)
.then(
() => result,
() => result
);
if (this.cache.isEnabled() && !result.error) {
return this.cache.store(task, result).then(
() => result,
() => result
);
}

return result;
};

if (this.cacheDir) {
return cacache
.get(this.cacheDir, serialize(task.cacheKeys))
.then(({ data }) => JSON.parse(data), enqueue);
if (this.cache.isEnabled()) {
return this.cache.get(task).then((data) => data, enqueue);
}

return enqueue();
Expand Down
36 changes: 36 additions & 0 deletions src/Webpack4Cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os from 'os';

import cacache from 'cacache';
import findCacheDir from 'find-cache-dir';
import serialize from 'serialize-javascript';

export default class Webpack4Cache {
constructor(compilation, options) {
this.options = options;
this.cacheDir =
options.cache === true
? Webpack4Cache.getCacheDirectory()
: options.cache;
}

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

isEnabled() {
return !!this.cacheDir;
}

get(task) {
// eslint-disable-next-line no-param-reassign
task.cacheIdent = task.cacheIdent || serialize(task.cacheKeys);

return cacache
.get(this.cacheDir, task.cacheIdent)
.then(({ data }) => JSON.parse(data));
}

store(task, data) {
return cacache.put(this.cacheDir, task.cacheIdent, JSON.stringify(data));
}
}
65 changes: 65 additions & 0 deletions src/Webpack5Cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import crypto from 'crypto';

// eslint-disable-next-line import/extensions,import/no-unresolved
import getLazyHashedEtag from 'webpack/lib/cache/getLazyHashedEtag';
import serialize from 'serialize-javascript';

export default class Cache {
constructor(compilation, options) {
this.options = options;
this.compilation = compilation;
}

isEnabled() {
return !!this.compilation.cache;
}

createCacheIdent(task) {
const cacheKeys = crypto
.createHash('md4')
.update(serialize(task.cacheKeys))
.digest('hex');

return `${this.compilation.compilerPath}/TerserWebpackPlugin/${cacheKeys}/${task.file}`;
}

get(task) {
// eslint-disable-next-line no-param-reassign
task.cacheIdent = task.cacheIdent || this.createCacheIdent(task);
// eslint-disable-next-line no-param-reassign
task.cacheETag = task.cacheETag || getLazyHashedEtag(task.asset);

return new Promise((resolve, reject) => {
this.compilation.cache.get(
task.cacheIdent,
task.cacheETag,
(err, result) => {
if (err) {
reject(err);
} else if (result) {
resolve(result);
} else {
reject();
}
}
);
});
}

store(task, data) {
return new Promise((resolve, reject) => {
this.compilation.cache.store(
task.cacheIdent,
task.cacheETag,
data,
(err) => {
if (err) {
reject(err);
} else {
resolve(data);
}
}
);
});
}
}
53 changes: 38 additions & 15 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ModuleFilenameHelpers,
SourceMapDevToolPlugin,
javascript,
version as webpackVersion,
} from 'webpack';
import validateOptions from 'schema-utils';
import serialize from 'serialize-javascript';
Expand Down Expand Up @@ -192,6 +193,10 @@ class TerserPlugin {
);
}

static isWebpack4() {
return webpackVersion[0] === '4';
}

apply(compiler) {
const { devtool, output, plugins } = compiler.options;

Expand Down Expand Up @@ -288,9 +293,11 @@ class TerserPlugin {
commentsFilename =
this.options.extractComments.filename || '[file].LICENSE[query]';

// Todo remove this in next major release
if (typeof commentsFilename === 'function') {
commentsFilename = commentsFilename.bind(null, file);
if (TerserPlugin.isWebpack4()) {
// Todo remove this in next major release
if (typeof commentsFilename === 'function') {
commentsFilename = commentsFilename.bind(null, file);
}
}

let query = '';
Expand Down Expand Up @@ -330,6 +337,7 @@ class TerserPlugin {
}

const task = {
asset,
file,
input,
inputSourceMap,
Expand All @@ -339,21 +347,30 @@ class TerserPlugin {
minify: this.options.minify,
};

if (this.options.cache) {
const defaultCacheKeys = {
if (TerserPlugin.isWebpack4()) {
if (this.options.cache) {
const defaultCacheKeys = {
terser: terserPackageJson.version,
// eslint-disable-next-line global-require
'terser-webpack-plugin': require('../package.json').version,
'terser-webpack-plugin-options': this.options,
nodeVersion: process.version,
filename: file,
contentHash: crypto
.createHash('md4')
.update(input)
.digest('hex'),
};

task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file);
}
} else {
task.cacheKeys = {
terser: terserPackageJson.version,
// eslint-disable-next-line global-require
'terser-webpack-plugin': require('../package.json').version,
'terser-webpack-plugin-options': this.options,
nodeVersion: process.version,
filename: file,
contentHash: crypto
.createHash('md4')
.update(input)
.digest('hex'),
};

task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file);
}

tasks.push(task);
Expand All @@ -373,8 +390,14 @@ class TerserPlugin {
return Promise.resolve();
}

const CacheEngine = TerserPlugin.isWebpack4()
? // eslint-disable-next-line global-require
require('./Webpack4Cache').default
: // eslint-disable-next-line global-require
require('./Webpack5Cache').default;

const taskRunner = new TaskRunner({
cache: this.options.cache,
cache: new CacheEngine(compilation, this.options),
parallel: this.options.parallel,
});

Expand Down Expand Up @@ -522,7 +545,7 @@ class TerserPlugin {
});
}

if (javascript && javascript.JavascriptModulesPlugin) {
if (!TerserPlugin.isWebpack4()) {
const hooks = javascript.JavascriptModulesPlugin.getCompilationHooks(
compilation
);
Expand Down
Loading

0 comments on commit 3649b3d

Please sign in to comment.