diff --git a/README.md b/README.md index cf3e367f..48782730 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Or, in case of just a `from` with the default destination, you can also use a `{ |[`ignore`](#ignore)|`{Array}`|`[]`|Globs to ignore for this pattern| |`flatten`|`{Boolean}`|`false`|Removes all directory references and only copies file names.⚠️ If files have the same name, the result is non-deterministic| |[`transform`](#transform)|`{Function\|Promise}`|`(content, path) => content`|Function or Promise that modifies file contents before copying| +|[`transformPath`](#transformPath)|`{Function\|Promise}`|`(targetPath, sourcePath) => path`|Function or Promise that modifies file writing path| |[`cache`](#cache)|`{Boolean\|Object}`|`false`|Enable `transform` caching. You can use `{ cache: { key: 'my-cache-key' } }` to invalidate the cache| |[`context`](#context)|`{String}`|`options.context \|\| compiler.options.context`|A path that determines how to interpret the `from` path| @@ -234,6 +235,43 @@ and so on... ] ``` +### `transformPath` + +#### `{Function}` + +**webpack.config.js** +```js +[ + new CopyWebpackPlugin([ + { + from: 'src/*.png', + to: 'dest/', + transformPath (targetPath, absolutePath) { + return 'newPath'; + } + } + ], options) +] +``` + +#### `{Promise}` + +**webpack.config.js** +```js +[ + new CopyWebpackPlugin([ + { + from: 'src/*.png', + to: 'dest/', + transform (targePath, absolutePath) { + return Promise.resolve('newPath') + } + } + ], options) +] +``` + + ### `cache` **webpack.config.js** diff --git a/src/writeFile.js b/src/writeFile.js index 61557404..0091d98a 100644 --- a/src/writeFile.js +++ b/src/writeFile.js @@ -63,8 +63,6 @@ export default function writeFile(globalRef, pattern, file) { return content; }).then((content) => { - const hash = loaderUtils.getHashDigest(content); - if (pattern.toType === 'template') { info(`interpolating template '${file.webpackTo}' for '${file.relativeFrom}'`); @@ -84,6 +82,20 @@ export default function writeFile(globalRef, pattern, file) { } ); } + + return content; + }).then((content) => { + if (pattern.transformPath) { + return Promise.resolve( + pattern.transformPath(file.webpackTo, file.absoluteFrom) + ).then((newPath) => { + file.webpackTo = newPath; + }).then(() => content); + } + + return content; + }).then((content) => { + const hash = loaderUtils.getHashDigest(content); if (!copyUnmodified && written[file.absoluteFrom] && diff --git a/tests/index.js b/tests/index.js index 071ead0f..0cdf33bb 100644 --- a/tests/index.js +++ b/tests/index.js @@ -319,6 +319,57 @@ describe('apply function', () => { .catch(done); }); + it('can transform target path of every file in glob', (done) => { + runEmit({ + expectedAssetKeys: [ + '/some/path/(special-*file).txt.tst', + '/some/path/binextension.bin.tst', + '/some/path/file.txt.tst', + '/some/path/file.txt.gz.tst', + '/some/path/directoryfile.txt.tst', + '/some/path/nestedfile.txt.tst', + '/some/path/noextension.tst', + '/some/path/hello.txt.tst' + ], + patterns: [{ + from: '**/*', + transformPath: function(targetPath, absoluteFrom) { + expect(absoluteFrom).to.have.string(HELPER_DIR); + return '/some/path/' + path.basename(targetPath) + '.tst'; + } + }] + }) + .then(done) + .catch(done); + }); + + it('can transform target path of every file in glob after applying template', (done) => { + runEmit({ + expectedAssetKeys: [ + 'transformed/[!]/hello-d41d8c.txt', + 'transformed/[special?directory]/directoryfile-22af64.txt', + 'transformed/[special?directory]/(special-*file)-0bd650.txt', + 'transformed/[special?directory]/nested/nestedfile-d41d8c.txt', + 'transformed/binextension-d41d8c.bin', + 'transformed/file-22af64.txt', + 'transformed/file.txt-5b311c.gz', + 'transformed/directory/directoryfile-22af64.txt', + 'transformed/directory/nested/nestedfile-d41d8c.txt', + 'transformed/noextension-d41d8c' + ], + patterns: [{ + from: '**/*', + to: 'nested/[path][name]-[hash:6].[ext]', + transformPath: function(targetPath, absoluteFrom) { + expect(absoluteFrom).to.have.string(HELPER_DIR); + return targetPath.replace('nested/', 'transformed/'); + } + }] + }) + .then(done) + .catch(done); + }); + it('can use a glob to move multiple files in a different relative context to a non-root directory', (done) => { runEmit({ expectedAssetKeys: [ @@ -539,6 +590,23 @@ describe('apply function', () => { .catch(done); }); + it('can transform target path', (done) => { + runEmit({ + expectedAssetKeys: [ + 'subdir/test.txt' + ], + patterns: [{ + from: 'file.txt', + transformPath: function(targetPath, absoluteFrom) { + expect(absoluteFrom).to.equal(path.join(HELPER_DIR, 'file.txt')); + return targetPath.replace('file.txt', 'subdir/test.txt'); + } + }] + }) + .then(done) + .catch(done); + }); + it('warns when file not found', (done) => { runEmit({ expectedAssetKeys: [], @@ -570,6 +638,23 @@ describe('apply function', () => { .catch(done); }); + it('warns when tranformPath failed', (done) => { + runEmit({ + expectedAssetKeys: [], + expectedErrors: [ + 'a failure happened' + ], + patterns: [{ + from: 'file.txt', + transformPath: function() { + throw 'a failure happened'; + } + }] + }) + .then(done) + .catch(done); + }); + it('can use an absolute path to move a file to the root directory', (done) => { const absolutePath = path.resolve(HELPER_DIR, 'file.txt'); @@ -987,6 +1072,26 @@ describe('apply function', () => { .catch(done); }); + it('transformPath with promise', (done) => { + runEmit({ + expectedAssetKeys: [ + '/some/path/file.txt' + ], + patterns: [{ + from: 'file.txt', + transformPath: function(targetPath, absoluteFrom) { + expect(absoluteFrom).to.have.string(HELPER_DIR); + + return new Promise((resolve) => { + resolve('/some/path/' + path.basename(targetPath)); + }); + } + }] + }) + .then(done) + .catch(done); + }); + it('same file to multiple targets', (done) => { runEmit({ expectedAssetKeys: [ @@ -1022,6 +1127,25 @@ describe('apply function', () => { .catch(done); }); + it('can transform target path of every file in directory', (done) => { + runEmit({ + expectedAssetKeys: [ + '/some/path/.dottedfile', + '/some/path/directoryfile.txt', + '/some/path/nestedfile.txt' + ], + patterns: [{ + from: 'directory', + transformPath: function(targetPath, absoluteFrom) { + expect(absoluteFrom).to.have.string(path.join(HELPER_DIR, 'directory')); + return '/some/path/' + path.basename(targetPath); + } + }] + }) + .then(done) + .catch(done); + }); + it('can move a directory\'s contents to the root directory using from with special characters', (done) => { runEmit({ expectedAssetKeys: [