UNPKG

4.93 kBJavaScriptView Raw
1const crypto = require('crypto');
2const webpack = require('webpack');
3const path = require('path');
4const expect = require('chai').expect;
5
6const expectAssetsNameToContainHash = (stats, filter = ()=>true) => {
7 const {assets} = stats.compilation;
8 const {hashFn} = stats;
9 expect(Object.keys(assets)).to.have.length.at.least(1);
10 Object.keys(assets).filter(filter).forEach(name => {
11 const asset = assets[name];
12 const { shortHash } = hashFn(asset.source());
13 expect(name).to.contain(shortHash);
14 });
15}
16
17const webpackCompile = (fixture) => {
18 return new Promise((resolve, reject)=> {
19 const dir = path.resolve(__dirname, fixture);
20 const config = path.resolve(dir, 'webpack.config.js');
21 const opts = Object.assign(require(config), { context: dir });
22 webpack(opts, (err, stats) => {
23 // Just attach the hashFn here while we have the outputOptions so we can assert with it.
24 stats.hashFn = makeHashFn(opts.output);
25 if (err) reject(err);
26 else resolve(stats);
27 });
28 })
29}
30
31// Each compilation may use a different hash fn, so we need to generate one from the webpack outputOptions.
32function makeHashFn({
33 hashFunction = 'md5',
34 hashDigest = 'hex',
35 hashDigestLength = 20,
36 hashSalt = null,
37} = {}) {
38 return (input) => {
39 const hashObj = crypto.createHash(hashFunction).update(input);
40 if (hashSalt) hashObj.update(hashSalt);
41 const fullHash = hashObj.digest(hashDigest);
42 return { fullHash, shortHash: fullHash.substr(0, hashDigestLength) };
43 };
44}
45
46const findAssetByName = (assets, name) => {
47 const assetName = Object.keys(assets).map(n => n.split('.')).find(n => n[0] === name).join('.');
48 return assets[assetName];
49}
50
51const extractHashes = (assets, filter) => {
52 return Object.keys(assets)
53 .map(n => n.split('.'))
54 .filter(filter)
55 .map(n => n[1])
56}
57
58describe('OutputHash', () => {
59 it("Works with single entry points", () => webpackCompile('one-asset')
60 .then((stats) => {
61 expectAssetsNameToContainHash(stats);
62 })
63 )
64
65 // Waiting on https://github.com/webpack/webpack/pull/4717
66 xit("Works with hashSalt", () => webpackCompile('one-asset-salt')
67 .then((stats) => {
68 expectAssetsNameToContainHash(stats);
69 })
70 )
71
72 it("Works with hashFunction (sha256)", () => webpackCompile('one-asset-sha256')
73 .then((stats) => {
74 expectAssetsNameToContainHash(stats);
75 })
76 )
77
78 it("Works with hashDigest (base64)", () => webpackCompile('one-asset-base64')
79 .then((stats) => {
80 expectAssetsNameToContainHash(stats);
81 })
82 )
83
84 it("Works with multiple entry points", () => webpackCompile('multi-asset')
85 .then((stats) => {
86 expectAssetsNameToContainHash(stats);
87 })
88 )
89
90 it("Works with common chunks", () => webpackCompile('common-chunks')
91 .then((stats) => {
92 expectAssetsNameToContainHash(stats);
93
94 const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="vendor")
95 const commons = findAssetByName(stats.compilation.assets, "vendor");
96
97 expect(hashes).to.have.lengthOf(1);
98 hashes.forEach(hash => {
99 expect(commons.source()).to.contain(hash);
100 });
101 })
102 )
103
104 it("Works with manifest file", () => webpackCompile('manifest')
105 .then((stats) => {
106 expectAssetsNameToContainHash(stats);
107
108 const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="manifest")
109 const commons = findAssetByName(stats.compilation.assets, "manifest");
110
111 expect(hashes).to.have.lengthOf(2);
112 hashes.forEach(hash => {
113 expect(commons.source()).to.contain(hash);
114 });
115 })
116 )
117
118 it("Works with a complex uglified project", () => webpackCompile('manifest-uglify')
119 .then((stats) => {
120 expectAssetsNameToContainHash(stats);
121
122 const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="manifest")
123 const commons = findAssetByName(stats.compilation.assets, "manifest");
124
125 expect(hashes).to.have.lengthOf(2);
126 hashes.forEach(hash => {
127 expect(commons.source()).to.contain(hash);
128 });
129 })
130 )
131
132 it("Works with HTML output", () => webpackCompile('html')
133 .then((stats) => {
134 expectAssetsNameToContainHash(stats, name => name!=="index.html");
135
136 const hashes = extractHashes(stats.compilation.assets, n => n[0]!=="index")
137 const commons = findAssetByName(stats.compilation.assets, "index");
138
139 expect(hashes).to.have.lengthOf(2);
140 hashes.forEach(hash => {
141 expect(commons.source()).to.contain(hash);
142 });
143 })
144 )
145});