Skip to content

Commit

Permalink
fix: compatibility with webpack@5
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Aug 22, 2020
1 parent 506fe12 commit 84e367b
Show file tree
Hide file tree
Showing 8 changed files with 476 additions and 270 deletions.
468 changes: 277 additions & 191 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@
},
"devDependencies": {
"@babel/cli": "^7.10.5",
"@babel/core": "^7.11.1",
"@babel/core": "^7.11.4",
"@babel/preset-env": "^7.11.0",
"@commitlint/cli": "^9.1.2",
"@commitlint/cli": "^10.0.0",
"@commitlint/config-conventional": "^10.0.0",
"@gfx/zopfli": "^1.0.15",
"@webpack-contrib/defaults": "^6.3.0",
Expand All @@ -65,7 +65,7 @@
"eslint-plugin-import": "^2.22.0",
"file-loader": "^6.0.0",
"husky": "^4.2.5",
"jest": "^26.4.0",
"jest": "^26.4.1",
"lint-staged": "^10.2.11",
"memfs": "^3.2.0",
"npm-run-all": "^4.1.5",
Expand Down
43 changes: 30 additions & 13 deletions src/Webpack4Cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,59 @@ import findCacheDir from 'find-cache-dir';
import serialize from 'serialize-javascript';

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

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

isEnabled() {
return Boolean(this.cacheDir);
}
async get(task, sources) {
const weakOutput = this.weakCache.get(task.assetSource);

if (weakOutput) {
return weakOutput;
}

if (!this.cache) {
// eslint-disable-next-line no-undefined
return undefined;
}

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

let cachedResult;

try {
cachedResult = await cacache.get(this.cacheDir, task.cacheIdent);
cachedResult = await cacache.get(this.cache, task.cacheIdent);
} catch (ignoreError) {
// eslint-disable-next-line no-undefined
return undefined;
}

return Buffer.from(JSON.parse(cachedResult.data).data);
return new sources.RawSource(
Buffer.from(JSON.parse(cachedResult.data).data)
);
}

async store(task) {
return cacache.put(
this.cacheDir,
task.cacheIdent,
JSON.stringify(task.output)
);
if (!this.weakCache.has(task.assetSource)) {
this.weakCache.set(task.assetSource, task.output);
}

if (!this.cache) {
// eslint-disable-next-line no-undefined
return undefined;
}

const { cacheIdent, output } = task;

return cacache.put(this.cache, cacheIdent, JSON.stringify(output.source()));
}
}
8 changes: 1 addition & 7 deletions src/Webpack5Cache.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
export default class Cache {
// eslint-disable-next-line no-unused-vars
constructor(compilation, ignored) {
constructor(compilation) {
this.cache = compilation.getCache('CompressionWebpackPlugin');
}

// eslint-disable-next-line class-methods-use-this
isEnabled() {
return true;
}

async get(task) {
// eslint-disable-next-line no-param-reassign
task.eTag = task.eTag || this.cache.getLazyHashedEtag(task.assetSource);
Expand Down
91 changes: 40 additions & 51 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import crypto from 'crypto';
import url from 'url';
import path from 'path';

import RawSource from 'webpack-sources/lib/RawSource';
import { ModuleFilenameHelpers, version as webpackVersion } from 'webpack';
import webpack, {
ModuleFilenameHelpers,
version as webpackVersion,
} from 'webpack';
import validateOptions from 'schema-utils';

import schema from './options.json';

const { RawSource } =
// eslint-disable-next-line global-require
webpack.sources || require('webpack-sources');

class CompressionPlugin {
constructor(options = {}) {
validateOptions(schema, options, {
Expand Down Expand Up @@ -203,29 +209,20 @@ class CompressionPlugin {
yield task;
}

afterTask(compilation, task, weakCache) {
afterTask(compilation, task) {
const { output, input } = task;

if (output.length / input.length > this.options.minRatio) {
if (output.source().length / input.length > this.options.minRatio) {
return;
}

const { assetSource, assetName } = task;

let weakOutput = weakCache.get(assetSource);

if (!weakOutput) {
weakOutput = new RawSource(output);

weakCache.set(assetSource, weakOutput);
}

const newAssetName = CompressionPlugin.interpolateName(
assetName,
this.options.filename
);

CompressionPlugin.emitAsset(compilation, newAssetName, weakOutput, {
CompressionPlugin.emitAsset(compilation, newAssetName, output, {
compressed: true,
});

Expand All @@ -241,28 +238,15 @@ class CompressionPlugin {

async runTasks(compilation, assetNames, CacheEngine, weakCache) {
const scheduledTasks = [];
const cache = new CacheEngine(compilation, {
cache: this.options.cache,
});
const cache = new CacheEngine(
compilation,
{
cache: this.options.cache,
},
weakCache
);

for (const assetName of assetNames) {
const enqueue = async (task) => {
try {
// eslint-disable-next-line no-param-reassign
task.output = await this.compress(task.input);
} catch (error) {
compilation.errors.push(error);

return;
}

if (cache.isEnabled()) {
await cache.store(task);
}

this.afterTask(compilation, task, weakCache);
};

scheduledTasks.push(
(async () => {
const task = this.getTask(compilation, assetName).next().value;
Expand All @@ -271,23 +255,24 @@ class CompressionPlugin {
return Promise.resolve();
}

if (cache.isEnabled()) {
task.output = await cache.get(task, { RawSource });

if (!task.output) {
try {
task.output = await cache.get(task);
} catch (ignoreError) {
return enqueue(task);
}
// eslint-disable-next-line no-param-reassign
task.output = new RawSource(await this.compress(task.input));
} catch (error) {
compilation.errors.push(error);

if (!task.output) {
return enqueue(task);
return Promise.resolve();
}

this.afterTask(compilation, task, weakCache);

return Promise.resolve();
await cache.store(task);
}

return enqueue(task);
this.afterTask(compilation, task);

return Promise.resolve();
})()
);
}
Expand All @@ -306,8 +291,12 @@ class CompressionPlugin {
undefined,
this.options
);
const weakCache = new WeakMap();
const compressionFn = async (compilation, CacheEngine, assets) => {
const compressionFn = async (
compilation,
assets,
CacheEngine,
weakCache
) => {
const assetNames = Object.keys(
typeof assets === 'undefined' ? compilation.assets : assets
).filter((assetName) => matchObject(assetName));
Expand All @@ -324,11 +313,11 @@ class CompressionPlugin {
if (CompressionPlugin.isWebpack4()) {
// eslint-disable-next-line global-require
const CacheEngine = require('./Webpack4Cache').default;
const weakCache = new WeakMap();

compiler.hooks.emit.tapPromise(
{ name: pluginName },
compiler.hooks.emit.tapPromise({ name: pluginName }, (compilation) =>
// eslint-disable-next-line no-undefined
(compilation) => compressionFn(compilation, CacheEngine)
compressionFn(compilation, undefined, CacheEngine, weakCache)
);
} else {
// eslint-disable-next-line global-require
Expand All @@ -343,7 +332,7 @@ class CompressionPlugin {
name: pluginName,
stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER,
},
(assets) => compressionFn(compilation, CacheEngine, assets)
(assets) => compressionFn(compilation, assets, CacheEngine)
);

compilation.hooks.statsPrinter.tap(pluginName, (stats) => {
Expand Down
30 changes: 30 additions & 0 deletions test/CompressionPlugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,34 @@ describe('CompressionPlugin', () => {
resolve();
});
});

it('should work in watch mode when "cache" is "false"', async () => {
if (getCompiler.isWebpack4()) {
expect(true).toBe(true);
} else {
const compiler = getCompiler('./entry.js', {
cache: false,
});

new CompressionPlugin().apply(compiler);

const stats = await compile(compiler);

expect(stats.compilation.emittedAssets.size).toBe(7);
expect(getAssetsNameAndSize(stats)).toMatchSnapshot('assets');
expect(getWarnings(stats)).toMatchSnapshot('errors');
expect(getErrors(stats)).toMatchSnapshot('warnings');

await new Promise(async (resolve) => {
const newStats = await compile(compiler);

expect(newStats.compilation.emittedAssets.size).toBe(0);
expect(getAssetsNameAndSize(newStats)).toMatchSnapshot('assets');
expect(getWarnings(newStats)).toMatchSnapshot('errors');
expect(getErrors(newStats)).toMatchSnapshot('warnings');

resolve();
});
}
});
});
Loading

0 comments on commit 84e367b

Please sign in to comment.