Skip to content

Commit 29254e3

Browse files
authoredMay 15, 2020
feat: implement the directory option for the cacheTransform option
BREAKING CHANGE: the `cacheTransform` option should have only `directory` and `keys` properties when it is an object
1 parent bebafcf commit 29254e3

8 files changed

+559
-140
lines changed
 

‎README.md

+145-15
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,19 @@ module.exports = {
7474

7575
### Patterns
7676

77-
| Name | Type | Default | Description |
78-
| :-------------------------------------: | :-----------------: | :---------------------------------------------: | :---------------------------------------------------------------------------------------------------- |
79-
| [`from`](#from) | `{String}` | `undefined` | Glob or path from where we сopy files. |
80-
| [`to`](#to) | `{String}` | `compiler.options.output` | Output path. |
81-
| [`context`](#context) | `{String}` | `options.context \|\| compiler.options.context` | A path that determines how to interpret the `from` path. |
82-
| [`globOptions`](#globoptions) | `{Object}` | `undefined` | [Options][glob-options] passed to the glob pattern matching library including `ignore` option. |
83-
| [`toType`](#totype) | `{String}` | `undefined` | Determinate what is `to` option - directory, file or template. |
84-
| [`force`](#force) | `{Boolean}` | `false` | Overwrites files already in `compilation.assets` (usually added by other plugins/loaders). |
85-
| [`flatten`](#flatten) | `{Boolean}` | `false` | Removes all directory references and only copies file names. |
86-
| [`transform`](#transform) | `{Function}` | `undefined` | Allows to modify the file contents. |
87-
| [`cacheTransform`](#cacheTransform) | `{Boolean\|Object}` | `false` | Enable `transform` caching. You can use `{ cache: { key: 'my-cache-key' } }` to invalidate the cache. |
88-
| [`transformPath`](#transformpath) | `{Function}` | `undefined` | Allows to modify the writing path. |
89-
| [`noErrorOnMissing`](#noerroronmissing) | `{Boolean}` | `false` | Doesn't generate an error on missing file(s). |
77+
| Name | Type | Default | Description |
78+
| :-------------------------------------: | :-------------------------: | :---------------------------------------------: | :---------------------------------------------------------------------------------------------------- |
79+
| [`from`](#from) | `{String}` | `undefined` | Glob or path from where we сopy files. |
80+
| [`to`](#to) | `{String}` | `compiler.options.output` | Output path. |
81+
| [`context`](#context) | `{String}` | `options.context \|\| compiler.options.context` | A path that determines how to interpret the `from` path. |
82+
| [`globOptions`](#globoptions) | `{Object}` | `undefined` | [Options][glob-options] passed to the glob pattern matching library including `ignore` option. |
83+
| [`toType`](#totype) | `{String}` | `undefined` | Determinate what is `to` option - directory, file or template. |
84+
| [`force`](#force) | `{Boolean}` | `false` | Overwrites files already in `compilation.assets` (usually added by other plugins/loaders). |
85+
| [`flatten`](#flatten) | `{Boolean}` | `false` | Removes all directory references and only copies file names. |
86+
| [`transform`](#transform) | `{Function}` | `undefined` | Allows to modify the file contents. |
87+
| [`cacheTransform`](#cacheTransform) | `{Boolean\|String\|Object}` | `false` | Enable `transform` caching. You can use `{ cache: { key: 'my-cache-key' } }` to invalidate the cache. |
88+
| [`transformPath`](#transformpath) | `{Function}` | `undefined` | Allows to modify the writing path. |
89+
| [`noErrorOnMissing`](#noerroronmissing) | `{Boolean}` | `false` | Doesn't generate an error on missing file(s). |
9090

9191
#### `from`
9292

@@ -456,12 +456,16 @@ module.exports = {
456456

457457
#### `cacheTransform`
458458

459-
Type: `Boolean|Object`
459+
Type: `Boolean|String|Object`
460460
Default: `false`
461461

462-
Enable/disable `transform` caching. You can use `{ cacheTransform: { key: 'my-cache-key' } }` to invalidate the cache.
462+
Enable/disable and configure caching.
463463
Default path to cache directory: `node_modules/.cache/copy-webpack-plugin`.
464464

465+
##### `Boolean`
466+
467+
Enables/Disable `transform` caching.
468+
465469
**webpack.config.js**
466470

467471
```js
@@ -483,6 +487,132 @@ module.exports = {
483487
};
484488
```
485489

490+
##### `String`
491+
492+
Enables `transform` caching and setup cache directory.
493+
494+
**webpack.config.js**
495+
496+
```js
497+
module.exports = {
498+
plugins: [
499+
new CopyPlugin({
500+
patterns: [
501+
{
502+
from: 'src/*.png',
503+
to: 'dest/',
504+
transform(content, path) {
505+
return optimize(content);
506+
},
507+
// Should be absolute
508+
cacheTransform: path.resolve(__dirname, 'cache-directory'),
509+
},
510+
],
511+
}),
512+
],
513+
};
514+
```
515+
516+
##### `Object`
517+
518+
Enables `transform` caching and setup cache directory and invalidation keys.
519+
520+
**webpack.config.js**
521+
522+
```js
523+
module.exports = {
524+
plugins: [
525+
new CopyPlugin({
526+
patterns: [
527+
{
528+
from: 'src/*.png',
529+
to: 'dest/',
530+
transform(content, path) {
531+
return optimize(content);
532+
},
533+
cacheTransform: {
534+
directory: path.resolve(__dirname, 'cache-directory'),
535+
keys: {
536+
// May be useful for invalidating cache based on external values
537+
// For example, you can invalid cache based on `process.version` - { node: process.version }
538+
key: 'value',
539+
},
540+
},
541+
},
542+
],
543+
}),
544+
],
545+
};
546+
```
547+
548+
You can setup invalidation keys using a function.
549+
550+
Simple function:
551+
552+
**webpack.config.js**
553+
554+
```js
555+
module.exports = {
556+
plugins: [
557+
new CopyPlugin({
558+
patterns: [
559+
{
560+
from: 'src/*.png',
561+
to: 'dest/',
562+
transform(content, path) {
563+
return optimize(content);
564+
},
565+
cacheTransform: {
566+
directory: path.resolve(__dirname, 'cache-directory'),
567+
keys: (defaultCacheKeys, absoluteFrom) => {
568+
const keys = getCustomCacheInvalidationKeysSync();
569+
570+
return {
571+
...defaultCacheKeys,
572+
keys,
573+
};
574+
},
575+
},
576+
},
577+
],
578+
}),
579+
],
580+
};
581+
```
582+
583+
Async function:
584+
585+
**webpack.config.js**
586+
587+
```js
588+
module.exports = {
589+
plugins: [
590+
new CopyPlugin({
591+
patterns: [
592+
{
593+
from: 'src/*.png',
594+
to: 'dest/',
595+
transform(content, path) {
596+
return optimize(content);
597+
},
598+
cacheTransform: {
599+
directory: path.resolve(__dirname, 'cache-directory'),
600+
keys: async (defaultCacheKeys, absoluteFrom) => {
601+
const keys = await getCustomCacheInvalidationKeysAsync();
602+
603+
return {
604+
...defaultCacheKeys,
605+
keys,
606+
};
607+
},
608+
},
609+
},
610+
],
611+
}),
612+
],
613+
};
614+
```
615+
486616
#### `transformPath`
487617

488618
Type: `Function`

‎src/options.json

+22-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,28 @@
3535
"type": "boolean"
3636
},
3737
{
38-
"type": "object"
38+
"type": "string"
39+
},
40+
{
41+
"type": "object",
42+
"additionalProperties": false,
43+
"properties": {
44+
"directory": {
45+
"type": "string",
46+
"absolutePath": true
47+
},
48+
"keys": {
49+
"anyOf": [
50+
{
51+
"type": "object",
52+
"additionalProperties": true
53+
},
54+
{
55+
"instanceof": "Function"
56+
}
57+
]
58+
}
59+
}
3960
}
4061
]
4162
},

‎src/postProcessPattern.js

+27-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import normalizePath from 'normalize-path';
1010

1111
import { RawSource } from 'webpack-sources';
1212

13-
import { name, version } from '../package.json';
13+
import { version } from '../package.json';
1414

1515
import { stat, readFile } from './utils/promisify';
1616

@@ -63,22 +63,33 @@ export default async function postProcessPattern(globalRef, pattern, file) {
6363
logger.log(`transforming content for '${file.absoluteFrom}'`);
6464

6565
if (pattern.cacheTransform) {
66-
if (!globalRef.cacheDir) {
67-
globalRef.cacheDir =
68-
findCacheDir({ name: 'copy-webpack-plugin' }) || os.tmpdir();
66+
const cacheDirectory = pattern.cacheTransform.directory
67+
? pattern.cacheTransform.directory
68+
: typeof pattern.cacheTransform === 'string'
69+
? pattern.cacheTransform
70+
: findCacheDir({ name: 'copy-webpack-plugin' }) || os.tmpdir();
71+
let defaultCacheKeys = {
72+
version,
73+
transform: pattern.transform,
74+
contentHash: crypto.createHash('md4').update(content).digest('hex'),
75+
};
76+
77+
if (typeof pattern.cacheTransform.keys === 'function') {
78+
defaultCacheKeys = await pattern.cacheTransform.keys(
79+
defaultCacheKeys,
80+
file.absoluteFrom
81+
);
82+
} else {
83+
defaultCacheKeys = {
84+
...defaultCacheKeys,
85+
...pattern.cacheTransform.keys,
86+
};
6987
}
7088

71-
const cacheKey = pattern.cacheTransform.key
72-
? pattern.cacheTransform.key
73-
: serialize({
74-
name,
75-
version,
76-
pattern,
77-
hash: crypto.createHash('md4').update(content).digest('hex'),
78-
});
89+
const cacheKeys = serialize(defaultCacheKeys);
7990

8091
try {
81-
const result = await cacache.get(globalRef.cacheDir, cacheKey);
92+
const result = await cacache.get(cacheDirectory, cacheKeys);
8293

8394
logger.debug(
8495
`getting cached transformation for '${file.absoluteFrom}'`
@@ -91,7 +102,7 @@ export default async function postProcessPattern(globalRef, pattern, file) {
91102
logger.debug(`caching transformation for '${file.absoluteFrom}'`);
92103

93104
content = await cacache
94-
.put(globalRef.cacheDir, cacheKey, content)
105+
.put(cacheDirectory, cacheKeys, content)
95106
.then(() => content);
96107
}
97108
} else {
@@ -138,14 +149,15 @@ export default async function postProcessPattern(globalRef, pattern, file) {
138149
const source = new RawSource(content);
139150

140151
// For old version webpack 4
152+
/* istanbul ignore if */
141153
if (typeof compilation.emitAsset !== 'function') {
142154
compilation.assets[targetPath] = source;
143155

144156
return;
145157
}
146158

147159
if (compilation.getAsset(targetPath)) {
148-
if (file.force) {
160+
if (pattern.force) {
149161
logger.log(
150162
`force updating '${file.webpackTo}' to compilation assets from '${file.absoluteFrom}'`
151163
);

‎src/processPattern.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,7 @@ export default async function processPattern(globalRef, pattern) {
3232
}
3333

3434
return paths.map((from) => {
35-
const file = {
36-
force: pattern.force,
37-
absoluteFrom: path.resolve(pattern.context, from),
38-
};
35+
const file = { absoluteFrom: path.resolve(pattern.context, from) };
3936

4037
file.relativeFrom = path.relative(pattern.context, file.absoluteFrom);
4138

‎src/utils/createPatternGlob.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ function createPatternGlob(pattern, globalRef) {
1717
const { logger, compilation } = globalRef;
1818

1919
// eslint-disable-next-line no-param-reassign
20-
pattern.globOptions = Object.assign(
21-
{
20+
pattern.globOptions = {
21+
...{
2222
cwd: pattern.context,
2323
followSymbolicLinks: true,
2424
},
25-
pattern.globOptions || {}
26-
);
25+
...(pattern.globOptions || {}),
26+
};
2727

2828
switch (pattern.fromType) {
2929
case 'dir':

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

+9-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ 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","to":"dir","context":"context","cacheTransform":{"foo":"bar"}}]" 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].cacheTransform has an unknown property 'foo'. These properties are valid:
43+
object { directory?, keys? }"
44+
`;
45+
4046
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","context":"context","flatten":"true"}]" value 1`] = `
4147
"Invalid options object. Copy Plugin has been initialized using an options object that does not match the API schema.
4248
- options.patterns[0].flatten should be a boolean."
@@ -69,11 +75,12 @@ exports[`validate options should throw an error on the "patterns" option with "[
6975
non-empty string | object { from, to?, context?, globOptions?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }
7076
Details:
7177
* options.patterns[0].cacheTransform should be one of these:
72-
boolean | object { }
78+
boolean | string | object { directory?, keys? }
7379
Details:
7480
* options.patterns[0].cacheTransform should be a boolean.
81+
* options.patterns[0].cacheTransform should be a string.
7582
* options.patterns[0].cacheTransform should be an object:
76-
object { }"
83+
object { directory?, keys? }"
7784
`;
7885
7986
exports[`validate options should throw an error on the "patterns" option with "[{"from":"test.txt","to":"dir","context":true}]" value 1`] = `

‎test/cacheTransform-option.test.js

+274-83
Large diffs are not rendered by default.

‎test/validate-options.test.js

+77-16
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
1-
import CopyPlugin from '../src/index';
2-
3-
// Todo remove after dorp node@6 support
4-
if (!Object.entries) {
5-
Object.entries = function entries(obj) {
6-
const ownProps = Object.keys(obj);
7-
let i = ownProps.length;
8-
const resArray = new Array(i);
1+
import os from 'os';
92

10-
// eslint-disable-next-line no-plusplus
11-
while (i--) {
12-
resArray[i] = [ownProps[i], obj[ownProps[i]]];
13-
}
3+
import findCacheDir from 'find-cache-dir';
144

15-
return resArray;
16-
};
17-
}
5+
import CopyPlugin from '../src/index';
186

197
describe('validate options', () => {
208
const tests = {
@@ -71,13 +59,76 @@ describe('validate options', () => {
7159
},
7260
},
7361
],
62+
[
63+
{
64+
from: 'test.txt',
65+
to: 'dir',
66+
context: 'context',
67+
cacheTransform:
68+
findCacheDir({ name: 'copy-webpack-plugin-a' }) || os.tmpdir(),
69+
},
70+
],
7471
[
7572
{
7673
from: 'test.txt',
7774
to: 'dir',
7875
context: 'context',
7976
cacheTransform: {
80-
foo: 'bar',
77+
keys: {
78+
foo: 'bar',
79+
},
80+
},
81+
},
82+
],
83+
[
84+
{
85+
from: 'test.txt',
86+
to: 'dir',
87+
context: 'context',
88+
cacheTransform: {
89+
keys: () => ({
90+
foo: 'bar',
91+
}),
92+
},
93+
},
94+
],
95+
[
96+
{
97+
from: 'test.txt',
98+
to: 'dir',
99+
context: 'context',
100+
cacheTransform: {
101+
keys: async () => ({
102+
foo: 'bar',
103+
}),
104+
},
105+
},
106+
],
107+
[
108+
{
109+
from: 'test.txt',
110+
to: 'dir',
111+
context: 'context',
112+
cacheTransform: {
113+
directory:
114+
findCacheDir({ name: 'copy-webpack-plugin-b' }) || os.tmpdir(),
115+
keys: {
116+
foo: 'bar',
117+
},
118+
},
119+
},
120+
],
121+
[
122+
{
123+
from: 'test.txt',
124+
to: 'dir',
125+
context: 'context',
126+
cacheTransform: {
127+
directory:
128+
findCacheDir({ name: 'copy-webpack-plugin-c' }) || os.tmpdir(),
129+
keys: () => ({
130+
foo: 'bar',
131+
}),
81132
},
82133
},
83134
],
@@ -150,6 +201,16 @@ describe('validate options', () => {
150201
cacheTransform: () => {},
151202
},
152203
],
204+
[
205+
{
206+
from: 'test.txt',
207+
to: 'dir',
208+
context: 'context',
209+
cacheTransform: {
210+
foo: 'bar',
211+
},
212+
},
213+
],
153214
[
154215
{
155216
from: 'test.txt',

0 commit comments

Comments
 (0)
Please sign in to comment.