Skip to content

Commit

Permalink
feat: implement support for array values in from property. Closes w…
Browse files Browse the repository at this point in the history
  • Loading branch information
orangy committed Nov 23, 2021
1 parent 568a66c commit 3cb90e1
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 30 deletions.
31 changes: 28 additions & 3 deletions README.md
Expand Up @@ -83,7 +83,7 @@ module.exports = {

| Name | Type | Default | Description |
| :-------------------------------------: | :------------------: | :---------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`from`](#from) | `{String}` | `undefined` | Glob or path from where we copy files. |
| [`from`](#from) | `{String\|Array}` | `undefined` | Glob or path from where we copy files, or array of such values. |
| [`to`](#to) | `{String\|Function}` | `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. |
Expand All @@ -98,12 +98,12 @@ module.exports = {

#### `from`

Type: `String`
Type: `String or Array of strings`
Default: `undefined`

Glob or path from where we copy files.
Globs accept [fast-glob pattern-syntax](https://github.com/mrmlnc/fast-glob#pattern-syntax).
Glob can only be a `string`.
Glob can only be a non-empty `string` or non-empty array of non-empty strings.

> ⚠️ Don't use directly `\\` in `from` option if it is a `glob` (i.e `path\to\file.ext`) option because on UNIX the backslash is a valid character inside a path component, i.e., it's not a separator.
> On Windows, the forward slash and the backward slash are both separators.
Expand Down Expand Up @@ -177,6 +177,31 @@ module.exports = {
The `context` behaves differently depending on what the `from` is (`glob`, `file` or `dir`).
More [`examples`](#examples)

##### Using arrays in `from`

When `from` is specified as an array, it behaves the same as if all array elements were processed separately
using shared options, such as `transform`. The only exception is `transformAll` which receives assets from processed
array elements in a single call, thus allowing to process group of specific files at once.

```js
module.exports = {
plugins: [
new CopyPlugin({
from: ["file.txt", "directory/directoryfile.txt"],
to: "file.txt",
transformAll(assets) {
const result = assets.sort().reduce((accumulator, asset) => {
const content = asset.sourceFilename;
accumulator = `${accumulator}${content}::`;
return accumulator;
}, "");
return result;
},
}),
],
};
```

#### `to`

Type: `String|Function`
Expand Down
44 changes: 35 additions & 9 deletions src/index.js
Expand Up @@ -633,15 +633,41 @@ class CopyPlugin {
let assets;

try {
assets = await CopyPlugin.runPattern(
globby,
compiler,
compilation,
logger,
cache,
item,
index
);
if (item.from instanceof Array) {
if (!item.from.every((from) => typeof from === "string")) {
compilation.errors.push(
new Error(
`Invalid "pattern.from": ${item.from}, every element should be a string"`
)
);
}

assets = [].concat(
...(await Promise.all(
item.from.map(async (from) =>
CopyPlugin.runPattern(
globby,
compiler,
compilation,
logger,
cache,
{ ...item, from },
index
)
)
))
);
} else {
assets = await CopyPlugin.runPattern(
globby,
compiler,
compilation,
logger,
cache,
item,
index
);
}
} catch (error) {
compilation.errors.push(error);

Expand Down
26 changes: 19 additions & 7 deletions src/options.json
Expand Up @@ -5,13 +5,25 @@
"additionalProperties": false,
"properties": {
"from": {
"type": "string",
"description": "Glob or path from where we copy files.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#from",
"minLength": 1
"oneOf": [
{
"type": "string",
"minLength": 1
},
{
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"minItems": 1
}
],
"description": "Glob or path from where we copy files, or array of paths or globs.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#from"
},
"to": {
"anyOf": [
"oneOf": [
{
"type": "string"
},
Expand Down Expand Up @@ -58,7 +70,7 @@
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#priority"
},
"info": {
"anyOf": [
"oneOf": [
{
"type": "object"
},
Expand All @@ -72,7 +84,7 @@
"transform": {
"description": "Allows to modify the file contents.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#transform",
"anyOf": [
"oneOf": [
{
"instanceof": "Function"
},
Expand Down
46 changes: 35 additions & 11 deletions test/__snapshots__/validate-options.test.js.snap
Expand Up @@ -38,9 +38,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":"","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].from should be a non-empty string.
-> Glob or path from where we copy files.
-> Read more at https://github.com/webpack-contrib/copy-webpack-plugin#from"
- options.patterns[0].from should be a non-empty string."
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":"dir","info":"string"}]" value 1`] = `
Expand Down Expand Up @@ -165,25 +163,51 @@ exports[`validate options should throw an error on the "patterns" option with "[
* options.patterns[0].to should be an instance of function."
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":["test1.txt",1],"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].from[1] should be a non-empty string."
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":[]}]" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns[0].from should be a non-empty array."
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":{"glob":"**/*","dot":false},"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].from should be a non-empty string.
-> Glob or path from where we copy files.
-> Read more at https://github.com/webpack-contrib/copy-webpack-plugin#from"
- options.patterns[0] should be one of these:
non-empty string | object { from, to?, context?, globOptions?, filter?, transformAll?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }
Details:
* options.patterns[0].from should be one of these:
non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Glob or path from where we copy files, or array of paths or globs.
-> Read more at https://github.com/webpack-contrib/copy-webpack-plugin#from
Details:
* options.patterns[0].from should be a non-empty string.
* options.patterns[0].from should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":true,"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].from should be a non-empty string.
-> Glob or path from where we copy files.
-> Read more at https://github.com/webpack-contrib/copy-webpack-plugin#from"
- options.patterns[0] should be one of these:
non-empty string | object { from, to?, context?, globOptions?, filter?, transformAll?, toType?, force?, priority?, info?, transform?, transformPath?, noErrorOnMissing? }
Details:
* options.patterns[0].from should be one of these:
non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Glob or path from where we copy files, or array of paths or globs.
-> Read more at https://github.com/webpack-contrib/copy-webpack-plugin#from
Details:
* options.patterns[0].from should be a non-empty string.
* options.patterns[0].from should be an array:
[non-empty string, ...] (should not have fewer than 1 item)"
`;
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[0] misses the property 'from'. Should be:
non-empty string
-> Glob or path from where we copy files.
non-empty string | [non-empty string, ...] (should not have fewer than 1 item)
-> Glob or path from where we copy files, or array of paths or globs.
-> Read more at https://github.com/webpack-contrib/copy-webpack-plugin#from"
`;
Expand Down
33 changes: 33 additions & 0 deletions test/from-option.test.js
Expand Up @@ -21,6 +21,18 @@ describe("from option", () => {
.then(done)
.catch(done);
});
it("should copy an array of files", (done) => {
runEmit({
expectedAssetKeys: ["file.txt", "directoryfile.txt"],
patterns: [
{
from: ["file.txt", "directory/directoryfile.txt"],
},
],
})
.then(done)
.catch(done);
});

it('should copy a file when "from" an absolute path', (done) => {
runEmit({
Expand Down Expand Up @@ -154,6 +166,27 @@ describe("from option", () => {
.catch(done);
});

it('should copy files when "from" is an array of directories', (done) => {
runEmit({
expectedAssetKeys: [
"file.txt",
"nesteddir/deepnesteddir/deepnesteddir.txt",
"nesteddir/nestedfile.txt",
".dottedfile",
"directoryfile.txt",
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
patterns: [
{
from: ["dir (86)", "directory"],
},
],
})
.then(done)
.catch(done);
});

it('should copy files when "from" is relative path to context', (done) => {
runEmit({
expectedAssetKeys: [
Expand Down
24 changes: 24 additions & 0 deletions test/transform-option.test.js
Expand Up @@ -29,6 +29,30 @@ describe("transform option", () => {
.catch(done);
});

it('should transform files when "from" is an array of files', (done) => {
runEmit({
expectedAssetKeys: ["file.txt", "directoryfile.txt"],
expectedAssetContent: {
"file.txt": "newchanged",
"directoryfile.txt": "newchanged",
},
patterns: [
{
from: ["file.txt", "directory/directoryfile.txt"],
transform: {
transformer(content, absoluteFrom) {
expect(absoluteFrom.includes(FIXTURES_DIR)).toBe(true);

return `${content}changed`;
},
},
},
],
})
.then(done)
.catch(done);
});

it('should transform target path of every when "from" is a directory', (done) => {
runEmit({
expectedAssetKeys: [
Expand Down
27 changes: 27 additions & 0 deletions test/transformAll-option.test.js
Expand Up @@ -51,6 +51,33 @@ describe("transformAll option", () => {
.catch(done);
});

it('should transform files when when "from" is an array of files', (done) => {
runEmit({
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
"file.txt": "directory/directoryfile.txt::file.txt::",
},
patterns: [
{
from: ["file.txt", "directory/directoryfile.txt"],
to: "file.txt",
transformAll(assets) {
const result = assets.sort().reduce((accumulator, asset) => {
const content = asset.sourceFilename;
// eslint-disable-next-line no-param-reassign
accumulator = `${accumulator}${content}::`;
return accumulator;
}, "");

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

it("should transform files when async function used", (done) => {
runEmit({
expectedAssetKeys: ["file.txt"],
Expand Down
21 changes: 21 additions & 0 deletions test/validate-options.test.js
Expand Up @@ -59,6 +59,14 @@ describe("validate options", () => {
transform: () => {},
},
],
[
{
from: ["test1.txt", "test2.txt"],
to: "dir",
context: "context",
transform: () => {},
},
],
[
{
from: "test.txt",
Expand Down Expand Up @@ -165,6 +173,11 @@ describe("validate options", () => {
info: "string",
},
],
[
{
from: [],
},
],
[
{
from: "dir",
Expand Down Expand Up @@ -233,6 +246,14 @@ describe("validate options", () => {
transform: true,
},
],
[
{
from: ["test1.txt", 1],
to: "dir",
context: "context",
transform: () => {},
},
],
[
{
from: {
Expand Down

0 comments on commit 3cb90e1

Please sign in to comment.