Skip to content

Commit

Permalink
feat: added filter option (#524)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed Aug 31, 2020
1 parent 09b1dc9 commit 1496f85
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 38 deletions.
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ module.exports = {
| [`to`](#to) | `{String}` | `compiler.options.output` | Output path. |
| [`context`](#context) | `{String}` | `options.context \|\| compiler.options.context` | A path that determines how to interpret the `from` path. |
| [`globOptions`](#globoptions) | `{Object}` | `undefined` | [Options][glob-options] passed to the glob pattern matching library including `ignore` option. |
| [`filter`](#filter) | `{Function}` | `undefined` | Allows to filter copied assets. |
| [`toType`](#totype) | `{String}` | `undefined` | Determinate what is `to` option - directory, file or template. |
| [`force`](#force) | `{Boolean}` | `false` | Overwrites files already in `compilation.assets` (usually added by other plugins/loaders). |
| [`flatten`](#flatten) | `{Boolean}` | `false` | Removes all directory references and only copies file names. |
Expand Down Expand Up @@ -277,6 +278,41 @@ module.exports = {
};
```

#### `filter`

Type: `Function`
Default: `undefined`

> ℹ️ To ignore files by path please use the [`globOptions.ignore`]((#globoptions) option.
**webpack.config.js**

```js
const fs = require('fs').promise;

module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: 'public/**/*',
filter: async (resourcePath) => {
const data = await fs.promises.readFile(resourcePath);
const content = data.toString();

if (content === 'my-custom-content') {
return false;
}

return true;
},
},
],
}),
],
};
```

#### `toType`

Type: `String`
Expand Down
3 changes: 3 additions & 0 deletions src/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
"globOptions": {
"type": "object"
},
"filter": {
"instanceof": "Function"
},
"toType": {
"enum": ["dir", "file", "template"]
},
Expand Down
62 changes: 38 additions & 24 deletions src/processPattern.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,46 @@ export default async function processPattern(globalRef, pattern) {
return Promise.resolve();
}

return (
paths
// Exclude directories
.filter((item) => item.dirent.isFile())
.map((item) => {
const from = item.path;

logger.debug(`found ${from}`);

// `globby`/`fast-glob` return the relative path when the path contains special characters on windows
const absoluteFrom = path.resolve(pattern.context, from);
const relativeFrom = pattern.flatten
? path.basename(absoluteFrom)
: path.relative(pattern.context, absoluteFrom);
let webpackTo =
pattern.toType === 'dir'
? path.join(pattern.to, relativeFrom)
: pattern.to;

if (path.isAbsolute(webpackTo)) {
webpackTo = path.relative(output, webpackTo);
const filteredPaths = (
await Promise.all(
paths.map(async (item) => {
// Exclude directories
if (!item.dirent.isFile()) {
return false;
}

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

return { absoluteFrom, relativeFrom, webpackTo };
return isFiltered ? item : false;
}

return item;
})
);
)
).filter((item) => item);

return filteredPaths.map((item) => {
const from = item.path;

logger.debug(`found ${from}`);

// `globby`/`fast-glob` return the relative path when the path contains special characters on windows
const absoluteFrom = path.resolve(pattern.context, from);
const relativeFrom = pattern.flatten
? path.basename(absoluteFrom)
: path.relative(pattern.context, absoluteFrom);
let webpackTo =
pattern.toType === 'dir'
? path.join(pattern.to, relativeFrom)
: pattern.to;

if (path.isAbsolute(webpackTo)) {
webpackTo = path.relative(output, webpackTo);
}

logger.log(`determined that '${from}' should write to '${webpackTo}'`);

return { absoluteFrom, relativeFrom, webpackTo };
});
}
33 changes: 19 additions & 14 deletions test/__snapshots__/validate-options.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ exports[`validate options should throw an error on the "options" option with "{"
exports[`validate options should throw an error on the "patterns" option with "" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns should be an array:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "patterns" option with "[""]" value 1`] = `
Expand All @@ -37,6 +37,11 @@ exports[`validate options should throw an error on the "patterns" option with "[
- options.patterns[0].from should be an non-empty string."
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","filter":"test"}]" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns[0].filter should be an instance of function."
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","context":"context","cacheTransform":{"foo":"bar"}}]" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns[0].cacheTransform has an unknown property 'foo'. These properties are valid:
Expand Down Expand Up @@ -72,7 +77,7 @@ exports[`validate options should throw an error on the "patterns" option with "[
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","context":"context"}]" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns[0] should be one of these:
non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }
non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }
Details:
* options.patterns[0].cacheTransform should be one of these:
boolean | string | object { directory?, keys? }
Expand Down Expand Up @@ -112,71 +117,71 @@ exports[`validate options should throw an error on the "patterns" option with "[
exports[`validate options should throw an error on the "patterns" option with "{}" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns should be an array:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "patterns" option with "true" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns should be an array:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "patterns" option with "true" value 2`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns should be an array:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "patterns" option with "undefined" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options misses the property 'patterns'. Should be:
[non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)"
`;
56 changes: 56 additions & 0 deletions test/filter-option.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import fs from 'fs';

import { runEmit } from './helpers/run';

describe('"filter" option', () => {
it('should work, copy files and filter some of them', (done) => {
runEmit({
expectedAssetKeys: [
'.dottedfile',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
],
patterns: [
{
from: 'directory',
filter: (resourcePath) => {
if (/directoryfile\.txt$/.test(resourcePath)) {
return false;
}

return true;
},
},
],
})
.then(done)
.catch(done);
});

it('should work, copy files and filter some of them using async function', (done) => {
runEmit({
expectedAssetKeys: [
'.dottedfile',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
],
patterns: [
{
from: 'directory',
filter: async (resourcePath) => {
const data = await fs.promises.readFile(resourcePath);
const content = data.toString();

if (content === 'new') {
return false;
}

return true;
},
},
],
})
.then(done)
.catch(done);
});
});
12 changes: 12 additions & 0 deletions test/validate-options.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ describe('validate options', () => {
},
},
],
[
{
from: 'test.txt',
filter: () => true,
},
],
],
failure: [
// eslint-disable-next-line no-undefined
Expand Down Expand Up @@ -247,6 +253,12 @@ describe('validate options', () => {
noErrorOnMissing: 'true',
},
],
[
{
from: 'test.txt',
filter: 'test',
},
],
],
},
options: {
Expand Down

0 comments on commit 1496f85

Please sign in to comment.