Skip to content

Commit

Permalink
feat: add [hash] token substitution support
Browse files Browse the repository at this point in the history
Closes #98, closes #111
  • Loading branch information
kisenka committed May 11, 2017
1 parent 8741c45 commit 87110f4
Show file tree
Hide file tree
Showing 10 changed files with 42 additions and 17 deletions.
2 changes: 1 addition & 1 deletion examples/extract-sprite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ module.exports = 'sprite.svg#logo-usage';
<img src="sprite.svg#logo-usage" alt="">
```

- [sprite.svg](build/sprite.svg)
- [sprite.svg](build/sprite-c9cbc8.svg)
- [main.html](build/main.html)
- [main.css](build/main.css)
- [main.js](build/main.js)
2 changes: 1 addition & 1 deletion examples/extract-sprite/build/main.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.twitter-logo {
width: 200px;
height: 200px;
background: url(sprite.svg#twitter-usage) no-repeat;
background: url(sprite-c9cbc8.svg#twitter-usage) no-repeat;
background-size: contain;
}
2 changes: 1 addition & 1 deletion examples/extract-sprite/build/main.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</head>
<body>
<h1>Sprite image embedded in entry.html (which processed by html-loader and extracted by extract-text-webpack-plugin)</h1>
<img src="sprite.svg#twitter-usage" alt="">
<img src="sprite-c9cbc8.svg#twitter-usage" alt="">

<h1>Sprite image embedded as background-image in main.css (which processed by css-loader and extracted by extract-text-webpack-plugin)</h1>
<link rel="stylesheet" href="main.css">
Expand Down
2 changes: 1 addition & 1 deletion examples/extract-sprite/build/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony default export */ __webpack_exports__["default"] = ("sprite.svg#twitter");
/* harmony default export */ __webpack_exports__["default"] = (__webpack_require__.p + "sprite-c9cbc8.svg#twitter-usage");

/***/ }),
/* 3 */
Expand Down
3 changes: 2 additions & 1 deletion examples/extract-sprite/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const config = merge(baseConfig, {
test: /\.svg$/,
loader: 'svg-sprite-loader',
options: {
extract: true
extract: true,
spriteFilename: 'sprite-[hash:6].svg'
}
},
{
Expand Down
15 changes: 8 additions & 7 deletions lib/utils/hash-tokens.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
const { getHashDigest } = require('loader-utils');

/**
* Partially stolen from loader-utils#interpolateName
* Custom `[hash-*]` tokens interpolator
* @param {string} input
* @param {Object<string, string>} tokensToHash
* @param {Object<name: string, content: string>} tokens
* @return {string}
* @see https://github.com/webpack/loader-utils#interpolatename
* @example
* hashTokens('[chunkname]-[spritehash:6]', { spritehash: '<svg>...</svg>' })
* // => '[chunkname]-8e04fd'
*/
module.exports = function hashTokens(input, tokensToHash) {
module.exports = function hashTokens(input, tokens) {
let result = input;

Object.keys(tokensToHash).forEach((key) => {
const content = tokensToHash[key];
// eslint-disable-next-line no-useless-escape
Object.keys(tokens).forEach((key) => {
const content = tokens[key];
/**
* @see https://github.com/webpack/loader-utils/blob/fface/lib/interpolateName.js#L65
*/
const re = new RegExp(`\\[(?:(\\w+):)?${key}(?::([a-z]+\\d*))?(?::(\\d+))?]`, 'ig');

result = result.replace(re, (all, hashType, digestType, maxLength) => {
Expand Down
1 change: 1 addition & 0 deletions lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports.getMatchedRules = require('./get-matched-rules');
module.exports.getModuleChunk = require('./get-module-chunk');
module.exports.hashTokens = require('./hash-tokens');
module.exports.isModuleShouldBeExtracted = require('./is-module-should-be-extracted');
module.exports.interpolate = require('./interpolate');
module.exports.isWebpack1 = require('./is-webpack-1');
module.exports.MappedList = require('./mapped-list');
module.exports.normalizeRule = require('./normalize-rule');
Expand Down
15 changes: 15 additions & 0 deletions lib/utils/interpolate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { interpolateName } = require('loader-utils');

/**
* @param {string} pattern
* @param {Object} options
* @param {string} options.resourcePath
* @param {string} [options.context]
* @param {string} [options.content]
*/
function interpolate(pattern, options) {
const { resourcePath, context, content } = options;
return interpolateName({ resourcePath }, pattern, { context, content });
}

module.exports = interpolate;
13 changes: 10 additions & 3 deletions lib/utils/mapped-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const getLoaderOptions = require('./get-loader-options');
const getLoadersRules = require('./get-loaders-rules');
const getMatchedRule = require('./get-matched-rule');
const getModuleChunk = require('./get-module-chunk');
const hashTokens = require('./hash-tokens');
const interpolate = require('./interpolate');

const spriteLoaderPath = require.resolve('../loader');

Expand Down Expand Up @@ -103,15 +103,22 @@ class MappedList {
return acc;
}, []);

// Additional pass to interpolate hash in spriteFilename
// Additional pass to interpolate [hash] in spriteFilename
const itemsBySpriteFilename = MappedList.groupItemsBySpriteFilename(data);
const filenames = Object.keys(itemsBySpriteFilename);

filenames.forEach((filename) => {
if (!filename.includes('[hash')) {
return;
}

const items = itemsBySpriteFilename[filename];
const spriteSymbols = items.map(item => item.symbol);
const content = spriteSymbols.map(s => s.render()).join('');
const interpolatedName = hashTokens(filename, { spritehash: content });
const interpolatedName = interpolate(filename, {
resourcePath: filename,
content
});

items
.filter(item => item.spriteFilename !== interpolatedName)
Expand Down
4 changes: 2 additions & 2 deletions test/loader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,11 @@ describe('loader and plugin', () => {
assets.should.have.property('entry2-sprite.svg');
});

it('should allow to use [spritehash] substitution token in `spriteFilename`', async () => {
it('should allow to use [hash] substitution token in `spriteFilename`', async () => {
const { assets } = await compile({
entry: './entry',
module: rules(
svgRule({ extract: true, spriteFilename: 'sprite-[spritehash:8].svg' })
svgRule({ extract: true, spriteFilename: 'sprite-[hash:8].svg' })
),
plugins: [new SpritePlugin()]
});
Expand Down

0 comments on commit 87110f4

Please sign in to comment.