Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

feat: add option to merge files (options.merge) #636

Closed
wants to merge 7 commits into from
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ new ExtractTextPlugin(options: filename | object)
|**`allChunks`**|`{Boolean}`|Extract from all additional chunks too (by default it extracts only from the initial chunk(s))<br />When using `CommonsChunkPlugin` and there are extracted chunks (from `ExtractTextPlugin.extract`) in the commons chunk, `allChunks` **must** be set to `true`|
|**`disable`**|`{Boolean}`|Disables the plugin|
|**`ignoreOrder`**|`{Boolean}`|Disables order check (useful for CSS Modules!), `false` by default|
|**`merge`**|`{Array}`|List of files to create merging other files. Each element should be like `{ test, filename, preventOriginalOutput}`|

* `[name]` name of the chunk
* `[id]` number of the chunk
Expand All @@ -84,7 +85,22 @@ new ExtractTextPlugin(options: filename | object)
* other `digestType`s, e.g. `hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64`
* and `length`, the length of the hash in chars

> :warning: `ExtractTextPlugin` generates a file **per entry**, so you must use `[name]`, `[id]` or `[contenthash]` when using multiple entries.
> :warning: `ExtractTextPlugin` generates a file **per entry**, so you must use `[name]`, `[id]` or `[contenthash]` when using multiple entries, or set the `merge` option.

```js
// Generate one styles.css from all the *.styles.css and one themes.css from all the *.themes.css files
new ExtractTextPlugin({
merge: [{
filename: 'styles.css', // name of the merged output file (required)
test: /\.styles\.css$/, // files to include (*.styles.css) (required)
originals: false, // when false (by default), original files won't be generated
}, {
filename: 'themes.css',
test: /\.themes\.css$/,
originals: true,
}]
})
```

#### `#extract`

Expand Down
5 changes: 4 additions & 1 deletion schema/loader.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
},
"publicPath": {
"type": "string"
}
},
"merge": {
"type": "array"
}
}
}
4 changes: 4 additions & 0 deletions schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
"publicPath": {
"description": "",
"type": "string"
},
"merge": {
"description": "List of objects as { filename, test, preventOriginalOutput }",
"type": "array"
}
}
}
59 changes: 56 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ class ExtractTextPlugin {
}
this.filename = options.filename;
this.id = options.id != null ? options.id : ++nextId;
this.options = {};
this.options = {
merge: [],
};
this.filesToMerge = {};
mergeOptions(this.options, options);
delete this.options.filename;
delete this.options.id;
Expand All @@ -55,6 +58,23 @@ class ExtractTextPlugin {
return ExtractTextPlugin.loader(mergeOptions({ id: this.id }, options));
}

mergeChunks(chunks, filename) {
const result = new Chunk();
// result.index = i;
// result.originalChunk = chunk;
result.name = filename;
result.entrypoints = [];

chunks.forEach((chunk) => {
// result.entrypoints = result.entrypoints.concat(chunk.entrypoints);
chunk.forEachModule((module) => {
result.addModule(module);
module.addChunk(result);
});
});
return result;
}

mergeNonInitialChunks(chunk, intoChunk, checkedChunks) {
if (!intoChunk) {
checkedChunks = [];
Expand Down Expand Up @@ -215,10 +235,43 @@ class ExtractTextPlugin {

const file = (isFunction(filename)) ? filename(getPath) : getPath(filename);

compilation.assets[file] = source;
chunk.files.push(file);
let outputOriginals = this.options.merge.length === 0;
let matched = false;
this.options.merge.forEach((mergeConfig) => {
if (mergeConfig.test.test(file)) {
matched = true;
if (!this.filesToMerge[mergeConfig.filename]) {
this.filesToMerge[mergeConfig.filename] = [];
}
this.filesToMerge[mergeConfig.filename].push(extractedChunk);
outputOriginals = outputOriginals || mergeConfig.originals;
}
});

if (!matched || outputOriginals) {
compilation.assets[file] = source;
chunk.files.push(file);
}
}
}, this);

Object.keys(this.filesToMerge).forEach((f) => {
const chunks = this.filesToMerge[f];
const mergedChunk = this.mergeChunks(chunks);
const source = this.renderExtractedChunk(mergedChunk);

const getPath = format => compilation.getPath(format, {
mergedChunk,
}).replace(/\[(?:(\w+):)?contenthash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, function () { // eslint-disable-line func-names
return loaderUtils.getHashDigest(source.source(), arguments[1], arguments[2], parseInt(arguments[3], 10));
});

const file = (isFunction(f)) ? filename(getPath) : getPath(f);

compilation.assets[file] = source;
mergedChunk.files.push(file);
});

callback();
});
});
Expand Down
22 changes: 22 additions & 0 deletions test/__snapshots__/webpack-integration.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ exports[`Webpack Integration Tests merging-multiple-entries-shared 2`] = `
"
`;

exports[`Webpack Integration Tests merging-multiple-outputs 1`] = `
"c
d
"
`;

exports[`Webpack Integration Tests merging-multiple-outputs 2`] = `
"a
"
`;

exports[`Webpack Integration Tests merging-multiple-outputs 3`] = `
"b
"
`;

exports[`Webpack Integration Tests merging-multiple-outputs 4`] = `
"a
b
"
`;

exports[`Webpack Integration Tests multiple-entries 1`] = `
"a
b
Expand Down
1 change: 1 addition & 0 deletions test/cases/merging-multiple-outputs/a.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a
1 change: 1 addition & 0 deletions test/cases/merging-multiple-outputs/b.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b
3 changes: 3 additions & 0 deletions test/cases/merging-multiple-outputs/bar-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require.ensure([], () => {
require('./c.txt');
});
3 changes: 3 additions & 0 deletions test/cases/merging-multiple-outputs/bar-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require.ensure([], () => {
require('./d.txt');
});
1 change: 1 addition & 0 deletions test/cases/merging-multiple-outputs/c.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
c
1 change: 1 addition & 0 deletions test/cases/merging-multiple-outputs/d.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
d
2 changes: 2 additions & 0 deletions test/cases/merging-multiple-outputs/expected/bar.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
c
d
1 change: 1 addition & 0 deletions test/cases/merging-multiple-outputs/expected/foo-1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a
1 change: 1 addition & 0 deletions test/cases/merging-multiple-outputs/expected/foo-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
b
2 changes: 2 additions & 0 deletions test/cases/merging-multiple-outputs/expected/foo.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a
b
3 changes: 3 additions & 0 deletions test/cases/merging-multiple-outputs/foo-1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require.ensure([], () => {
require('./a.txt');
});
3 changes: 3 additions & 0 deletions test/cases/merging-multiple-outputs/foo-2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require.ensure([], () => {
require('./b.txt');
});
24 changes: 24 additions & 0 deletions test/cases/merging-multiple-outputs/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import ExtractTextPlugin from '../../../src/index';

module.exports = {
entry: {
'foo-1': './foo-1',
'foo-2': './foo-2',
'bar-1': './bar-1',
'bar-2': './bar-2',
},
plugins: [
new ExtractTextPlugin({
filename: '[name].txt',
allChunks: true,
merge: [{
filename: 'foo.txt',
test: /foo/,
originals: true,
}, {
filename: 'bar.txt',
test: /bar/,
}]
}),
],
};