diff --git a/README.md b/README.md index f0f575c8..ca1073e5 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ module.exports = { | [`cacheTransform`](#cacheTransform) | `{Boolean\|String\|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). | +| [`info`](#info) | `{Object\|Function}` | `undefined` | Allows to add assets info. | #### `from` @@ -773,6 +774,51 @@ module.exports = { }; ``` +#### `info` + +Type: `Object|Function` +Default: `undefined` + +Allows to add assets info. + +**webpack.config.js** + +```js +module.exports = { + plugins: [ + new CopyPlugin({ + patterns: [ + "relative/path/to/file.ext", + { + from: "**/*", + // Terser skip this file for minimization + info: { minimized: true }, + }, + ], + }), + ], +}; +``` + +**webpack.config.js** + +```js +module.exports = { + plugins: [ + new CopyPlugin({ + patterns: [ + "relative/path/to/file.ext", + { + from: "**/*", + // Terser skip this file for minimization + info: (file) => ({ minimized: true }), + }, + ], + }), + ], +}; +``` + ### Options | Name | Type | Default | Description | diff --git a/src/index.js b/src/index.js index f46255ba..93de4c56 100644 --- a/src/index.js +++ b/src/index.js @@ -360,6 +360,10 @@ class CopyPlugin { sourceFilename, filename, force: pattern.force, + info: + typeof pattern.info === "function" + ? pattern.info(file) || {} + : pattern.info || {}, }; // If this came from a glob or dir, add it to the file dependencies @@ -731,7 +735,10 @@ class CopyPlugin { `force updating '${filename}' from '${absoluteFilename}' to compilation assets, because it already exists...` ); - compilation.updateAsset(filename, source, info); + compilation.updateAsset(filename, source, { + ...info, + ...asset.info, + }); logger.log( `force updated '${filename}' from '${absoluteFilename}' to compilation assets, because it already exists` @@ -747,17 +754,20 @@ class CopyPlugin { return; } - logger.log( - `writing '${filename}' from '${absoluteFilename}' to compilation assets...` - ); - const info = { copied: true, sourceFilename }; if (asset.immutable) { info.immutable = true; } - compilation.emitAsset(filename, source, info); + logger.log( + `writing '${filename}' from '${absoluteFilename}' to compilation assets...` + ); + + compilation.emitAsset(filename, source, { + ...info, + ...asset.info, + }); logger.log( `written '${filename}' from '${absoluteFilename}' to compilation assets` diff --git a/src/options.json b/src/options.json index 0e3ed49b..667b3a5a 100644 --- a/src/options.json +++ b/src/options.json @@ -33,6 +33,16 @@ "force": { "type": "boolean" }, + "info": { + "anyOf": [ + { + "type": "object" + }, + { + "instanceof": "Function" + } + ] + }, "flatten": { "type": "boolean" }, diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap index 992c663d..c4631c18 100644 --- a/test/__snapshots__/validate-options.test.js.snap +++ b/test/__snapshots__/validate-options.test.js.snap @@ -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?, filter?, 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?, info?, 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`] = ` @@ -37,6 +37,32 @@ 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":"dir","info":"string"}]" 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?, filter?, toType?, force?, info?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? } + Details: + * options.patterns[0].info should be one of these: + object { … } | function + Details: + * options.patterns[0].info should be an object: + object { … } + * options.patterns[0].info should be an instance of function." +`; + +exports[`validate options should throw an error on the "patterns" option with "[{"from":"dir","info":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] should be one of these: + non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? } + Details: + * options.patterns[0].info should be one of these: + object { … } | function + Details: + * options.patterns[0].info should be an object: + object { … } + * options.patterns[0].info should be an instance of function." +`; + 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." @@ -77,7 +103,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?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? } + non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? } Details: * options.patterns[0].cacheTransform should be one of these: boolean | string | object { directory?, keys? } @@ -96,7 +122,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":true,"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?, filter?, toType?, force?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? } + non-empty string | object { from, to?, context?, globOptions?, filter?, toType?, force?, info?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? } Details: * options.patterns[0].to should be one of these: string | function @@ -124,71 +150,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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, 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?, filter?, 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?, info?, flatten?, transform?, cacheTransform?, transformPath?, noErrorOnMissing? }, ...] (should not have fewer than 1 item)" `; diff --git a/test/info-option.test.js b/test/info-option.test.js new file mode 100644 index 00000000..622c2f54 --- /dev/null +++ b/test/info-option.test.js @@ -0,0 +1,110 @@ +import { run, runEmit } from "./helpers/run"; + +describe("info option", () => { + it('should work without "info" option', (done) => { + runEmit({ + expectedAssetKeys: ["file.txt"], + patterns: [ + { + from: "file.txt", + }, + ], + }) + .then(done) + .catch(done); + }); + + it('should work when "info" option is a object', (done) => { + run({ + expectedAssetKeys: ["file.txt"], + patterns: [ + { + from: "file.txt", + info: { test: true }, + }, + ], + }) + .then(({ compilation }) => { + expect(compilation.assetsInfo.get("file.txt").test).toBe(true); + }) + .then(done) + .catch(done); + }); + + it('should work when "info" option is a object and "force" option is true', (done) => { + const expectedAssetKeys = ["file.txt"]; + + run({ + preCopy: { + additionalAssets: [ + { name: "file.txt", data: "Content", info: { custom: true } }, + ], + }, + expectedAssetKeys, + patterns: [ + { + from: "file.txt", + force: true, + info: { test: true }, + }, + ], + }) + .then(({ compilation }) => { + expect(compilation.assetsInfo.get("file.txt").test).toBe(true); + }) + .then(done) + .catch(done); + }); + + it('should work when "info" option is a function', (done) => { + run({ + expectedAssetKeys: ["file.txt"], + patterns: [ + { + from: "file.txt", + info: (file) => { + expect.assertions(4); + + const fileKeys = ["absoluteFilename", "sourceFilename", "filename"]; + + for (const key of fileKeys) { + expect(key in file).toBe(true); + } + + return { test: true }; + }, + }, + ], + }) + .then(({ compilation }) => { + expect(compilation.assetsInfo.get("file.txt").test).toBe(true); + }) + .then(done) + .catch(done); + }); + + it('should work when "info" option is a function and "force" option is true', (done) => { + const expectedAssetKeys = ["file.txt"]; + + run({ + preCopy: { + additionalAssets: [ + { name: "file.txt", data: "Content", info: { custom: true } }, + ], + }, + expectedAssetKeys, + patterns: [ + { + from: "file.txt", + force: true, + info: () => ({ test: true }), + }, + ], + }) + .then(({ compilation }) => { + expect(compilation.assetsInfo.get("file.txt").test).toBe(true); + }) + .then(done) + .catch(done); + }); +}); diff --git a/test/validate-options.test.js b/test/validate-options.test.js index 9686377e..7c6b7875 100644 --- a/test/validate-options.test.js +++ b/test/validate-options.test.js @@ -144,6 +144,16 @@ describe("validate options", () => { filter: () => true, }, ], + [ + { + from: "test.txt", + info: { custom: true }, + }, + { + from: "test.txt", + info: () => ({ custom: true }), + }, + ], ], failure: [ // eslint-disable-next-line no-undefined @@ -155,6 +165,18 @@ describe("validate options", () => { [], [""], [{}], + [ + { + from: "dir", + info: "string", + }, + ], + [ + { + from: "dir", + info: true, + }, + ], [ { from: "",