Skip to content

Commit 2899ed3

Browse files
authored
Merge pull request #59 from webpack-contrib/feature/contenthash
add support for webpack's new contenthash support
2 parents 9b1bd63 + 7b56927 commit 2899ed3

19 files changed

+169
-24
lines changed

.gitattributes

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
package-lock.json -diff
22
* text=auto
3-
bin/* eol=lf
3+
test/cases/* eol=lf
4+
bin/* eol=lf

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ module.exports = {
133133
<tbody>
134134
</table>
135135

136+
#### Long Term Caching
137+
138+
For long term caching use `filename: "[contenthash].css"`. Optionally add `[name]`.
139+
140+
136141
[npm]: https://img.shields.io/npm/v/mini-css-extract-plugin.svg
137142
[npm-url]: https://npmjs.com/package/mini-css-extract-plugin
138143

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"nsp": "^3.1.0",
4545
"pre-commit": "^1.2.2",
4646
"standard-version": "^4.3.0",
47-
"webpack": "^4.1.0",
47+
"webpack": "^4.3.0",
4848
"webpack-cli": "^2.0.13",
4949
"webpack-defaults": "^1.6.0",
5050
"webpack-dev-server": "^3.1.1"
@@ -56,7 +56,7 @@
5656
"node": ">= 6.11.5"
5757
},
5858
"peerDependencies": {
59-
"webpack": "^4.1.0"
59+
"webpack": "^4.3.0"
6060
},
6161
"pre-commit": "lint-staged",
6262
"lint-staged": {

src/index.js

+48-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import webpack from 'webpack';
44
import sources from 'webpack-sources';
55

66
const { ConcatSource, SourceMapSource, OriginalSource } = sources;
7-
const { Template } = webpack;
7+
const { Template, util: { createHash } } = webpack;
88

99
const NS = path.dirname(fs.realpathSync(__filename));
1010

@@ -57,7 +57,7 @@ class CssModule extends webpack.Module {
5757
nameForCondition() {
5858
const resource = this._identifier.split('!').pop();
5959
const idx = resource.indexOf('?');
60-
if (idx >= 0) return resource.substr(0, idx);
60+
if (idx >= 0) return resource.substring(0, idx);
6161
return resource;
6262
}
6363

@@ -66,6 +66,13 @@ class CssModule extends webpack.Module {
6666
this.buildMeta = {};
6767
callback();
6868
}
69+
70+
updateHash(hash) {
71+
super.updateHash(hash);
72+
hash.update(this.content);
73+
hash.update(this.media || '');
74+
hash.update(JSON.stringify(this.sourceMap || ''));
75+
}
6976
}
7077

7178
class CssModuleFactory {
@@ -121,6 +128,7 @@ class MiniCssExtractPlugin {
121128
filenameTemplate: this.options.filename,
122129
pathOptions: {
123130
chunk,
131+
contentHashType: NS,
124132
},
125133
identifier: `mini-css-extract-plugin.${chunk.id}`,
126134
});
@@ -134,11 +142,26 @@ class MiniCssExtractPlugin {
134142
filenameTemplate: this.options.chunkFilename,
135143
pathOptions: {
136144
chunk,
145+
contentHashType: NS,
137146
},
138147
identifier: `mini-css-extract-plugin.${chunk.id}`,
139148
});
140149
}
141150
});
151+
compilation.hooks.contentHash.tap(pluginName, (chunk) => {
152+
const { outputOptions } = compilation;
153+
const { hashFunction, hashDigest, hashDigestLength } = outputOptions;
154+
const hash = createHash(hashFunction);
155+
for (const m of chunk.modulesIterable) {
156+
if (m.type === NS) {
157+
m.updateHash(hash);
158+
}
159+
}
160+
const { contentHash } = chunk;
161+
contentHash[NS] = hash
162+
.digest(hashDigest)
163+
.substring(0, hashDigestLength);
164+
});
142165
const { mainTemplate } = compilation;
143166
mainTemplate.hooks.localVars.tap(
144167
pluginName,
@@ -178,13 +201,35 @@ class MiniCssExtractPlugin {
178201
const shortChunkHashMap = Object.create(null);
179202
for (const chunkId of Object.keys(chunkMaps.hash)) {
180203
if (typeof chunkMaps.hash[chunkId] === 'string') {
181-
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substr(0, length);
204+
shortChunkHashMap[chunkId] = chunkMaps.hash[chunkId].substring(0, length);
182205
}
183206
}
184207
return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
185208
},
209+
contentHash: {
210+
[NS]: `" + ${JSON.stringify(
211+
chunkMaps.contentHash[NS],
212+
)}[chunkId] + "`,
213+
},
214+
contentHashWithLength: {
215+
[NS]: (length) => {
216+
const shortContentHashMap = {};
217+
const contentHash = chunkMaps.contentHash[NS];
218+
for (const chunkId of Object.keys(contentHash)) {
219+
if (typeof contentHash[chunkId] === 'string') {
220+
shortContentHashMap[chunkId] = contentHash[
221+
chunkId
222+
].substring(0, length);
223+
}
224+
}
225+
return `" + ${JSON.stringify(
226+
shortContentHashMap,
227+
)}[chunkId] + "`;
228+
},
229+
},
186230
name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "`,
187231
},
232+
contentHashType: NS,
188233
},
189234
);
190235
return Template.asString([

test/TestCases.test.js

+10-7
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ describe('TestCases', () => {
1313
const outputDirectoryForCase = path.resolve(outputDirectory, directory);
1414
// eslint-disable-next-line import/no-dynamic-require, global-require
1515
const webpackConfig = require(path.resolve(directoryForCase, 'webpack.config.js'));
16-
webpack(Object.assign({
17-
mode: 'none',
18-
context: directoryForCase,
19-
output: {
20-
path: outputDirectoryForCase,
21-
},
22-
}, webpackConfig), (err, stats) => {
16+
for (const config of [].concat(webpackConfig)) {
17+
Object.assign(config, {
18+
mode: 'none',
19+
context: directoryForCase,
20+
output: Object.assign({
21+
path: outputDirectoryForCase,
22+
}, config.output),
23+
}, config);
24+
}
25+
webpack(webpackConfig, (err, stats) => {
2326
if (err) {
2427
done(err);
2528
return;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
body { background: red; }
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
body { background: green; }
2+

test/cases/contenthash/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import './style.css';

test/cases/contenthash/style1.css

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
body { background: red; }

test/cases/contenthash/style2.css

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
body { background: green; }
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const Self = require('../../../');
2+
3+
module.exports = [1, 2].map(n => ({
4+
entry: './index.js',
5+
module: {
6+
rules: [
7+
{
8+
test: /\.css$/,
9+
use: [
10+
Self.loader,
11+
'css-loader',
12+
],
13+
},
14+
],
15+
},
16+
output: {
17+
filename: `${n}.[name].js`
18+
},
19+
resolve: {
20+
alias: {
21+
'./style.css': `./style${n}.css`
22+
}
23+
},
24+
plugins: [
25+
new Self({
26+
filename: `${n}.[name].[contenthash].css`,
27+
}),
28+
],
29+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[
2+
/* 0 */,
3+
/* 1 */
4+
/***/ (function(module, exports, __webpack_require__) {
5+
6+
// extracted by mini-css-extract-plugin
7+
module.exports = {"a":"wX52cuPepLZcpDx5S3yYO"};
8+
9+
/***/ })
10+
]]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.wX52cuPepLZcpDx5S3yYO { background: red; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.wX52cuPepLZcpDx5S3yYO { background: green; }

test/cases/js-hash/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import(/* webpackChunkName: "style" */'./style.css');

test/cases/js-hash/loader.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = function(source) {
2+
const { number } = this.query;
3+
return source.split(/\r?\n/)[number-1];
4+
};

test/cases/js-hash/style.css

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.a { background: red; }
2+
.a { background: green; }

test/cases/js-hash/webpack.config.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
const Self = require('../../../');
2+
3+
module.exports = [1, 2].map(n => ({
4+
entry: './index.js',
5+
module: {
6+
rules: [
7+
{
8+
test: /\.css$/,
9+
use: [
10+
Self.loader,
11+
{
12+
loader: 'css-loader',
13+
options: {
14+
modules: true,
15+
},
16+
},
17+
{
18+
loader: './loader',
19+
ident: 'my-loader',
20+
options: {
21+
number: n
22+
}
23+
}
24+
],
25+
},
26+
],
27+
},
28+
output: {
29+
filename: `[name].[contenthash].js`
30+
},
31+
plugins: [
32+
new Self({
33+
filename: `[name].[contenthash].[chunkhash].css`,
34+
}),
35+
],
36+
}));

yarn.lock

+11-11
Original file line numberDiff line numberDiff line change
@@ -1327,7 +1327,7 @@ cac@^3.0.3:
13271327
suffix "^0.1.0"
13281328
text-table "^0.2.0"
13291329

1330-
cacache@^10.0.1:
1330+
cacache@^10.0.4:
13311331
version "10.0.4"
13321332
resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460"
13331333
dependencies:
@@ -6922,7 +6922,7 @@ sax@^1.2.4, sax@~1.2.1:
69226922
version "1.2.4"
69236923
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
69246924

6925-
schema-utils@^0.4.2:
6925+
schema-utils@^0.4.2, schema-utils@^0.4.5:
69266926
version "0.4.5"
69276927
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
69286928
dependencies:
@@ -7724,13 +7724,13 @@ uglify-to-browserify@~1.0.0:
77247724
version "1.0.2"
77257725
resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7"
77267726

7727-
uglifyjs-webpack-plugin@^1.1.1:
7728-
version "1.2.2"
7729-
resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.2.tgz#e7516d4367afdb715c3847841eb46f94c45ca2b9"
7727+
uglifyjs-webpack-plugin@^1.2.4:
7728+
version "1.2.4"
7729+
resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.4.tgz#5eec941b2e9b8538be0a20fc6eda25b14c7c1043"
77307730
dependencies:
7731-
cacache "^10.0.1"
7731+
cacache "^10.0.4"
77327732
find-cache-dir "^1.0.0"
7733-
schema-utils "^0.4.2"
7733+
schema-utils "^0.4.5"
77347734
serialize-javascript "^1.4.0"
77357735
source-map "^0.6.1"
77367736
uglify-es "^3.3.4"
@@ -8125,9 +8125,9 @@ webpack-sources@^1.0.1, webpack-sources@^1.1.0:
81258125
source-list-map "^2.0.0"
81268126
source-map "~0.6.1"
81278127

8128-
webpack@^4.1.0:
8129-
version "4.1.0"
8130-
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.1.0.tgz#91b6862e56eb3b18b79bb10b51866987ff10d2d6"
8128+
webpack@^4.3.0:
8129+
version "4.3.0"
8130+
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.3.0.tgz#0b0c1e211311b3995dd25aed47ab46ea658be070"
81318131
dependencies:
81328132
acorn "^5.0.0"
81338133
acorn-dynamic-import "^3.0.0"
@@ -8145,7 +8145,7 @@ webpack@^4.1.0:
81458145
node-libs-browser "^2.0.0"
81468146
schema-utils "^0.4.2"
81478147
tapable "^1.0.0"
8148-
uglifyjs-webpack-plugin "^1.1.1"
8148+
uglifyjs-webpack-plugin "^1.2.4"
81498149
watchpack "^1.5.0"
81508150
webpack-sources "^1.0.1"
81518151

0 commit comments

Comments
 (0)