Skip to content

Commit ea610bc

Browse files
authoredMar 4, 2021
feat: added priority option (#590)
1 parent a9b06a6 commit ea610bc

6 files changed

+183
-22
lines changed
 

‎README.md

+36
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ module.exports = {
8585
| [`filter`](#filter) | `{Function}` | `undefined` | Allows to filter copied assets. |
8686
| [`toType`](#totype) | `{String}` | `undefined` | Determinate what is `to` option - directory, file or template. |
8787
| [`force`](#force) | `{Boolean}` | `false` | Overwrites files already in `compilation.assets` (usually added by other plugins/loaders). |
88+
| [`priority`](#priority) | `{Number}` | `0` | Allows you to specify the copy priority. |
8889
| [`transform`](#transform) | `{Object}` | `undefined` | Allows to modify the file contents. Enable `transform` caching. You can use `{ transform: {cache: { key: 'my-cache-key' }} }` to invalidate the cache. |
8990
| [`noErrorOnMissing`](#noerroronmissing) | `{Boolean}` | `false` | Doesn't generate an error on missing file(s). |
9091
| [`info`](#info) | `{Object\|Function}` | `undefined` | Allows to add assets info. |
@@ -462,6 +463,41 @@ module.exports = {
462463
};
463464
```
464465

466+
#### `priority`
467+
468+
Type: `Number`
469+
Default: `0`
470+
471+
Allows to specify the priority of copying files with the same destination name.
472+
Files for patterns with higher priority will be copied later.
473+
To overwrite files, the [`force`](#force) option must be enabled.
474+
475+
**webpack.config.js**
476+
477+
```js
478+
module.exports = {
479+
plugins: [
480+
new CopyPlugin({
481+
patterns: [
482+
// Сopied second and will overwrite "dir_2/file.txt"
483+
{
484+
from: "dir_1/file.txt",
485+
to: "newfile.txt",
486+
force: true,
487+
priority: 10,
488+
},
489+
// Сopied first
490+
{
491+
from: "dir_2/file.txt",
492+
to: "newfile.txt",
493+
priority: 5,
494+
},
495+
],
496+
}),
497+
],
498+
};
499+
```
500+
465501
#### `transform`
466502

467503
Type: `Function|Object`

‎src/index.js

+27-5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class CopyPlugin {
7070
}
7171

7272
static async runPattern(
73+
assetMap,
7374
compiler,
7475
compilation,
7576
logger,
@@ -313,7 +314,13 @@ class CopyPlugin {
313314
path.relative(compiler.context, absoluteFilename)
314315
);
315316

316-
return { absoluteFilename, sourceFilename, filename, toType };
317+
return {
318+
absoluteFilename,
319+
sourceFilename,
320+
filename,
321+
toType,
322+
priority: pattern.priority || 0,
323+
};
317324
})
318325
);
319326

@@ -322,7 +329,13 @@ class CopyPlugin {
322329
try {
323330
assets = await Promise.all(
324331
files.map(async (file) => {
325-
const { absoluteFilename, sourceFilename, filename, toType } = file;
332+
const {
333+
absoluteFilename,
334+
sourceFilename,
335+
filename,
336+
toType,
337+
priority,
338+
} = file;
326339
const info =
327340
typeof pattern.info === "function"
328341
? pattern.info(file) || {}
@@ -578,6 +591,12 @@ class CopyPlugin {
578591
result.filename = normalizePath(result.filename);
579592
}
580593

594+
if (!assetMap.has(priority)) {
595+
assetMap.set(priority, []);
596+
}
597+
598+
assetMap.get(priority).push(result);
599+
581600
// eslint-disable-next-line consistent-return
582601
return result;
583602
})
@@ -612,13 +631,14 @@ class CopyPlugin {
612631
async (unusedAssets, callback) => {
613632
logger.log("starting to add additional assets...");
614633

615-
let assets;
634+
const assetMap = new Map();
616635

617636
try {
618-
assets = await Promise.all(
637+
await Promise.all(
619638
this.patterns.map((item, index) =>
620639
limit(async () =>
621640
CopyPlugin.runPattern(
641+
assetMap,
622642
compiler,
623643
compilation,
624644
logger,
@@ -637,10 +657,12 @@ class CopyPlugin {
637657
return;
638658
}
639659

660+
const assets = [...assetMap.entries()].sort((a, b) => a[0] - b[0]);
661+
640662
// Avoid writing assets inside `p-limit`, because it creates concurrency.
641663
// It could potentially lead to an error - 'Multiple assets emit different content to the same filename'
642664
assets
643-
.reduce((acc, val) => acc.concat(val), [])
665+
.reduce((acc, val) => acc.concat(val[1]), [])
644666
.filter(Boolean)
645667
.forEach((asset) => {
646668
const {

‎src/options.json

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
"force": {
3434
"type": "boolean"
3535
},
36+
"priority": {
37+
"type": "number"
38+
},
3639
"info": {
3740
"anyOf": [
3841
{

‎test/__snapshots__/validate-options.test.js.snap

+32-17
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ exports[`validate options should throw an error on the "options" option with "{"
1414
exports[`validate options should throw an error on the "patterns" option with "" value 1`] = `
1515
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
1616
- options.patterns should be an array:
17-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
17+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
1818
`;
1919
2020
exports[`validate options should throw an error on the "patterns" option with "[""]" value 1`] = `
@@ -40,7 +40,7 @@ exports[`validate options should throw an error on the "patterns" option with "[
4040
exports[`validate options should throw an error on the "patterns" option with "[{"from":"dir","info":"string"}]" value 1`] = `
4141
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
4242
- options.patterns[0] should be one of these:
43-
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }
43+
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }
4444
Details:
4545
* options.patterns[0].info should be one of these:
4646
object { … } | function
@@ -53,7 +53,7 @@ exports[`validate options should throw an error on the "patterns" option with "[
5353
exports[`validate options should throw an error on the "patterns" option with "[{"from":"dir","info":true}]" value 1`] = `
5454
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
5555
- options.patterns[0] should be one of these:
56-
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }
56+
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }
5757
Details:
5858
* options.patterns[0].info should be one of these:
5959
object { … } | function
@@ -88,7 +88,7 @@ exports[`validate options should throw an error on the "patterns" option with "[
8888
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","context":"context","transform":true}]" value 1`] = `
8989
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
9090
- options.patterns[0] should be one of these:
91-
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }
91+
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }
9292
Details:
9393
* options.patterns[0].transform should be one of these:
9494
function | object { transformer?, cache? }
@@ -103,10 +103,25 @@ exports[`validate options should throw an error on the "patterns" option with "[
103103
- options.patterns[0].context should be a string."
104104
`;
105105
106+
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","priority":"5"}]" value 1`] = `
107+
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
108+
- options.patterns[0].priority should be a number."
109+
`;
110+
111+
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","priority":true}]" value 1`] = `
112+
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
113+
- options.patterns[0].priority should be a number."
114+
`;
115+
116+
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir"}]" value 1`] = `
117+
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
118+
- options.patterns[0].priority should be a number."
119+
`;
120+
106121
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":true,"context":"context"}]" value 1`] = `
107122
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
108123
- options.patterns[0] should be one of these:
109-
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }
124+
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }
110125
Details:
111126
* options.patterns[0].to should be one of these:
112127
string | function
@@ -134,71 +149,71 @@ exports[`validate options should throw an error on the "patterns" option with "[
134149
exports[`validate options should throw an error on the "patterns" option with "{}" value 1`] = `
135150
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
136151
- options.patterns should be an array:
137-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
152+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
138153
`;
139154
140155
exports[`validate options should throw an error on the "patterns" option with "true" value 1`] = `
141156
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
142157
- options.patterns should be an array:
143-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
158+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
144159
`;
145160
146161
exports[`validate options should throw an error on the "patterns" option with "true" value 2`] = `
147162
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
148163
- options.patterns should be an array:
149-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
164+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
150165
`;
151166
152167
exports[`validate options should throw an error on the "patterns" option with "undefined" value 1`] = `
153168
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
154169
- options misses the property 'patterns'. Should be:
155-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
170+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
156171
`;
157172
158173
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `
159174
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
160175
- options misses the property 'patterns'. Should be:
161-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
176+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
162177
`;
163178
164179
exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = `
165180
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
166181
- options misses the property 'patterns'. Should be:
167-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
182+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
168183
`;
169184
170185
exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = `
171186
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
172187
- options misses the property 'patterns'. Should be:
173-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
188+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
174189
`;
175190
176191
exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = `
177192
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
178193
- options misses the property 'patterns'. Should be:
179-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
194+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
180195
`;
181196
182197
exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = `
183198
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
184199
- options misses the property 'patterns'. Should be:
185-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
200+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
186201
`;
187202
188203
exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = `
189204
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
190205
- options misses the property 'patterns'. Should be:
191-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
206+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
192207
`;
193208
194209
exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = `
195210
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
196211
- options misses the property 'patterns'. Should be:
197-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
212+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
198213
`;
199214
200215
exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = `
201216
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
202217
- options misses the property 'patterns'. Should be:
203-
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
218+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
204219
`;

‎test/priority-option.test.js

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { run } from "./helpers/run";
2+
3+
describe("priority option", () => {
4+
it("should copy without specifying priority option", (done) => {
5+
run({
6+
expectedAssetKeys: [],
7+
patterns: [
8+
{
9+
from: "dir (86)/file.txt",
10+
to: "newfile.txt",
11+
force: true,
12+
},
13+
{
14+
from: "file.txt",
15+
to: "newfile.txt",
16+
force: true,
17+
},
18+
],
19+
})
20+
.then(({ stats }) => {
21+
const { info } = stats.compilation.getAsset("newfile.txt");
22+
23+
expect(info.sourceFilename).toEqual("file.txt");
24+
25+
done();
26+
})
27+
.catch(done);
28+
});
29+
30+
it("should copy with specifying priority option", (done) => {
31+
run({
32+
expectedAssetKeys: [],
33+
patterns: [
34+
{
35+
from: "dir (86)/file.txt",
36+
to: "newfile.txt",
37+
force: true,
38+
priority: 10,
39+
},
40+
{
41+
from: "file.txt",
42+
to: "newfile.txt",
43+
force: true,
44+
priority: 5,
45+
},
46+
],
47+
})
48+
.then(({ stats }) => {
49+
const { info } = stats.compilation.getAsset("newfile.txt");
50+
51+
expect(info.sourceFilename).toEqual("dir (86)/file.txt");
52+
53+
done();
54+
})
55+
.catch(done);
56+
});
57+
});

‎test/validate-options.test.js

+28
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ describe("validate options", () => {
133133
},
134134
},
135135
],
136+
[
137+
{
138+
from: "test.txt",
139+
to: "dir",
140+
priority: 5,
141+
},
142+
],
136143
],
137144
failure: [
138145
// eslint-disable-next-line no-undefined
@@ -242,6 +249,27 @@ describe("validate options", () => {
242249
filter: "test",
243250
},
244251
],
252+
[
253+
{
254+
from: "test.txt",
255+
to: "dir",
256+
priority: "5",
257+
},
258+
],
259+
[
260+
{
261+
from: "test.txt",
262+
to: "dir",
263+
priority: () => {},
264+
},
265+
],
266+
[
267+
{
268+
from: "test.txt",
269+
to: "dir",
270+
priority: true,
271+
},
272+
],
245273
],
246274
},
247275
options: {

0 commit comments

Comments
 (0)
Please sign in to comment.