Skip to content

Commit

Permalink
refactor: code and test (#214)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Jan 30, 2020
1 parent 330c1f6 commit 8d9837d
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 47 deletions.
19 changes: 15 additions & 4 deletions src/Webpack5Cache.js
Expand Up @@ -2,7 +2,7 @@
import getLazyHashedEtag from 'webpack/lib/cache/getLazyHashedEtag';
import serialize from 'serialize-javascript';

import TerserPlugin from './index';
import { util } from 'webpack';

export default class Cache {
constructor(compiler, compilation, options) {
Expand All @@ -16,9 +16,20 @@ export default class Cache {
}

createCacheIdent(task) {
const cacheKeys = TerserPlugin.getHasher(this.compiler)
.update(serialize(task.cacheKeys))
.digest('hex');
const {
outputOptions: { hashSalt, hashDigest, hashDigestLength, hashFunction },
} = this.compilation;

const hash = util.createHash(hashFunction);

if (hashSalt) {
hash.update(hashSalt);
}

hash.update(serialize(task.cacheKeys));

const digest = hash.digest(hashDigest);
const cacheKeys = digest.substr(0, hashDigestLength);

return `${this.compilation.compilerPath}/TerserWebpackPlugin/${cacheKeys}/${task.file}`;
}
Expand Down
31 changes: 20 additions & 11 deletions src/index.js
Expand Up @@ -4,12 +4,12 @@ import { SourceMapConsumer } from 'source-map';
import { SourceMapSource, RawSource, ConcatSource } from 'webpack-sources';
import RequestShortener from 'webpack/lib/RequestShortener';
import {
util,
ModuleFilenameHelpers,
SourceMapDevToolPlugin,
javascript,
version as webpackVersion,
} from 'webpack';
import createHash from 'webpack/lib/util/createHash';
import validateOptions from 'schema-utils';
import serialize from 'serialize-javascript';
import terserPackageJson from 'terser/package.json';
Expand Down Expand Up @@ -386,6 +386,24 @@ class TerserPlugin {
};

if (TerserPlugin.isWebpack4()) {
const {
outputOptions: {
hashSalt,
hashDigest,
hashDigestLength,
hashFunction,
},
} = compilation;
const hash = util.createHash(hashFunction);

if (hashSalt) {
hash.update(hashSalt);
}

hash.update(input);

const digest = hash.digest(hashDigest);

if (this.options.cache) {
const defaultCacheKeys = {
terser: terserPackageJson.version,
Expand All @@ -394,9 +412,7 @@ class TerserPlugin {
'terser-webpack-plugin-options': this.options,
nodeVersion: process.version,
filename: file,
contentHash: TerserPlugin.getHasher(compiler)
.update(input)
.digest('hex'),
contentHash: digest.substr(0, hashDigestLength),
};

task.cacheKeys = this.options.cacheKeys(defaultCacheKeys, file);
Expand Down Expand Up @@ -566,13 +582,6 @@ class TerserPlugin {
);
});
}

static getHasher(compiler = null) {
const hashFunction =
compiler && compiler.output && compiler.output.hashFunction;

return createHash(hashFunction || 'md4');
}
}

export default TerserPlugin;
73 changes: 41 additions & 32 deletions test/TerserPlugin.test.js
@@ -1,5 +1,7 @@
import crypto from 'crypto';

import path from 'path';

import RequestShortener from 'webpack/lib/RequestShortener';
import { javascript } from 'webpack';
import MainTemplate from 'webpack/lib/MainTemplate';
Expand Down Expand Up @@ -386,47 +388,54 @@ describe('TerserPlugin', () => {
)
).toMatchSnapshot();
});
});

const UNHASHED = 'this is some text';
const HASHED_MD4 = '565a21837631bdec2da173a5de2a2f87';
const HASHED_SHA1 = '0393694d16b84deb612e47ce6252bd35f0d86c06';

