Skip to content

Commit

Permalink
feat: implement the noErrorOnMissing option (#475)
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi committed May 15, 2020
1 parent 8e5bc1b commit e3803ce
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 21 deletions.
47 changes: 35 additions & 12 deletions README.md
Expand Up @@ -74,18 +74,19 @@ module.exports = {

### Patterns

| Name | Type | Default | Description |
| :---------------------------------: | :-----------------: | :---------------------------------------------: | :---------------------------------------------------------------------------------------------------- |
| [`from`](#from) | `{String}` | `undefined` | Glob or path from where we сopy files. |
| [`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. |
| [`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. |
| [`transform`](#transform) | `{Function}` | `undefined` | Allows to modify the file contents. |
| [`cacheTransform`](#cacheTransform) | `{Boolean\|Object}` | `false` | Enable `transform` caching. You can use `{ cache: { key: 'my-cache-key' } }` to invalidate the cache. |
| [`transformPath`](#transformpath) | `{Function}` | `undefined` | Allows to modify the writing path. |
| Name | Type | Default | Description |
| :-------------------------------------: | :-----------------: | :---------------------------------------------: | :---------------------------------------------------------------------------------------------------- |
| [`from`](#from) | `{String}` | `undefined` | Glob or path from where we сopy files. |
| [`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. |
| [`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. |
| [`transform`](#transform) | `{Function}` | `undefined` | Allows to modify the file contents. |
| [`cacheTransform`](#cacheTransform) | `{Boolean\|Object}` | `false` | Enable `transform` caching. You can use `{ cache: { key: 'my-cache-key' } }` to invalidate the cache. |
| [`transformPath`](#transformpath) | `{Function}` | `undefined` | Allows to modify the writing path. |
| [`noErrorOnMissing`](#noerroronmissing) | `{Boolean}` | `false` | Doesn't generate an error on missing file(s). |

#### `from`

Expand Down Expand Up @@ -533,6 +534,28 @@ module.exports = {
};
```

### `noErrorOnMissing`

Type: `Boolean`
Default: `false`

Doesn't generate an error on missing file(s);

```js
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, 'missing-file.txt'),
noErrorOnMissing: true,
},
],
}),
],
};
```

### Options

| Name | Type | Default | Description |
Expand Down
7 changes: 7 additions & 0 deletions src/options.json
Expand Up @@ -2,6 +2,7 @@
"definitions": {
"ObjectPattern": {
"type": "object",
"additionalProperties": false,
"properties": {
"from": {
"type": "string",
Expand All @@ -13,6 +14,9 @@
"context": {
"type": "string"
},
"globOptions": {
"type": "object"
},
"toType": {
"enum": ["dir", "file", "template"]
},
Expand All @@ -37,6 +41,9 @@
},
"transformPath": {
"instanceof": "Function"
},
"noErrorOnMissing": {
"type": "boolean"
}
},
"required": ["from"]
Expand Down
2 changes: 2 additions & 0 deletions src/preProcessPattern.js
Expand Up @@ -20,6 +20,8 @@ export default async function preProcessPattern(globalRef, pattern) {
pattern.context = path.join(context, pattern.context);
}

pattern.noErrorOnMissing = pattern.noErrorOnMissing || false;

// Todo remove this in next major
const isToDirectory =
path.extname(pattern.to) === '' || pattern.to.slice(-1) === path.sep;
Expand Down
5 changes: 5 additions & 0 deletions src/processPattern.js
Expand Up @@ -8,6 +8,7 @@ import createPatternGlob from './utils/createPatternGlob';

export default async function processPattern(globalRef, pattern) {
const { logger, output, compilation } = globalRef;

createPatternGlob(pattern, globalRef);

logger.log(
Expand All @@ -17,6 +18,10 @@ export default async function processPattern(globalRef, pattern) {
const paths = await globby(pattern.glob, pattern.globOptions);

if (paths.length === 0) {
if (pattern.noErrorOnMissing) {
return Promise.resolve();
}

const newWarning = new Error(
`unable to locate '${pattern.from}' at '${pattern.absoluteFrom}'`
);
Expand Down
13 changes: 10 additions & 3 deletions src/utils/createPatternGlob.js
Expand Up @@ -3,8 +3,6 @@ import path from 'path';
import normalizePath from 'normalize-path';
import globParent from 'glob-parent';

/* eslint-disable no-param-reassign */

function getAbsoluteContext(context) {
const result = normalizePath(path.resolve(context));

Expand All @@ -18,6 +16,7 @@ function getAbsoluteContext(context) {
function createPatternGlob(pattern, globalRef) {
const { logger, compilation } = globalRef;

// eslint-disable-next-line no-param-reassign
pattern.globOptions = Object.assign(
{
cwd: pattern.context,
Expand All @@ -30,8 +29,10 @@ function createPatternGlob(pattern, globalRef) {
case 'dir':
logger.debug(`determined '${pattern.absoluteFrom}' is a directory`);
logger.debug(`add ${pattern.absoluteFrom} as contextDependencies`);

compilation.contextDependencies.add(pattern.absoluteFrom);

/* eslint-disable no-param-reassign */
pattern.context = pattern.absoluteFrom;
pattern.glob = path.posix.join(
getAbsoluteContext(pattern.absoluteFrom),
Expand All @@ -42,20 +43,23 @@ function createPatternGlob(pattern, globalRef) {
if (typeof pattern.globOptions.dot === 'undefined') {
pattern.globOptions.dot = true;
}
/* eslint-enable no-param-reassign */

break;

case 'file':
logger.debug(`determined '${pattern.absoluteFrom}' is a file`);
logger.debug(`add ${pattern.absoluteFrom} as fileDependencies`);

compilation.fileDependencies.add(pattern.absoluteFrom);

/* eslint-disable no-param-reassign */
pattern.context = path.dirname(pattern.absoluteFrom);
pattern.glob = getAbsoluteContext(pattern.absoluteFrom);

if (typeof pattern.globOptions.dot === 'undefined') {
pattern.globOptions.dot = true;
}
/* eslint-enable no-param-reassign */

break;

Expand All @@ -68,15 +72,18 @@ function createPatternGlob(pattern, globalRef) {
);

logger.debug(`add ${contextDependencies} as contextDependencies`);

compilation.contextDependencies.add(contextDependencies);

/* eslint-disable no-param-reassign */
pattern.fromType = 'glob';
pattern.glob = path.isAbsolute(pattern.fromOrigin)
? pattern.fromOrigin
: path.posix.join(
getAbsoluteContext(pattern.context),
pattern.fromOrigin
);
/* eslint-enable no-param-reassign */
}

return pattern;
Expand Down
15 changes: 10 additions & 5 deletions test/__snapshots__/validate-options.test.js.snap
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?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, 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 @@ -27,6 +27,11 @@ exports[`validate options should throw an error on the "patterns" option with "[
- options.patterns should be an non-empty array."
`;
exports[`validate options should throw an error on the "patterns" option with "[{"from":"","to":"dir","context":"context","noErrorOnMissing":"true"}]" value 1`] = `
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
- options.patterns[0].noErrorOnMissing should be a boolean."
`;
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 an non-empty string."
Expand Down Expand Up @@ -61,7 +66,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?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, }
non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }
Details:
* options.patterns[0].cacheTransform should be one of these:
boolean | object { … }
Expand Down Expand Up @@ -100,19 +105,19 @@ 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?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, 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?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, 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?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, }, ...] (should not have fewer than 1 item)"
[non-empty string | object { from, to?, context?, globOptions?, 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`] = `
Expand Down
1 change: 0 additions & 1 deletion test/globOptions-option.test.js
Expand Up @@ -245,7 +245,6 @@ describe('globOptions ignore option', () => {
],
patterns: [
{
ignore: ['file.*'],
from: 'file.txt',
globOptions: {
ignore: ['**/file.*'],
Expand Down
51 changes: 51 additions & 0 deletions test/noErrorOnMissing.test.js
@@ -0,0 +1,51 @@
import { runEmit } from './helpers/run';

describe('noErrorOnMissing option', () => {
describe('is a file', () => {
it('should work', (done) => {
runEmit({
expectedAssetKeys: [],
patterns: [
{
from: 'unknown.unknown',
noErrorOnMissing: true,
},
],
})
.then(done)
.catch(done);
});
});

describe('is a directory', () => {
it('should work', (done) => {
runEmit({
expectedAssetKeys: [],
patterns: [
{
from: 'unknown',
noErrorOnMissing: true,
},
],
})
.then(done)
.catch(done);
});
});

describe('is a glob', () => {
it('should work', (done) => {
runEmit({
expectedAssetKeys: [],
patterns: [
{
from: '*.unknown',
noErrorOnMissing: true,
},
],
})
.then(done)
.catch(done);
});
});
});
9 changes: 9 additions & 0 deletions test/validate-options.test.js
Expand Up @@ -58,6 +58,7 @@ describe('validate options', () => {
transform: () => {},
cacheTransform: true,
transformPath: () => {},
noErrorOnMissing: true,
},
],
[
Expand Down Expand Up @@ -175,6 +176,14 @@ describe('validate options', () => {
context: 'context',
},
],
[
{
from: '',
to: 'dir',
context: 'context',
noErrorOnMissing: 'true',
},
],
],
},
options: {
Expand Down

0 comments on commit e3803ce

Please sign in to comment.