Skip to content

Commit 1496f85

Browse files
feat: added filter option (#524)
1 parent 09b1dc9 commit 1496f85

File tree

6 files changed

+164
-38
lines changed

6 files changed

+164
-38
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ module.exports = {
8080
| [`to`](#to) | `{String}` | `compiler.options.output` | Output path. |
8181
| [`context`](#context) | `{String}` | `options.context \|\| compiler.options.context` | A path that determines how to interpret the `from` path. |
8282
| [`globOptions`](#globoptions) | `{Object}` | `undefined` | [Options][glob-options] passed to the glob pattern matching library including `ignore` option. |
83+
| [`filter`](#filter) | `{Function}` | `undefined` | Allows to filter copied assets. |
8384
| [`toType`](#totype) | `{String}` | `undefined` | Determinate what is `to` option - directory, file or template. |
8485
| [`force`](#force) | `{Boolean}` | `false` | Overwrites files already in `compilation.assets` (usually added by other plugins/loaders). |
8586
| [`flatten`](#flatten) | `{Boolean}` | `false` | Removes all directory references and only copies file names. |
@@ -277,6 +278,41 @@ module.exports = {
277278
};
278279
```
279280

281+
#### `filter`
282+
283+
Type: `Function`
284+
Default: `undefined`
285+
286+
> ℹ️ To ignore files by path please use the [`globOptions.ignore`]((#globoptions) option.
287+
288+
**webpack.config.js**
289+
290+
```js
291+
const fs = require('fs').promise;
292+
293+
module.exports = {
294+
plugins: [
295+
new CopyPlugin({
296+
patterns: [
297+
{
298+
from: 'public/**/*',
299+
filter: async (resourcePath) => {
300+
const data = await fs.promises.readFile(resourcePath);
301+
const content = data.toString();
302+
303+
if (content === 'my-custom-content') {
304+
return false;
305+
}
306+
307+
return true;
308+
},
309+
},
310+
],
311+
}),
312+
],
313+
};
314+
```
315+
280316
#### `toType`
281317

282318
Type: `String`

src/options.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
"globOptions": {
1818
"type": "object"
1919
},
20+
"filter": {
21+
"instanceof": "Function"
22+
},
2023
"toType": {
2124
"enum": ["dir", "file", "template"]
2225
},

src/processPattern.js

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,32 +31,46 @@ export default async function processPattern(globalRef, pattern) {
3131
return Promise.resolve();
3232
}
3333

34-
return (
35-
paths
36-
// Exclude directories
37-
.filter((item) => item.dirent.isFile())
38-
.map((item) => {
39-
const from = item.path;
40-
41-
logger.debug(`found ${from}`);
42-
43-
// `globby`/`fast-glob` return the relative path when the path contains special characters on windows
44-
const absoluteFrom = path.resolve(pattern.context, from);
45-
const relativeFrom = pattern.flatten
46-
? path.basename(absoluteFrom)
47-
: path.relative(pattern.context, absoluteFrom);
48-
let webpackTo =
49-
pattern.toType === 'dir'
50-
? path.join(pattern.to, relativeFrom)
51-
: pattern.to;
52-
53-
if (path.isAbsolute(webpackTo)) {
54-
webpackTo = path.relative(output, webpackTo);
34+
const filteredPaths = (
35+
await Promise.all(
36+
paths.map(async (item) => {
37+
// Exclude directories
38+
if (!item.dirent.isFile()) {
39+
return false;
5540
}
5641

57-
logger.log(`determined that '${from}' should write to '${webpackTo}'`);
42+
if (pattern.filter) {
43+
const isFiltered = await pattern.filter(item.path);
5844

59-
return { absoluteFrom, relativeFrom, webpackTo };
45+
return isFiltered ? item : false;
46+
}
47+
48+
return item;
6049
})
61-
);
50+
)
51+
).filter((item) => item);
52+
53+
return filteredPaths.map((item) => {
54+
const from = item.path;
55+
56+
logger.debug(`found ${from}`);
57+
58+
// `globby`/`fast-glob` return the relative path when the path contains special characters on windows
59+
const absoluteFrom = path.resolve(pattern.context, from);
60+
const relativeFrom = pattern.flatten
61+
? path.basename(absoluteFrom)
62+
: path.relative(pattern.context, absoluteFrom);
63+
let webpackTo =
64+
pattern.toType === 'dir'
65+
? path.join(pattern.to, relativeFrom)
66+
: pattern.to;
67+
68+
if (path.isAbsolute(webpackTo)) {
69+
webpackTo = path.relative(output, webpackTo);
70+
}
71+
72+
logger.log(`determined that '${from}' should write to '${webpackTo}'`);
73+
74+
return { absoluteFrom, relativeFrom, webpackTo };
75+
});
6276
}

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

Lines changed: 19 additions & 14 deletions
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?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
17+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, 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`] = `
@@ -37,6 +37,11 @@ exports[`validate options should throw an error on the "patterns" option with "[
3737
- options.patterns[0].from should be an non-empty string."
3838
`;
3939
40+
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","filter":"test"}]" value 1`] = `
41+
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
42+
- options.patterns[0].filter should be an instance of function."
43+
`;
44+
4045
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","context":"context","cacheTransform":{"foo":"bar"}}]" value 1`] = `
4146
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
4247
- options.patterns[0].cacheTransform has an unknown property 'foo'. These properties are valid:
@@ -72,7 +77,7 @@ exports[`validate options should throw an error on the "patterns" option with "[
7277
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","context":"context"}]" value 1`] = `
7378
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
7479
- options.patterns[0] should be one of these:
75-
non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }
80+
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }
7681
Details:
7782
* options.patterns[0].cacheTransform should be one of these:
7883
boolean | string | object { directory?, keys? }
@@ -112,71 +117,71 @@ exports[`validate options should throw an error on the "patterns" option with "[
112117
exports[`validate options should throw an error on the "patterns" option with "{}" value 1`] = `
113118
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
114119
- options.patterns should be an array:
115-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
120+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
116121
`;
117122
118123
exports[`validate options should throw an error on the "patterns" option with "true" value 1`] = `
119124
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
120125
- options.patterns should be an array:
121-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
126+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
122127
`;
123128
124129
exports[`validate options should throw an error on the "patterns" option with "true" value 2`] = `
125130
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
126131
- options.patterns should be an array:
127-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
132+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
128133
`;
129134
130135
exports[`validate options should throw an error on the "patterns" option with "undefined" value 1`] = `
131136
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
132137
- options misses the property 'patterns'. Should be:
133-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
138+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
134139
`;
135140
136141
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `
137142
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
138143
- options misses the property 'patterns'. Should be:
139-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
144+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
140145
`;
141146
142147
exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = `
143148
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
144149
- options misses the property 'patterns'. Should be:
145-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
150+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
146151
`;
147152
148153
exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = `
149154
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
150155
- options misses the property 'patterns'. Should be:
151-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
156+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
152157
`;
153158
154159
exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = `
155160
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
156161
- options misses the property 'patterns'. Should be:
157-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
162+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
158163
`;
159164
160165
exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = `
161166
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
162167
- options misses the property 'patterns'. Should be:
163-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
168+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
164169
`;
165170
166171
exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = `
167172
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
168173
- options misses the property 'patterns'. Should be:
169-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
174+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
170175
`;
171176
172177
exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = `
173178
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
174179
- options misses the property 'patterns'. Should be:
175-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
180+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
176181
`;
177182
178183
exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = `
179184
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
180185
- options misses the property 'patterns'. Should be:
181-
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
186+
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
182187
`;

test/filter-option.test.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import fs from 'fs';
2+
3+
import { runEmit } from './helpers/run';
4+
5+
describe('"filter" option', () => {
6+
it('should work, copy files and filter some of them', (done) => {
7+
runEmit({
8+
expectedAssetKeys: [
9+
'.dottedfile',
10+
'nested/deep-nested/deepnested.txt',
11+
'nested/nestedfile.txt',
12+
],
13+
patterns: [
14+
{
15+
from: 'directory',
16+
filter: (resourcePath) => {
17+
if (/directoryfile\.txt$/.test(resourcePath)) {
18+
return false;
19+
}
20+
21+
return true;
22+
},
23+
},
24+
],
25+
})
26+
.then(done)
27+
.catch(done);
28+
});
29+
30+
it('should work, copy files and filter some of them using async function', (done) => {
31+
runEmit({
32+
expectedAssetKeys: [
33+
'.dottedfile',
34+
'nested/deep-nested/deepnested.txt',
35+
'nested/nestedfile.txt',
36+
],
37+
patterns: [
38+
{
39+
from: 'directory',
40+
filter: async (resourcePath) => {
41+
const data = await fs.promises.readFile(resourcePath);
42+
const content = data.toString();
43+
44+
if (content === 'new') {
45+
return false;
46+
}
47+
48+
return true;
49+
},
50+
},
51+
],
52+
})
53+
.then(done)
54+
.catch(done);
55+
});
56+
});

test/validate-options.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ describe('validate options', () => {
132132
},
133133
},
134134
],
135+
[
136+
{
137+
from: 'test.txt',
138+
filter: () => true,
139+
},
140+
],
135141
],
136142
failure: [
137143
// eslint-disable-next-line no-undefined
@@ -247,6 +253,12 @@ describe('validate options', () => {
247253
noErrorOnMissing: 'true',
248254
},
249255
],
256+
[
257+
{
258+
from: 'test.txt',
259+
filter: 'test',
260+
},
261+
],
250262
],
251263
},
252264
options: {

0 commit comments

Comments
 (0)