describe('getHasher', () => {
it('should return MD4 hasher with no compiler parameter', () => {
const hasher = TerserPlugin.getHasher();
it('should respect the hash options #1', async () => {
const compiler = getCompiler({
output: {
pathinfo: false,
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
chunkFilename: '[id].[name].js',
hashDigest: 'hex',
hashDigestLength: 20,
hashFunction: 'sha256',
hashSalt: 'salt',
},
});

expect(hasher).not.toBeNull();
expect(hasher.update(UNHASHED).digest('hex')).toEqual(HASHED_MD4);
});
new TerserPlugin().apply(compiler);

it('should return MD4 hasher with incomplete compiler parameter', () => {
const compiler = { incomplete: { bad: {} } };
const hasher = TerserPlugin.getHasher(compiler);
const stats = await compile(compiler);

expect(hasher).not.toBeNull();
expect(hasher.update(UNHASHED).digest('hex')).toEqual(HASHED_MD4);
expect(readsAssets(compiler, stats)).toMatchSnapshot('assets');
expect(getErrors(stats)).toMatchSnapshot('errors');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
});

it('should return hasher with string as hashFunction', () => {
const compiler = { output: { hashFunction: 'SHA1' } };
const hasher = TerserPlugin.getHasher(compiler);
it('should respect hash options #2', async () => {
function sha256() {
return crypto.createHash('sha256');
}

expect(hasher).not.toBeNull();
expect(hasher.update(UNHASHED).digest('hex')).toEqual(HASHED_SHA1);
});
const compiler = getCompiler({
output: {
pathinfo: false,
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
chunkFilename: '[id].[name].js',
hashDigest: 'hex',
hashDigestLength: 20,
hashFunction: sha256,
hashSalt: 'salt',
},
});

it('should return hasher with function as hashFunction', () => {
function sha1() {
return crypto.createHash('SHA1');
}
new TerserPlugin().apply(compiler);

const compiler = {
output: { hashFunction: sha1 },
};
const hasher = TerserPlugin.getHasher(compiler);
const stats = await compile(compiler);

expect(hasher).not.toBeNull();
expect(hasher.update(UNHASHED).digest('hex')).toEqual(HASHED_SHA1);
expect(readsAssets(compiler, stats)).toMatchSnapshot('assets');
expect(getErrors(stats)).toMatchSnapshot('errors');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
});
});
20 changes: 20 additions & 0 deletions test/__snapshots__/TerserPlugin.test.js.snap.webpack4
Expand Up @@ -82,6 +82,26 @@ exports[`TerserPlugin should regenerate hash: errors 1`] = `Array []`;

exports[`TerserPlugin should regenerate hash: warnings 1`] = `Array []`;

exports[`TerserPlugin should respect hash options #2: assets 1`] = `
Object {
"main.js": "!function(e){var t={};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}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\\"a\\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\\"\\",n(n.s=0)}([function(e,t){e.exports=function(){console.log(7)}}]);",
}
`;

exports[`TerserPlugin should respect hash options #2: errors 1`] = `Array []`;

exports[`TerserPlugin should respect hash options #2: warnings 1`] = `Array []`;

exports[`TerserPlugin should respect the hash options #1: assets 1`] = `
Object {
"main.js": "!function(e){var t={};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}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\\"a\\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\\"\\",n(n.s=0)}([function(e,t){e.exports=function(){console.log(7)}}]);",
}
`;

exports[`TerserPlugin should respect the hash options #1: errors 1`] = `Array []`;

exports[`TerserPlugin should respect the hash options #1: warnings 1`] = `Array []`;

exports[`TerserPlugin should work (without options): assets 1`] = `
Object {
"main.js": "!function(e){var t={};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}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){\\"undefined\\"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:\\"Module\\"}),Object.defineProperty(e,\\"__esModule\\",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&\\"object\\"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,\\"default\\",{enumerable:!0,value:e}),2&t&&\\"string\\"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,\\"a\\",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p=\\"\\",n(n.s=0)}([function(e,t){e.exports=function(){console.log(7)}}]);",
Expand Down
20 changes: 20 additions & 0 deletions test/__snapshots__/TerserPlugin.test.js.snap.webpack5
Expand Up @@ -82,6 +82,26 @@ exports[`TerserPlugin should regenerate hash: errors 1`] = `Array []`;

exports[`TerserPlugin should regenerate hash: warnings 1`] = `Array []`;

exports[`TerserPlugin should respect hash options #2: assets 1`] = `
Object {
"main.js": "(()=>{var r={791:r=>{r.exports=function(){console.log(7)}}},o={};!function t(e){if(o[e])return o[e].exports;var n=o[e]={exports:{}};return r[e](n,n.exports,t),n.exports}(791)})();",
}
`;

exports[`TerserPlugin should respect hash options #2: errors 1`] = `Array []`;

exports[`TerserPlugin should respect hash options #2: warnings 1`] = `Array []`;

exports[`TerserPlugin should respect the hash options #1: assets 1`] = `
Object {
"main.js": "(()=>{var r={791:r=>{r.exports=function(){console.log(7)}}},o={};!function t(e){if(o[e])return o[e].exports;var n=o[e]={exports:{}};return r[e](n,n.exports,t),n.exports}(791)})();",
}
`;

exports[`TerserPlugin should respect the hash options #1: errors 1`] = `Array []`;

exports[`TerserPlugin should respect the hash options #1: warnings 1`] = `Array []`;

exports[`TerserPlugin should work (without options): assets 1`] = `
Object {
"main.js": "(()=>{var r={791:r=>{r.exports=function(){console.log(7)}}},o={};!function t(e){if(o[e])return o[e].exports;var n=o[e]={exports:{}};return r[e](n,n.exports,t),n.exports}(791)})();",
Expand Down

0 comments on commit 8d9837d

Please sign in to comment.