Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: webpack-contrib/copy-webpack-plugin
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 40e6ae58bd836699d6a9cb9abba3bd1733a955ce
Choose a base ref
...
head repository: webpack-contrib/copy-webpack-plugin
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 797a006521521603d5cdf34b1041a59ade07dd25
Choose a head ref

Commits on Aug 27, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    13cebc1 View commit details

Commits on Aug 29, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    714af2f View commit details
  2. chore(release): 6.0.4

    alexander-akait committed Aug 29, 2020
    Copy the full SHA
    d8b4a72 View commit details

Commits on Aug 31, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    09b1dc9 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1496f85 View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a1989d5 View commit details
  4. chore(release): 6.1.0

    alexander-akait committed Aug 31, 2020
    Copy the full SHA
    d8f8fce View commit details

Commits on Sep 18, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    808c10a View commit details
  2. chore(release): 6.1.1

    alexander-akait committed Sep 18, 2020
    Copy the full SHA
    fd03615 View commit details
  3. test: cache (#529)

    evilebottnawi authored Sep 18, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    58a574b View commit details

Commits on Sep 21, 2020

  1. refactor: code

    evilebottnawi authored Sep 21, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f2ac763 View commit details

Commits on Oct 2, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    6f2f455 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9151610 View commit details
  3. Copy the full SHA
    4699111 View commit details

Commits on Oct 9, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    dcb5b29 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    892a21e View commit details
  3. Copy the full SHA
    fb60b9b View commit details

Commits on Oct 13, 2020

  1. Copy the full SHA
    bd09a24 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    36ff46a View commit details

Commits on Oct 21, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    93936a0 View commit details

Commits on Oct 22, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c892451 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    db2e3bf View commit details
  3. refactor: code (#543)

    evilebottnawi authored Oct 22, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b971374 View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f98be10 View commit details

Commits on Oct 23, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b827c6e View commit details

Commits on Oct 28, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    87a8486 View commit details

Commits on Nov 3, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    bc2833e View commit details
  2. Copy the full SHA
    fa5aa1b View commit details

Commits on Nov 13, 2020

  1. fix: watching (#555)

    evilebottnawi authored Nov 13, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    b996923 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c92b5ee View commit details
  3. chore(release): 6.3.1

    alexander-akait committed Nov 13, 2020
    Copy the full SHA
    5215721 View commit details

Commits on Nov 19, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    7b58fd9 View commit details
  2. chore(release): 6.3.2

    alexander-akait committed Nov 19, 2020
    Copy the full SHA
    7167645 View commit details

Commits on Dec 7, 2020

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9bc5416 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    db53937 View commit details
  3. Copy the full SHA
    6c11e21 View commit details

Commits on Dec 9, 2020

  1. refactor: next

    BREAKING CHANGE: yes
    cap-Bernardito authored Dec 9, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4cea28b View commit details

Commits on Dec 10, 2020

  1. refactor: code

    cap-Bernardito authored Dec 10, 2020

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c6f68a5 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5d5635f View commit details
  3. chore(release): 7.0.0

    alexander-akait committed Dec 10, 2020
    Copy the full SHA
    46af20a View commit details

Commits on Jan 25, 2021

  1. docs: remove $ from readme (#578)

    For easy copying of the plugin installation command
    feisk authored Jan 25, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ed7069b View commit details

Commits on Feb 27, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f5e661b View commit details

Commits on Mar 1, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8d5ab9a View commit details

Commits on Mar 3, 2021

  1. refactor: template strings in to option (#589)

    BREAKING CHANGE: placeholders for the `to` option were changed
    cap-Bernardito authored Mar 3, 2021

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f2c1bc8 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a9b06a6 View commit details

Commits on Mar 4, 2021

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ea610bc View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d68a8eb View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9e12a33 View commit details
  4. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    83601dc View commit details
  5. Copy the full SHA
    1787d2d View commit details
Showing with 25,249 additions and 11,545 deletions.
  1. +1 −1 .eslintrc.js
  2. +1 −1 .github/ISSUE_TEMPLATE.md
  3. +6 −6 .github/workflows/nodejs.yml
  4. +2 −1 .gitignore
  5. +1 −0 .husky/.gitignore
  6. +4 −0 .husky/commit-msg
  7. +4 −0 .husky/pre-commit
  8. +0 −1 .prettierrc.js
  9. +123 −1 CHANGELOG.md
  10. +438 −177 README.md
  11. +2 −2 babel.config.js
  12. +1 −1 commitlint.config.js
  13. +8 −8 globalSetup.js
  14. +0 −6 husky.config.js
  15. +2 −2 jest.config.js
  16. +2 −2 lint-staged.config.js
  17. +20,488 −8,790 package-lock.json
  18. +32 −38 package.json
  19. +1 −1 src/cjs.js
  20. +762 −73 src/index.js
  21. +45 −16 src/options.json
  22. +0 −132 src/postProcessPattern.js
  23. +0 −69 src/preProcessPattern.js
  24. +0 −62 src/processPattern.js
  25. +0 −82 src/utils/createPatternGlob.js
  26. +0 −4 src/utils/isTemplateLike.js
  27. +762 −190 test/CopyPlugin.test.js
  28. +394 −46 test/__snapshots__/CopyPlugin.test.js.snap
  29. +21 −0 test/__snapshots__/transformAll-option.test.js.snap
  30. +76 −34 test/__snapshots__/validate-options.test.js.snap
  31. +0 −486 test/cacheTransform-option.test.js
  32. +4 −4 test/cjs.test.js
  33. +55 −55 test/context-option.test.js
  34. +56 −0 test/filter-option.test.js
  35. +0 −147 test/flatten-option.test.js
  36. +99 −90 test/force-option.test.js
  37. +191 −184 test/from-option.test.js
  38. +144 −95 test/globOptions-option.test.js
  39. +33 −0 test/helpers/BreakContenthashPlugin.js
  40. +4 −4 test/helpers/ChildCompiler.js
  41. +7 −9 test/helpers/PreCopyPlugin.js
  42. +2 −3 test/helpers/compile.js
  43. +3 −0 test/helpers/enter-with-asset-modules.js
  44. +21 −16 test/helpers/getCompiler.js
  45. +4 −4 test/helpers/index.js
  46. +3 −3 test/helpers/readAsset.js
  47. +2 −2 test/helpers/readAssets.js
  48. +1 −1 test/helpers/removeIllegalCharacterForWindows.js
  49. +30 −19 test/helpers/run.js
  50. +112 −0 test/info-option.test.js
  51. +11 −11 test/noErrorOnMissing.test.js
  52. +57 −0 test/priority-option.test.js
  53. +664 −144 test/to-option.test.js
  54. +36 −35 test/toType-option.test.js
  55. +142 −82 test/transform-option.test.js
  56. +221 −0 test/transformAll-option.test.js
  57. +0 −273 test/transformPath-option.test.js
  58. +171 −132 test/validate-options.test.js
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: ['@webpack-contrib/eslint-config-webpack', 'prettier'],
extends: ["@webpack-contrib/eslint-config-webpack", "prettier"],
};
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
You arrived at this template because you felt none of the other options
matched the kind of issue you'd like to report. Please use this opportunity to
tell us about your particular type of issue so we can try to accomodate
tell us about your particular type of issue so we can try to accommodate
similar issues in the future.
PLEASE do note, if you're using this to report an issue already covered by the
12 changes: 6 additions & 6 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
@@ -29,10 +29,10 @@ jobs:
with:
fetch-depth: 0

- name: Use Node.js ${{ env.node-version }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ env.node-version }}
node-version: ${{ matrix.node-version }}

- name: Use latest NPM
run: sudo npm i -g npm
@@ -43,8 +43,8 @@ jobs:
- name: Lint
run: npm run lint

# - name: Security audit
# run: npm run security
- name: Security audit
run: npm run security

- name: Check commit message
uses: wagoid/commitlint-github-action@v1
@@ -55,8 +55,8 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [10.x, 12.x, 14.x]
webpack-version: [latest, next]
node-version: [12.x, 14.x, 16.x]
webpack-version: [latest]

runs-on: ${{ matrix.os }}

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@ npm-debug.log*
/local
/reports
/node_modules
/test/fixtures/\[special\?directory\]
/test/fixtures/\[special\$directory\]
/test/outputs

.DS_Store
Thumbs.db
1 change: 1 addition & 0 deletions .husky/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_
4 changes: 4 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install commitlint --edit $1
4 changes: 4 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install lint-staged
1 change: 0 additions & 1 deletion .prettierrc.js

This file was deleted.

124 changes: 123 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -2,6 +2,128 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

### [9.0.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v9.0.0...v9.0.1) (2021-06-25)

### Chore

* update `serialize-javascript`

## [9.0.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v8.1.1...v9.0.0) (2021-05-21)


### ⚠ BREAKING CHANGES

* minimum supported `Node.js` version is `12.13.0`

### [8.1.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v8.1.0...v8.1.1) (2021-04-06)


### Bug Fixes

* `stage` for processing assets ([#600](https://github.com/webpack-contrib/copy-webpack-plugin/issues/600)) ([#601](https://github.com/webpack-contrib/copy-webpack-plugin/issues/601)) ([d8fa32a](https://github.com/webpack-contrib/copy-webpack-plugin/commit/d8fa32ac1a9e3d42c6257ac7aab6c43cc1bed791))

## [8.1.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v8.0.0...v8.1.0) (2021-03-22)


### Features

* added the `transformAll` option ([#596](https://github.com/webpack-contrib/copy-webpack-plugin/issues/596)) ([dde71f0](https://github.com/webpack-contrib/copy-webpack-plugin/commit/dde71f01417b9291c7029a3876e043d76beb9e8d))

## [8.0.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v7.0.0...v8.0.0) (2021-03-04)


### ⚠ BREAKING CHANGES

* logic for some placeholders was changed:
- `[hash]` and `[fullhash]` works as in webpack (i.e. it is `hash` of build, not content hash of file), to migrate change `[name].[hash].[ext]` to `[name].[contenthash][ext]`
- `[ext]` doesn't require `.` (dot) before, i.e. change `[name].[ext]` to `[name][ext]`
- `[<hashType>:contenthash:<digestType>:<length>]` and `[<hashType>:hash:<digestType>:<length>]` is not supported anymore, you can use `output.hashDigest`, `output.hashDigestLength` and `output.hashFunction` options to setup it
- `[N]` was removed in favor of using the `to` option as a function
- `[folder]` was removed
- `[emoji]` was removed

### Features

* added `priority` option ([#590](https://github.com/webpack-contrib/copy-webpack-plugin/issues/590)) ([ea610bc](https://github.com/webpack-contrib/copy-webpack-plugin/commit/ea610bc1a0fa7097f291b0928fb28eb96b0f03af))

## [7.0.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.4.0...v7.0.0) (2020-12-10)


### ⚠ BREAKING CHANGES

* minimum supported webpack version is `5`
* the `flatten` option was removed in favor `[name].[ext]` value for the `to` option,
* the `transformPath` option was removed in favor `Function` type of the `to` option, look at [examples](https://github.com/webpack-contrib/copy-webpack-plugin#function)
* the `cacheTransform` option was removed in favor `Object` type of the `transform` option, look at [examples](https://github.com/webpack-contrib/copy-webpack-plugin#transform)
* migration on the `compilation.hooks.processAssets` hook
* empty filtered paths throw an error, you can disable this behaviour using the `noErrorOnMissing` option

## [6.4.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.3.2...v6.4.0) (2020-12-07)


### Features

* added the `info` option ([db53937](https://github.com/webpack-contrib/copy-webpack-plugin/commit/db53937016b7dbf494bc728f00242cd26541f6a3))
* added type `Function` for the `to` option ([#563](https://github.com/webpack-contrib/copy-webpack-plugin/issues/563)) ([9bc5416](https://github.com/webpack-contrib/copy-webpack-plugin/commit/9bc541694c0d0975c59586cedfea4a51d11f5278))

### [6.3.2](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.3.1...v6.3.2) (2020-11-19)


### Bug Fixes

* watching directories ([#558](https://github.com/webpack-contrib/copy-webpack-plugin/issues/558)) ([7b58fd9](https://github.com/webpack-contrib/copy-webpack-plugin/commit/7b58fd9a89e9b29578b30cb3119453e78e036ec2))

### [6.3.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.3.0...v6.3.1) (2020-11-13)


### Bug Fixes

* watching ([#555](https://github.com/webpack-contrib/copy-webpack-plugin/issues/555)) ([b996923](https://github.com/webpack-contrib/copy-webpack-plugin/commit/b9969230321df68ed235ed1861729837f234750e))

## [6.3.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.2.1...v6.3.0) (2020-11-03)


### Features

* added the `sourceFilename` info (original source filename) to assets info ([#542](https://github.com/webpack-contrib/copy-webpack-plugin/issues/542)) ([db2e3bf](https://github.com/webpack-contrib/copy-webpack-plugin/commit/db2e3bfae9322592c3a9af1e45d25df165b6b4e0))
* persistent cache between compilations (webpack@5 only) ([#541](https://github.com/webpack-contrib/copy-webpack-plugin/issues/541)) ([c892451](https://github.com/webpack-contrib/copy-webpack-plugin/commit/c8924512a34391ce92715a2b61fc4b0b91a9e10f))

### [6.2.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.2.0...v6.2.1) (2020-10-09)

### Chore

* update `schema-utils`

## [6.2.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.1.1...v6.2.0) (2020-10-02)


### Features

* use webpack input filesystem (only webpack@5) ([#531](https://github.com/webpack-contrib/copy-webpack-plugin/issues/531)) ([6f2f455](https://github.com/webpack-contrib/copy-webpack-plugin/commit/6f2f455b9411ac69ef6aa3b953474f1d7fa23808))

### [6.1.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.1.0...v6.1.1) (2020-09-18)

### Chore

* updated `serialize-javascript`

## [6.1.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.0.4...v6.1.0) (2020-08-31)


### Features

* added `filter` option ([#524](https://github.com/webpack-contrib/copy-webpack-plugin/issues/524)) ([1496f85](https://github.com/webpack-contrib/copy-webpack-plugin/commit/1496f85d2fa5e87dccd0cda92b1343c649f3e5bd))
* added the `copied` flag to asset info ([09b1dc9](https://github.com/webpack-contrib/copy-webpack-plugin/commit/09b1dc995e476bb7090ebb2c2cbd4b5ebedeaa79))
* added the `immutable` flag to asset info with hash in name ([#525](https://github.com/webpack-contrib/copy-webpack-plugin/issues/525)) ([a1989d5](https://github.com/webpack-contrib/copy-webpack-plugin/commit/a1989d59b8b0a8caf0b826016e20c82a9ac38aa1))
* **webpack@5:** improve stats output for `copied` files

### [6.0.4](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.0.3...v6.0.4) (2020-08-29)


### Bug Fixes

* compatibility with webpack@5 ([#522](https://github.com/webpack-contrib/copy-webpack-plugin/issues/522)) ([714af2f](https://github.com/webpack-contrib/copy-webpack-plugin/commit/714af2ff72da168ec7456ac9a93ef4f4486be21e))

### [6.0.3](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v6.0.2...v6.0.3) (2020-06-30)


@@ -33,7 +155,7 @@ All notable changes to this project will be documented in this file. See [standa
* minimum supported Node.js version is `10.13`,
* the plugin now accepts an object, you should change `new CopyPlugin(patterns, options)` to `new CopyPlugin({ patterns, options })`
* migrate on `compilation.additionalAssets` hook
* the `ignore` option was removed in favor `globOptions.ignore`
* the `ignore` option (which accepted [micromatch](https://github.com/micromatch/micromatch) syntax) was removed in favor `globOptions.ignore` (which accepts [fast-glob pattern-syntax](https://github.com/mrmlnc/fast-glob#pattern-syntax))
* the `test` option was removed in favor the `transformPath` option
* the `cache` option was renamed to the `cacheTransform` option, `cacheTransform` option should have only `directory` and `keys` properties when it is an object
* global `context` and `ignore` options were removed in favor `patten.context` and `pattern.globOptions.ignore` options
615 changes: 438 additions & 177 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -7,10 +7,10 @@ module.exports = (api) => {
return {
presets: [
[
'@babel/preset-env',
"@babel/preset-env",
{
targets: {
node: '10.13.0',
node: "12.13.0",
},
},
],
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
extends: ["@commitlint/config-conventional"],
};
16 changes: 8 additions & 8 deletions globalSetup.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
const path = require('path');
const fs = require('fs');
const path = require("path");
const fs = require("fs");

// eslint-disable-next-line import/no-extraneous-dependencies
const mkdirp = require('mkdirp');
const mkdirp = require("mkdirp");

const removeIllegalCharacterForWindows = require('./test/helpers/removeIllegalCharacterForWindows');
const removeIllegalCharacterForWindows = require("./test/helpers/removeIllegalCharacterForWindows");

const baseDir = path.resolve(__dirname, 'test/fixtures');
const baseDir = path.resolve(__dirname, "test/fixtures");

const specialFiles = {
'[special?directory]/nested/nestedfile.txt': '',
'[special?directory]/(special-*file).txt': 'special',
'[special?directory]/directoryfile.txt': 'new',
"[special$directory]/nested/nestedfile.txt": "",
"[special$directory]/(special-*file).txt": "special",
"[special$directory]/directoryfile.txt": "new",
};

module.exports = () => {
6 changes: 0 additions & 6 deletions husky.config.js

This file was deleted.

4 changes: 2 additions & 2 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
testEnvironment: 'node',
globalSetup: '<rootDir>/globalSetup.js',
testEnvironment: "node",
globalSetup: "<rootDir>/globalSetup.js",
};
4 changes: 2 additions & 2 deletions lint-staged.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
'*.js': ['prettier --write', 'eslint --fix'],
'*.{json,md,yml,css,ts}': ['prettier --write'],
"*.js": ["eslint --fix", "prettier --write"],
"*.{json,md,yml,css,ts}": ["prettier --write"],
};
29,278 changes: 20,488 additions & 8,790 deletions package-lock.json

Large diffs are not rendered by default.

70 changes: 32 additions & 38 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "copy-webpack-plugin",
"version": "6.0.3",
"version": "9.0.1",
"description": "Copy files && directories with webpack",
"license": "MIT",
"repository": "webpack-contrib/copy-webpack-plugin",
@@ -13,15 +13,15 @@
},
"main": "dist/cjs.js",
"engines": {
"node": ">= 10.13.0"
"node": ">= 12.13.0"
},
"scripts": {
"start": "npm run build -- -w",
"clean": "del-cli dist",
"prebuild": "npm run clean",
"build": "cross-env NODE_ENV=production babel src -d dist --copy-files",
"commitlint": "commitlint --from=master",
"security": "npm audit",
"security": "npm audit --production",
"lint:prettier": "prettier --list-different .",
"lint:js": "eslint --cache .",
"lint": "npm-run-all -l -p \"lint:**\"",
@@ -30,55 +30,49 @@
"test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
"pretest": "npm run lint",
"test": "npm run test:coverage",
"prepare": "npm run build",
"release": "standard-version",
"defaults": "webpack-defaults"
"prepare": "husky install && npm run build",
"release": "standard-version"
},
"files": [
"dist"
],
"peerDependencies": {
"webpack": "^4.37.0 || ^5.0.0"
"webpack": "^5.1.0"
},
"dependencies": {
"cacache": "^15.0.4",
"fast-glob": "^3.2.4",
"find-cache-dir": "^3.3.1",
"glob-parent": "^5.1.1",
"globby": "^11.0.1",
"loader-utils": "^2.0.0",
"fast-glob": "^3.2.5",
"glob-parent": "^6.0.0",
"globby": "^11.0.3",
"normalize-path": "^3.0.0",
"p-limit": "^3.0.1",
"schema-utils": "^2.7.0",
"serialize-javascript": "^4.0.0",
"webpack-sources": "^1.4.3"
"p-limit": "^3.1.0",
"schema-utils": "^3.0.0",
"serialize-javascript": "^6.0.0"
},
"devDependencies": {
"@babel/cli": "^7.10.1",
"@babel/core": "^7.10.2",
"@babel/preset-env": "^7.10.2",
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"@webpack-contrib/defaults": "^6.3.0",
"@babel/cli": "^7.14.5",
"@babel/core": "^7.14.6",
"@babel/preset-env": "^7.14.7",
"@commitlint/cli": "^12.1.4",
"@commitlint/config-conventional": "^12.1.4",
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
"babel-jest": "^26.0.1",
"chokidar": "^3.4.0",
"cross-env": "^7.0.2",
"del": "^5.1.0",
"del-cli": "^3.0.1",
"eslint": "^7.2.0",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.21.2",
"husky": "^4.2.5",
"babel-jest": "^27.0.5",
"cross-env": "^7.0.3",
"del": "^6.0.0",
"del-cli": "^4.0.0",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.23.4",
"file-loader": "^6.2.0",
"husky": "^6.0.0",
"is-gzip": "^2.0.0",
"jest": "^26.0.1",
"lint-staged": "^10.2.11",
"memfs": "^3.2.0",
"jest": "^27.0.5",
"lint-staged": "^11.0.0",
"memfs": "^3.2.2",
"mkdirp": "^1.0.4",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.5",
"standard-version": "^8.0.0",
"webpack": "^4.43.0"
"prettier": "^2.3.1",
"standard-version": "^9.3.0",
"webpack": "^5.40.0"
},
"keywords": [
"webpack",
2 changes: 1 addition & 1 deletion src/cjs.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
const plugin = require('./index');
const plugin = require("./index");

module.exports = plugin.default;
835 changes: 762 additions & 73 deletions src/index.js

Large diffs are not rendered by default.

61 changes: 45 additions & 16 deletions src/options.json
Original file line number Diff line number Diff line change
@@ -9,50 +9,79 @@
"minLength": 1
},
"to": {
"type": "string"
"anyOf": [
{
"type": "string"
},
{
"instanceof": "Function"
}
]
},
"context": {
"type": "string"
},
"globOptions": {
"type": "object"
},
"filter": {
"instanceof": "Function"
},
"transformAll": {
"instanceof": "Function"
},
"toType": {
"enum": ["dir", "file", "template"]
},
"force": {
"type": "boolean"
},
"flatten": {
"type": "boolean"
},
"transform": {
"instanceof": "Function"
"priority": {
"type": "number"
},
"cacheTransform": {
"info": {
"anyOf": [
{
"type": "boolean"
"type": "object"
},
{
"type": "string"
"instanceof": "Function"
}
]
},
"transform": {
"anyOf": [
{
"instanceof": "Function"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"directory": {
"type": "string",
"absolutePath": true
"transformer": {
"instanceof": "Function"
},
"keys": {
"cache": {
"anyOf": [
{
"type": "object",
"additionalProperties": true
"type": "boolean"
},
{
"instanceof": "Function"
"type": "object",
"additionalProperties": false,
"properties": {
"keys": {
"anyOf": [
{
"type": "object",
"additionalProperties": true
},
{
"instanceof": "Function"
}
]
}
}
}
]
}
132 changes: 0 additions & 132 deletions src/postProcessPattern.js

This file was deleted.

69 changes: 0 additions & 69 deletions src/preProcessPattern.js

This file was deleted.

62 changes: 0 additions & 62 deletions src/processPattern.js

This file was deleted.

82 changes: 0 additions & 82 deletions src/utils/createPatternGlob.js

This file was deleted.

4 changes: 0 additions & 4 deletions src/utils/isTemplateLike.js

This file was deleted.

952 changes: 762 additions & 190 deletions test/CopyPlugin.test.js

Large diffs are not rendered by default.

440 changes: 394 additions & 46 deletions test/__snapshots__/CopyPlugin.test.js.snap

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions test/__snapshots__/transformAll-option.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`cache should work with the "memory" cache: assets 1`] = `
Object {
"file.txt": "new::directory/nested/deep-nested/deepnested.txt::directory/nested/nestedfile.txt::",
}
`;

exports[`cache should work with the "memory" cache: assets 2`] = `
Object {
"file.txt": "new::directory/nested/deep-nested/deepnested.txt::directory/nested/nestedfile.txt::",
}
`;

exports[`cache should work with the "memory" cache: errors 1`] = `Array []`;

exports[`cache should work with the "memory" cache: errors 2`] = `Array []`;

exports[`cache should work with the "memory" cache: warnings 1`] = `Array []`;

exports[`cache should work with the "memory" cache: warnings 2`] = `Array []`;
110 changes: 76 additions & 34 deletions test/__snapshots__/validate-options.test.js.snap

Large diffs are not rendered by default.

486 changes: 0 additions & 486 deletions test/cacheTransform-option.test.js

This file was deleted.

8 changes: 4 additions & 4 deletions test/cjs.test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import src from '../src';
import cjs from '../src/cjs';
import src from "../src/index";
import cjs from "../src/cjs";

describe('cjs', () => {
it('should exported', () => {
describe("cjs", () => {
it("should exported", () => {
expect(cjs).toEqual(src);
});
});
110 changes: 55 additions & 55 deletions test/context-option.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import path from 'path';
import path from "path";

import { runEmit } from './helpers/run';
import { runEmit } from "./helpers/run";

const FIXTURES_DIR = path.join(__dirname, 'fixtures');
const FIXTURES_DIR = path.join(__dirname, "fixtures");

describe('context option', () => {
describe("context option", () => {
it('should work when "from" is a file and "context" is a relative path', (done) => {
runEmit({
expectedAssetKeys: ['directoryfile.txt'],
expectedAssetKeys: ["directoryfile.txt"],
patterns: [
{
from: 'directoryfile.txt',
context: 'directory',
from: "directoryfile.txt",
context: "directory",
},
],
})
@@ -21,11 +21,11 @@ describe('context option', () => {

it('should work when "from" is a directory and "context" is a relative path', (done) => {
runEmit({
expectedAssetKeys: ['deep-nested/deepnested.txt', 'nestedfile.txt'],
expectedAssetKeys: ["deep-nested/deepnested.txt", "nestedfile.txt"],
patterns: [
{
from: 'nested',
context: 'directory',
from: "nested",
context: "directory",
},
],
})
@@ -36,13 +36,13 @@ describe('context option', () => {
it('should work when "from" is a glob and "context" is a relative path', (done) => {
runEmit({
expectedAssetKeys: [
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
patterns: [
{
from: 'nested/**/*',
context: 'directory',
from: "nested/**/*",
context: "directory",
},
],
})
@@ -52,11 +52,11 @@ describe('context option', () => {

it('should work when "from" is a file and "context" is an absolute path', (done) => {
runEmit({
expectedAssetKeys: ['directoryfile.txt'],
expectedAssetKeys: ["directoryfile.txt"],
patterns: [
{
from: 'directoryfile.txt',
context: path.join(FIXTURES_DIR, 'directory'),
from: "directoryfile.txt",
context: path.join(FIXTURES_DIR, "directory"),
},
],
})
@@ -66,11 +66,11 @@ describe('context option', () => {

it('should work when "from" is a directory and "context" is an absolute path', (done) => {
runEmit({
expectedAssetKeys: ['deep-nested/deepnested.txt', 'nestedfile.txt'],
expectedAssetKeys: ["deep-nested/deepnested.txt", "nestedfile.txt"],
patterns: [
{
from: 'nested',
context: path.join(FIXTURES_DIR, 'directory'),
from: "nested",
context: path.join(FIXTURES_DIR, "directory"),
},
],
})
@@ -81,13 +81,13 @@ describe('context option', () => {
it('should work when "from" is a glob and "context" is an absolute path', (done) => {
runEmit({
expectedAssetKeys: [
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
patterns: [
{
from: 'nested/**/*',
context: path.join(FIXTURES_DIR, 'directory'),
from: "nested/**/*",
context: path.join(FIXTURES_DIR, "directory"),
},
],
})
@@ -97,11 +97,11 @@ describe('context option', () => {

it('should work when "from" is a file and "context" with special characters', (done) => {
runEmit({
expectedAssetKeys: ['directoryfile.txt'],
expectedAssetKeys: ["directoryfile.txt"],
patterns: [
{
from: 'directoryfile.txt',
context: '[special?directory]',
from: "directoryfile.txt",
context: "[special$directory]",
},
],
})
@@ -112,15 +112,15 @@ describe('context option', () => {
it('should work when "from" is a directory and "context" with special characters', (done) => {
runEmit({
expectedAssetKeys: [
'directoryfile.txt',
'(special-*file).txt',
'nested/nestedfile.txt',
"directoryfile.txt",
"(special-*file).txt",
"nested/nestedfile.txt",
],
patterns: [
{
// Todo strange behavour when you use `FIXTURES_DIR`, need investigate for next major release
from: '.',
context: '[special?directory]',
from: ".",
context: "[special$directory]",
},
],
})
@@ -131,14 +131,14 @@ describe('context option', () => {
it('should work when "from" is a glob and "context" with special characters', (done) => {
runEmit({
expectedAssetKeys: [
'directoryfile.txt',
'(special-*file).txt',
'nested/nestedfile.txt',
"directoryfile.txt",
"(special-*file).txt",
"nested/nestedfile.txt",
],
patterns: [
{
from: '**/*',
context: '[special?directory]',
from: "**/*",
context: "[special$directory]",
},
],
})
@@ -148,11 +148,11 @@ describe('context option', () => {

it('should work when "from" is a glob and "context" with special characters #2', (done) => {
runEmit({
expectedAssetKeys: ['(special-*file).txt'],
expectedAssetKeys: ["(special-*file).txt"],
patterns: [
{
from: '\\(special-*file\\).txt',
context: '[special?directory]',
from: "\\(special-*file\\).txt",
context: "[special$directory]",
},
],
})
@@ -162,12 +162,12 @@ describe('context option', () => {

it('should work when "from" is a file and "to" is a directory', (done) => {
runEmit({
expectedAssetKeys: ['newdirectory/directoryfile.txt'],
expectedAssetKeys: ["newdirectory/directoryfile.txt"],
patterns: [
{
context: 'directory',
from: 'directoryfile.txt',
to: 'newdirectory',
context: "directory",
from: "directoryfile.txt",
to: "newdirectory",
},
],
})
@@ -178,14 +178,14 @@ describe('context option', () => {
it('should work when "from" is a directory and "to" is a directory', (done) => {
runEmit({
expectedAssetKeys: [
'newdirectory/deep-nested/deepnested.txt',
'newdirectory/nestedfile.txt',
"newdirectory/deep-nested/deepnested.txt",
"newdirectory/nestedfile.txt",
],
patterns: [
{
context: 'directory',
from: 'nested',
to: 'newdirectory',
context: "directory",
from: "nested",
to: "newdirectory",
},
],
})
@@ -196,15 +196,15 @@ describe('context option', () => {
it('should work when "from" is a glob and "to" is a directory', (done) => {
runEmit({
expectedAssetKeys: [
'nested/directoryfile.txt',
'nested/nested/deep-nested/deepnested.txt',
'nested/nested/nestedfile.txt',
"nested/directoryfile.txt",
"nested/nested/deep-nested/deepnested.txt",
"nested/nested/nestedfile.txt",
],
patterns: [
{
context: 'directory',
from: '**/*',
to: 'nested',
context: "directory",
from: "**/*",
to: "nested",
},
],
})
56 changes: 56 additions & 0 deletions test/filter-option.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import fs from "fs";

import { runEmit } from "./helpers/run";

describe('"filter" option', () => {
it("should work, copy files and filter some of them", (done) => {
runEmit({
expectedAssetKeys: [
".dottedfile",
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
patterns: [
{
from: "directory",
filter: (resourcePath) => {
if (/directoryfile\.txt$/.test(resourcePath)) {
return false;
}

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

it("should work, copy files and filter some of them using async function", (done) => {
runEmit({
expectedAssetKeys: [
".dottedfile",
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
patterns: [
{
from: "directory",
filter: async (resourcePath) => {
const data = await fs.promises.readFile(resourcePath);
const content = data.toString();

if (content === "new") {
return false;
}

return true;
},
},
],
})
.then(done)
.catch(done);
});
});
147 changes: 0 additions & 147 deletions test/flatten-option.test.js

This file was deleted.

189 changes: 99 additions & 90 deletions test/force-option.test.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { runForce } from './helpers/run';
import { runForce } from "./helpers/run";

describe('force option', () => {
describe('is not specified', () => {
describe("force option", () => {
describe("is not specified", () => {
it('should not overwrite a file already in the compilation by default when "from" is a file', (done) => {
runForce({
existingAssets: ['file.txt'],
expectedAssetKeys: ['file.txt'],
additionalAssets: [{ name: "file.txt", data: "existing" }],
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
'file.txt': 'existing',
"file.txt": "existing",
},
patterns: [
{
from: 'file.txt',
from: "file.txt",
},
],
})
@@ -21,27 +21,27 @@ describe('force option', () => {

it('should not overwrite files already in the compilation when "from" is a directory', (done) => {
runForce({
existingAssets: [
'.dottedfile',
'directoryfile.txt',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
additionalAssets: [
{ name: ".dottedfile", data: "existing" },
{ name: "directoryfile.txt", data: "existing" },
{ name: "nested/deep-nested/deepnested.txt", data: "existing" },
{ name: "nested/nestedfile.txt", data: "existing" },
],
expectedAssetKeys: [
'.dottedfile',
'directoryfile.txt',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
".dottedfile",
"directoryfile.txt",
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
expectedAssetContent: {
'.dottedfile': 'existing',
'nested/deep-nested/deepnested.txt': 'existing',
'nested/nestedfile.txt': 'existing',
'directoryfile.txt': 'existing',
".dottedfile": "existing",
"nested/deep-nested/deepnested.txt": "existing",
"nested/nestedfile.txt": "existing",
"directoryfile.txt": "existing",
},
patterns: [
{
from: 'directory',
from: "directory",
},
],
})
@@ -51,24 +51,27 @@ describe('force option', () => {

it('should not overwrite files already in the compilation when "from" is a glob', (done) => {
runForce({
existingAssets: [
'directory/directoryfile.txt',
'directory/nested/deep-nested/deepnested.txt',
'directory/nested/nestedfile.txt',
additionalAssets: [
{ name: "directory/directoryfile.txt", data: "existing" },
{
name: "directory/nested/deep-nested/deepnested.txt",
data: "existing",
},
{ name: "directory/nested/nestedfile.txt", data: "existing" },
],
expectedAssetKeys: [
'directory/directoryfile.txt',
'directory/nested/deep-nested/deepnested.txt',
'directory/nested/nestedfile.txt',
"directory/directoryfile.txt",
"directory/nested/deep-nested/deepnested.txt",
"directory/nested/nestedfile.txt",
],
expectedAssetContent: {
'directory/nested/deep-nested/deepnested.txt': 'existing',
'directory/nested/nestedfile.txt': 'existing',
'directory/directoryfile.txt': 'existing',
"directory/nested/deep-nested/deepnested.txt": "existing",
"directory/nested/nestedfile.txt": "existing",
"directory/directoryfile.txt": "existing",
},
patterns: [
{
from: 'directory/**/*',
from: "directory/**/*",
},
],
})
@@ -80,15 +83,15 @@ describe('force option', () => {
describe('is "false" (Boolean)', () => {
it('should not overwrite a file already in the compilation by default when "from" is a file', (done) => {
runForce({
existingAssets: ['file.txt'],
expectedAssetKeys: ['file.txt'],
additionalAssets: [{ name: "file.txt", data: "existing" }],
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
'file.txt': 'existing',
"file.txt": "existing",
},
patterns: [
{
force: false,
from: 'file.txt',
from: "file.txt",
},
],
})
@@ -98,28 +101,28 @@ describe('force option', () => {

it('should not overwrite files already in the compilation when "from" is a directory', (done) => {
runForce({
existingAssets: [
'.dottedfile',
'directoryfile.txt',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
additionalAssets: [
{ name: ".dottedfile", data: "existing" },
{ name: "directoryfile.txt", data: "existing" },
{ name: "nested/deep-nested/deepnested.txt", data: "existing" },
{ name: "nested/nestedfile.txt", data: "existing" },
],
expectedAssetKeys: [
'.dottedfile',
'directoryfile.txt',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
".dottedfile",
"directoryfile.txt",
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
expectedAssetContent: {
'.dottedfile': 'existing',
'nested/deep-nested/deepnested.txt': 'existing',
'nested/nestedfile.txt': 'existing',
'directoryfile.txt': 'existing',
".dottedfile": "existing",
"nested/deep-nested/deepnested.txt": "existing",
"nested/nestedfile.txt": "existing",
"directoryfile.txt": "existing",
},
patterns: [
{
force: false,
from: 'directory',
from: "directory",
},
],
})
@@ -129,25 +132,28 @@ describe('force option', () => {

it('should not overwrite files already in the compilation when "from" is a glob', (done) => {
runForce({
existingAssets: [
'directory/directoryfile.txt',
'directory/nested/deep-nested/deepnested.txt',
'directory/nested/nestedfile.txt',
additionalAssets: [
{ name: "directory/directoryfile.txt", data: "existing" },
{
name: "directory/nested/deep-nested/deepnested.txt",
data: "existing",
},
{ name: "directory/nested/nestedfile.txt", data: "existing" },
],
expectedAssetKeys: [
'directory/directoryfile.txt',
'directory/nested/deep-nested/deepnested.txt',
'directory/nested/nestedfile.txt',
"directory/directoryfile.txt",
"directory/nested/deep-nested/deepnested.txt",
"directory/nested/nestedfile.txt",
],
expectedAssetContent: {
'directory/nested/deep-nested/deepnested.txt': 'existing',
'directory/nested/nestedfile.txt': 'existing',
'directory/directoryfile.txt': 'existing',
"directory/nested/deep-nested/deepnested.txt": "existing",
"directory/nested/nestedfile.txt": "existing",
"directory/directoryfile.txt": "existing",
},
patterns: [
{
force: false,
from: 'directory/**/*',
from: "directory/**/*",
},
],
})
@@ -159,15 +165,15 @@ describe('force option', () => {
describe('is "true" (Boolean)', () => {
it('should force overwrite a file already in the compilation when "from" is a file', (done) => {
runForce({
existingAssets: ['file.txt'],
expectedAssetKeys: ['file.txt'],
additionalAssets: [{ name: "file.txt", data: "existing" }],
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
'file.txt': 'new',
"file.txt": "new",
},
patterns: [
{
force: true,
from: 'file.txt',
from: "file.txt",
},
],
})
@@ -177,28 +183,28 @@ describe('force option', () => {

it('should force overwrite files already in the compilation when "from" is a directory', (done) => {
runForce({
existingAssets: [
'.dottedfile',
'directoryfile.txt',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
additionalAssets: [
{ name: ".dottedfile", data: "existing" },
{ name: "directoryfile.txt", data: "existing" },
{ name: "nested/deep-nested/deepnested.txt", data: "existing" },
{ name: "nested/nestedfile.txt", data: "existing" },
],
expectedAssetKeys: [
'.dottedfile',
'directoryfile.txt',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
".dottedfile",
"directoryfile.txt",
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
expectedAssetContent: {
'.dottedfile': 'dottedfile contents\n',
'nested/deep-nested/deepnested.txt': '',
'nested/nestedfile.txt': '',
'directoryfile.txt': 'new',
".dottedfile": "dottedfile contents\n",
"nested/deep-nested/deepnested.txt": "",
"nested/nestedfile.txt": "",
"directoryfile.txt": "new",
},
patterns: [
{
force: true,
from: 'directory',
from: "directory",
},
],
})
@@ -208,25 +214,28 @@ describe('force option', () => {

it('should force overwrite files already in the compilation when "from" is a glob', (done) => {
runForce({
existingAssets: [
'directory/directoryfile.txt',
'directory/nested/deep-nested/deepnested.txt',
'directory/nested/nestedfile.txt',
additionalAssets: [
{ name: "directory/directoryfile.txt", data: "existing" },
{
name: "directory/nested/deep-nested/deepnested.txt",
data: "existing",
},
{ name: "directory/nested/nestedfile.txt", data: "existing" },
],
expectedAssetKeys: [
'directory/directoryfile.txt',
'directory/nested/deep-nested/deepnested.txt',
'directory/nested/nestedfile.txt',
"directory/directoryfile.txt",
"directory/nested/deep-nested/deepnested.txt",
"directory/nested/nestedfile.txt",
],
expectedAssetContent: {
'directory/nested/deep-nested/deepnested.txt': '',
'directory/nested/nestedfile.txt': '',
'directory/directoryfile.txt': 'new',
"directory/nested/deep-nested/deepnested.txt": "",
"directory/nested/nestedfile.txt": "",
"directory/directoryfile.txt": "new",
},
patterns: [
{
force: true,
from: 'directory/**/*',
from: "directory/**/*",
},
],
})
375 changes: 191 additions & 184 deletions test/from-option.test.js

Large diffs are not rendered by default.

239 changes: 144 additions & 95 deletions test/globOptions-option.test.js

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions test/helpers/BreakContenthashPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class BreakContenthashPlugin {
constructor(options = {}) {
this.options = options.options || {};
}

apply(compiler) {
const plugin = { name: "BrokeContenthashPlugin" };

compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
compilation.hooks.processAssets.tapAsync(
{
name: "broken-contenthash-webpack-plugin",
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE,
},
(unusedAssets, callback) => {
this.options.targetAssets.forEach(({ name, newName, newHash }) => {
const asset = compilation.getAsset(name);

compilation.updateAsset(asset.name, asset.source, {
...asset.info,
contenthash: newHash,
});
compilation.renameAsset(asset.name, newName);
});

callback();
}
);
});
}
}

export default BreakContenthashPlugin;
8 changes: 4 additions & 4 deletions test/helpers/ChildCompiler.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
export default class ChildCompiler {
// eslint-disable-next-line class-methods-use-this
apply(compiler) {
compiler.hooks.make.tapAsync('Child Compiler', (compilation, callback) => {
compiler.hooks.make.tapAsync("Child Compiler", (compilation, callback) => {
const outputOptions = {
filename: 'output.js',
filename: "output.js",
publicPath: compilation.outputOptions.publicPath,
};
const childCompiler = compilation.createChildCompiler(
'ChildCompiler',
"ChildCompiler",
outputOptions
);
childCompiler.runAsChild((error, entries, childCompilation) => {
@@ -19,7 +19,7 @@ export default class ChildCompiler {

if (assets.length > 0) {
callback(
new Error('Copy plugin should not be ran in child compilations')
new Error("Copy plugin should not be ran in child compilations")
);

return;
16 changes: 7 additions & 9 deletions test/helpers/PreCopyPlugin.js
Original file line number Diff line number Diff line change
@@ -4,19 +4,17 @@ class PreCopyPlugin {
}

apply(compiler) {
const plugin = { name: 'PreCopyPlugin' };
const plugin = { name: "PreCopyPlugin" };

compiler.hooks.thisCompilation.tap(plugin, (compilation) => {
compilation.hooks.additionalAssets.tapAsync(
'copy-webpack-plugin',
"pre-copy-webpack-plugin",
(callback) => {
this.options.existingAssets.forEach((assetName) => {
// eslint-disable-next-line no-param-reassign
compilation.assets[assetName] = {
source() {
return 'existing';
},
};
this.options.additionalAssets.forEach(({ name, data, info }) => {
const { RawSource } = compiler.webpack.sources;
const source = new RawSource(data);

compilation.emitAsset(name, source, info);
});

callback();
5 changes: 2 additions & 3 deletions test/helpers/compile.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default (compiler) => {
return new Promise((resolve, reject) => {
export default (compiler) =>
new Promise((resolve, reject) => {
compiler.run((error, stats) => {
if (error) {
return reject(error);
@@ -8,4 +8,3 @@ export default (compiler) => {
return resolve({ stats, compiler });
});
});
};
3 changes: 3 additions & 0 deletions test/helpers/enter-with-asset-modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import txtURL from "../fixtures/directory/nested/deep-nested/deepnested.txt";

export default txtURL;
37 changes: 21 additions & 16 deletions test/helpers/getCompiler.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
import path from 'path';
import path from "path";

import webpack from 'webpack';
import { createFsFromVolume, Volume } from 'memfs';
// eslint-disable-next-line import/no-extraneous-dependencies
import webpack from "webpack";
// eslint-disable-next-line import/no-extraneous-dependencies
import { createFsFromVolume, Volume } from "memfs";

export default (config = {}) => {
const fullConfig = {
mode: 'development',
context: path.resolve(__dirname, '../fixtures'),
entry: path.resolve(__dirname, '../helpers/enter.js'),
mode: "development",
context: path.resolve(__dirname, "../fixtures"),
entry: path.resolve(__dirname, "../helpers/enter.js"),
output: {
path: path.resolve(__dirname, '../build'),
path: path.resolve(__dirname, "../build"),
},
module: {
rules: [
{
test: /\.txt/,
type: "asset/resource",
generator: {
filename: "asset-modules/[name][ext]",
},
},
],
},
...config,
};

if (webpack.version[0] === 5) {
fullConfig.stats.source = true;
}

const compiler = webpack(fullConfig);

if (!config.outputFileSystem) {
const outputFileSystem = createFsFromVolume(new Volume());
// Todo remove when we drop webpack@4 support
outputFileSystem.join = path.join.bind(path);

compiler.outputFileSystem = outputFileSystem;
compiler.outputFileSystem = createFsFromVolume(new Volume());
}

return compiler;
8 changes: 4 additions & 4 deletions test/helpers/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import compile from './compile';
import getCompiler from './getCompiler';
import readAsset from './readAsset';
import readAssets from './readAssets';
import compile from "./compile";
import getCompiler from "./getCompiler";
import readAsset from "./readAsset";
import readAssets from "./readAssets";

export { compile, getCompiler, readAsset, readAssets };
6 changes: 3 additions & 3 deletions test/helpers/readAsset.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import path from 'path';
import path from "path";

export default (asset, compiler, stats) => {
const usedFs = compiler.outputFileSystem;
const outputPath = stats.compilation.outputOptions.path;

let data = '';
let data = "";
let targetFile = asset;

const queryStringIdx = targetFile.indexOf('?');
const queryStringIdx = targetFile.indexOf("?");

if (queryStringIdx >= 0) {
targetFile = targetFile.substr(0, queryStringIdx);
4 changes: 2 additions & 2 deletions test/helpers/readAssets.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import readAsset from './readAsset';
import readAsset from "./readAsset";

export default function readAssets(compiler, stats) {
const assets = {};

Object.keys(stats.compilation.assets)
.filter((a) => a !== 'main.js')
.filter((a) => a !== "main.js")
.forEach((asset) => {
assets[asset] = readAsset(asset, compiler, stats);
});
2 changes: 1 addition & 1 deletion test/helpers/removeIllegalCharacterForWindows.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
module.exports = (string) =>
process.platform !== 'win32' ? string : string.replace(/[*?"<>|]/g, '');
process.platform !== "win32" ? string : string.replace(/[*?"<>|]/g, "");
49 changes: 30 additions & 19 deletions test/helpers/run.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
// Ideally we pass in patterns and confirm the resulting assets
import fs from 'fs';
import path from 'path';
import fs from "fs";
import path from "path";

import CopyPlugin from '../../src';
import CopyPlugin from "../../src/index";

import ChildCompilerPlugin from './ChildCompiler';
import PreCopyPlugin from './PreCopyPlugin';
import ChildCompilerPlugin from "./ChildCompiler";
import PreCopyPlugin from "./PreCopyPlugin";
import BreakContenthashPlugin from "./BreakContenthashPlugin";

import removeIllegalCharacterForWindows from './removeIllegalCharacterForWindows';
import removeIllegalCharacterForWindows from "./removeIllegalCharacterForWindows";

import { compile, getCompiler, readAssets } from './';
import { compile, getCompiler, readAssets } from "./";

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

const isWin = process.platform === 'win32';
const isWin = process.platform === "win32";

const ignore = [
'**/symlink/**/*',
'**/file-ln.txt',
'**/directory-ln',
'**/watch/**/*',
"**/symlink/**/*",
"**/file-ln.txt",
"**/directory-ln",
"**/watch/**/*",
];

function run(opts) {
@@ -31,7 +32,7 @@ function run(opts) {
pattern.context = removeIllegalCharacterForWindows(pattern.context);
}

if (typeof pattern !== 'string') {
if (typeof pattern !== "string") {
if (!opts.symlink || isWin) {
pattern.globOptions = pattern.globOptions || {};
pattern.globOptions.ignore = [
@@ -45,6 +46,16 @@ function run(opts) {

const compiler = opts.compiler || getCompiler();

if (opts.preCopy) {
new PreCopyPlugin({ options: opts.preCopy }).apply(compiler);
}

if (opts.breakContenthash) {
new BreakContenthashPlugin({ options: opts.breakContenthash }).apply(
compiler
);
}

new CopyPlugin({ patterns: opts.patterns, options: opts.options }).apply(
compiler
);
@@ -70,7 +81,7 @@ function run(opts) {
throw compilation.warnings[0];
}

const enryPoint = path.resolve(__dirname, 'enter.js');
const enryPoint = path.resolve(__dirname, "enter.js");

if (compilation.fileDependencies.has(enryPoint)) {
compilation.fileDependencies.delete(enryPoint);
@@ -91,14 +102,14 @@ function runEmit(opts) {
if (opts.expectedAssetKeys && opts.expectedAssetKeys.length > 0) {
expect(
Object.keys(compilation.assets)
.filter((a) => a !== 'main.js')
.filter((a) => a !== "main.js")
.sort()
).toEqual(
opts.expectedAssetKeys.sort().map(removeIllegalCharacterForWindows)
);
} else {
// eslint-disable-next-line no-param-reassign
delete compilation.assets['main.js'];
delete compilation.assets["main.js"];
expect(compilation.assets).toEqual({});
}

@@ -146,8 +157,8 @@ function runChange(opts) {
);

// Create two test files
fs.writeFileSync(opts.newFileLoc1, 'file1contents');
fs.writeFileSync(opts.newFileLoc2, 'file2contents');
fs.writeFileSync(opts.newFileLoc1, "file1contents");
fs.writeFileSync(opts.newFileLoc2, "file2contents");

const arrayOfStats = [];

@@ -161,7 +172,7 @@ function runChange(opts) {

await delay(500);

fs.appendFileSync(opts.newFileLoc1, 'extra');
fs.appendFileSync(opts.newFileLoc1, "extra");

await delay(500);

112 changes: 112 additions & 0 deletions test/info-option.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
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: () => {
return { test: true };
},
},
],
})
.then(({ compilation }) => {
expect(compilation.assetsInfo.get("file.txt").test).toBe(true);
})
.then(done)
.catch(done);
});
});
22 changes: 11 additions & 11 deletions test/noErrorOnMissing.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { runEmit } from './helpers/run';
import { runEmit } from "./helpers/run";

describe('noErrorOnMissing option', () => {
describe('is a file', () => {
it('should work', (done) => {
describe("noErrorOnMissing option", () => {
describe("is a file", () => {
it("should work", (done) => {
runEmit({
expectedAssetKeys: [],
patterns: [
{
from: 'unknown.unknown',
from: "unknown.unknown",
noErrorOnMissing: true,
},
],
@@ -17,13 +17,13 @@ describe('noErrorOnMissing option', () => {
});
});

describe('is a directory', () => {
it('should work', (done) => {
describe("is a directory", () => {
it("should work", (done) => {
runEmit({
expectedAssetKeys: [],
patterns: [
{
from: 'unknown',
from: "unknown",
noErrorOnMissing: true,
},
],
@@ -33,13 +33,13 @@ describe('noErrorOnMissing option', () => {
});
});

describe('is a glob', () => {
it('should work', (done) => {
describe("is a glob", () => {
it("should work", (done) => {
runEmit({
expectedAssetKeys: [],
patterns: [
{
from: '*.unknown',
from: "*.unknown",
noErrorOnMissing: true,
},
],
57 changes: 57 additions & 0 deletions test/priority-option.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { run } from "./helpers/run";

describe("priority option", () => {
it("should copy without specifying priority option", (done) => {
run({
expectedAssetKeys: [],
patterns: [
{
from: "dir (86)/file.txt",
to: "newfile.txt",
force: true,
},
{
from: "file.txt",
to: "newfile.txt",
force: true,
},
],
})
.then(({ stats }) => {
const { info } = stats.compilation.getAsset("newfile.txt");

expect(info.sourceFilename).toEqual("file.txt");

done();
})
.catch(done);
});

it("should copy with specifying priority option", (done) => {
run({
expectedAssetKeys: [],
patterns: [
{
from: "dir (86)/file.txt",
to: "newfile.txt",
force: true,
priority: 10,
},
{
from: "file.txt",
to: "newfile.txt",
force: true,
priority: 5,
},
],
})
.then(({ stats }) => {
const { info } = stats.compilation.getAsset("newfile.txt");

expect(info.sourceFilename).toEqual("dir (86)/file.txt");

done();
})
.catch(done);
});
});
808 changes: 664 additions & 144 deletions test/to-option.test.js

Large diffs are not rendered by default.

71 changes: 36 additions & 35 deletions test/toType-option.test.js
Original file line number Diff line number Diff line change
@@ -1,101 +1,102 @@
import path from 'path';
import path from "path";

import { runEmit } from './helpers/run';
import { runEmit } from "./helpers/run";

const FIXTURES_DIR = path.join(__dirname, 'fixtures');
const FIXTURES_DIR_NORMALIZED = path
.join(__dirname, "fixtures")
.replace(/\\/g, "/");

describe('toType option', () => {
it('should move a file to a new file', (done) => {
describe("toType option", () => {
it("should copy a file to a new file", (done) => {
runEmit({
expectedAssetKeys: ['new-file.txt'],
expectedAssetKeys: ["new-file.txt"],
patterns: [
{
from: 'file.txt',
to: 'new-file.txt',
toType: 'file',
from: "file.txt",
to: "new-file.txt",
toType: "file",
},
],
})
.then(done)
.catch(done);
});

it('should move a file to a new directory', (done) => {
it("should copy a file to a new directory", (done) => {
runEmit({
expectedAssetKeys: ['new-file.txt/file.txt'],
expectedAssetKeys: ["new-file.txt/file.txt"],
patterns: [
{
from: 'file.txt',
to: 'new-file.txt',
toType: 'dir',
from: "file.txt",
to: "new-file.txt",
toType: "dir",
},
],
})
.then(done)
.catch(done);
});

it('should move a file to a new directory', (done) => {
it("should copy a file to a new directory", (done) => {
runEmit({
expectedAssetKeys: [
'directory/directorynew-directoryfile.txt.5d7817ed5bc246756d73d6a4c8e94c33.5d7817ed5bc246756d73d6a4c8e94c33.22af645d.22af645d.txt',
"directory/directoryfile.txt-new-directoryfile.txt.5d7817ed5bc246756d73.ac7f6fcb65ddfcc43b2c-ac7f6fcb65ddfcc43b2c.txt",
],
patterns: [
{
from: 'directory/directoryfile.*',
to:
'[path][folder]new-[name].[ext].[hash].[contenthash].[md5:contenthash:hex:8].[md5:hash:hex:8].txt',
toType: 'template',
from: "directory/directoryfile.*",
to: "[path][base]-new-[name][ext].[contenthash].[hash]-[fullhash][ext]",
toType: "template",
},
],
})
.then(done)
.catch(done);
});

it('should move a file to a new file with no extension', (done) => {
it("should copy a file to a new file with no extension", (done) => {
runEmit({
expectedAssetKeys: ['newname'],
expectedAssetKeys: ["newname"],
patterns: [
{
from: 'file.txt',
to: 'newname',
toType: 'file',
from: "file.txt",
to: "newname",
toType: "file",
},
],
})
.then(done)
.catch(done);
});

it('should move a file to a new directory with an extension', (done) => {
it("should copy a file to a new directory with an extension", (done) => {
runEmit({
expectedAssetKeys: ['newdirectory.ext/file.txt'],
expectedAssetKeys: ["newdirectory.ext/file.txt"],
patterns: [
{
from: 'file.txt',
to: 'newdirectory.ext',
toType: 'dir',
from: "file.txt",
to: "newdirectory.ext",
toType: "dir",
},
],
})
.then(done)
.catch(done);
});

it('should warn when file not found and stats is undefined', (done) => {
it("should warn when file not found and stats is undefined", (done) => {
runEmit({
expectedAssetKeys: [],
expectedErrors: [
new Error(
`unable to locate 'nonexistent.txt' at '${FIXTURES_DIR}${path.sep}nonexistent.txt'`
`unable to locate '${FIXTURES_DIR_NORMALIZED}/nonexistent.txt' glob`
),
],
patterns: [
{
from: 'nonexistent.txt',
to: '.',
toType: 'dir',
from: "nonexistent.txt",
to: ".",
toType: "dir",
},
],
})
224 changes: 142 additions & 82 deletions test/transform-option.test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import path from 'path';
import zlib from 'zlib';
import path from "path";
import zlib from "zlib";

import { run, runEmit } from './helpers/run';
import { run, runEmit } from "./helpers/run";

const FIXTURES_DIR = path.join(__dirname, 'fixtures');
const FIXTURES_DIR = path.join(__dirname, "fixtures");

describe('transform option', () => {
describe("transform option", () => {
it('should transform file when "from" is a file', (done) => {
runEmit({
expectedAssetKeys: ['file.txt'],
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
'file.txt': 'newchanged',
"file.txt": "newchanged",
},
patterns: [
{
from: 'file.txt',
transform(content, absoluteFrom) {
expect(absoluteFrom.includes(FIXTURES_DIR)).toBe(true);
from: "file.txt",
transform: {
transformer(content, absoluteFrom) {
expect(absoluteFrom.includes(FIXTURES_DIR)).toBe(true);

return `${content}changed`;
return `${content}changed`;
},
},
},
],
@@ -30,24 +32,26 @@ describe('transform option', () => {
it('should transform target path of every when "from" is a directory', (done) => {
runEmit({
expectedAssetKeys: [
'.dottedfile',
'directoryfile.txt',
'nested/deep-nested/deepnested.txt',
'nested/nestedfile.txt',
".dottedfile",
"directoryfile.txt",
"nested/deep-nested/deepnested.txt",
"nested/nestedfile.txt",
],
expectedAssetContent: {
'.dottedfile': 'dottedfile contents\nchanged',
'directoryfile.txt': 'newchanged',
'nested/deep-nested/deepnested.txt': 'changed',
'nested/nestedfile.txt': 'changed',
".dottedfile": "dottedfile contents\nchanged",
"directoryfile.txt": "newchanged",
"nested/deep-nested/deepnested.txt": "changed",
"nested/nestedfile.txt": "changed",
},
patterns: [
{
from: 'directory',
transform(content, absoluteFrom) {
expect(absoluteFrom.includes(FIXTURES_DIR)).toBe(true);
from: "directory",
transform: {
transformer(content, absoluteFrom) {
expect(absoluteFrom.includes(FIXTURES_DIR)).toBe(true);

return `${content}changed`;
return `${content}changed`;
},
},
},
],
@@ -59,22 +63,24 @@ describe('transform option', () => {
it('should transform target path of every file when "from" is a glob', (done) => {
runEmit({
expectedAssetKeys: [
'directory/directoryfile.txt',
'directory/nested/deep-nested/deepnested.txt',
'directory/nested/nestedfile.txt',
"directory/directoryfile.txt",
"directory/nested/deep-nested/deepnested.txt",
"directory/nested/nestedfile.txt",
],
expectedAssetContent: {
'directory/directoryfile.txt': 'newchanged',
'directory/nested/deep-nested/deepnested.txt': 'changed',
'directory/nested/nestedfile.txt': 'changed',
"directory/directoryfile.txt": "newchanged",
"directory/nested/deep-nested/deepnested.txt": "changed",
"directory/nested/nestedfile.txt": "changed",
},
patterns: [
{
from: 'directory/**/*',
transform(content, absoluteFrom) {
expect(absoluteFrom.includes(FIXTURES_DIR)).toBe(true);
from: "directory/**/*",
transform: {
transformer(content, absoluteFrom) {
expect(absoluteFrom.includes(FIXTURES_DIR)).toBe(true);

return `${content}changed`;
return `${content}changed`;
},
},
},
],
@@ -83,15 +89,32 @@ describe('transform option', () => {
.catch(done);
});

it('should transform file when function return Promise', (done) => {
it("should transform file when transform is function", (done) => {
runEmit({
expectedAssetKeys: ['file.txt'],
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
'file.txt': 'newchanged!',
"file.txt": "newchanged!",
},
patterns: [
{
from: 'file.txt',
from: "file.txt",
transform: (content) => `${content}changed!`,
},
],
})
.then(done)
.catch(done);
});

it("should transform file when function return Promise", (done) => {
runEmit({
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
"file.txt": "newchanged!",
},
patterns: [
{
from: "file.txt",
transform(content) {
return new Promise((resolve) => {
resolve(`${content}changed!`);
@@ -104,21 +127,46 @@ describe('transform option', () => {
.catch(done);
});

it('should transform target path when async function used', (done) => {
it("should transform file when function `transformer` return Promise", (done) => {
runEmit({
expectedAssetKeys: ['file.txt'],
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
'file.txt': 'newchanged!',
"file.txt": "newchanged!",
},
patterns: [
{
from: 'file.txt',
async transform(content) {
const newPath = await new Promise((resolve) => {
resolve(`${content}changed!`);
});
from: "file.txt",
transform: {
transformer(content) {
return new Promise((resolve) => {
resolve(`${content}changed!`);
});
},
},
},
],
})
.then(done)
.catch(done);
});

return newPath;
it("should transform target path when async function used", (done) => {
runEmit({
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
"file.txt": "newchanged!",
},
patterns: [
{
from: "file.txt",
transform: {
async transformer(content) {
const newPath = await new Promise((resolve) => {
resolve(`${content}changed!`);
});

return newPath;
},
},
},
],
@@ -127,16 +175,18 @@ describe('transform option', () => {
.catch(done);
});

it('should warn when function throw error', (done) => {
it("should warn when function throw error", (done) => {
runEmit({
expectedAssetKeys: [],
expectedErrors: [new Error('a failure happened')],
expectedErrors: [new Error("a failure happened")],
patterns: [
{
from: 'file.txt',
transform() {
// eslint-disable-next-line no-throw-literal
throw new Error('a failure happened');
from: "file.txt",
transform: {
transformer() {
// eslint-disable-next-line no-throw-literal
throw new Error("a failure happened");
},
},
},
],
@@ -145,17 +195,19 @@ describe('transform option', () => {
.catch(done);
});

it('should warn when Promise was rejected', (done) => {
it("should warn when Promise was rejected", (done) => {
runEmit({
expectedAssetKeys: [],
expectedErrors: [new Error('a failure happened')],
expectedErrors: [new Error("a failure happened")],
patterns: [
{
from: 'file.txt',
transform() {
return new Promise((resolve, reject) => {
return reject(new Error('a failure happened'));
});
from: "file.txt",
transform: {
transformer() {
return new Promise((resolve, reject) =>
reject(new Error("a failure happened"))
);
},
},
},
],
@@ -164,17 +216,19 @@ describe('transform option', () => {
.catch(done);
});

it('should warn when async function throw error', (done) => {
it("should warn when async function throw error", (done) => {
runEmit({
expectedAssetKeys: [],
expectedErrors: [new Error('a failure happened')],
expectedErrors: [new Error("a failure happened")],
patterns: [
{
from: 'file.txt',
async transform() {
await new Promise((resolve, reject) => {
reject(new Error('a failure happened'));
});
from: "file.txt",
transform: {
async transformer() {
await new Promise((resolve, reject) => {
reject(new Error("a failure happened"));
});
},
},
},
],
@@ -183,23 +237,25 @@ describe('transform option', () => {
.catch(done);
});

it('should be a different size for the source file and the converted file', (done) => {
it("should be a different size for the source file and the converted file", (done) => {
run({
patterns: [
{
from: 'file.txt',
from: "file.txt",
},
{
from: 'file.txt',
to: 'file.txt.gz',
transform: (content) => zlib.gzipSync(content),
from: "file.txt",
to: "file.txt.gz",
transform: {
transformer: (content) => zlib.gzipSync(content),
},
},
],
})
.then(({ compilation }) => {
expect(
compilation.assets['file.txt'].size() !==
compilation.assets['file.txt.gz'].size()
compilation.assets["file.txt"].size() !==
compilation.assets["file.txt.gz"].size()
).toBe(true);
})
.then(done)
@@ -208,22 +264,26 @@ describe('transform option', () => {

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

return `${content}changed`;
return `${content}changed`;
},
},
transformPath(targetPath, absoluteFrom) {
expect(absoluteFrom).toBe(path.join(FIXTURES_DIR, 'file.txt'));
to({ context, absoluteFilename }) {
expect(absoluteFilename).toBe(path.join(FIXTURES_DIR, "file.txt"));

const targetPath = path.relative(context, absoluteFilename);

return targetPath.replace('file.txt', 'subdir/test.txt');
return targetPath.replace("file.txt", "subdir/test.txt");
},
},
],
221 changes: 221 additions & 0 deletions test/transformAll-option.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import CopyPlugin from "../src";

import { runEmit } from "./helpers/run";
import { compile, getCompiler, readAssets } from "./helpers";

describe("transformAll option", () => {
it('should be defined "assets"', (done) => {
runEmit({
expectedAssetKeys: ["file.txt"],
patterns: [
{
from: "file.txt",
to: "file.txt",
transformAll(assets) {
expect(assets).toBeDefined();

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

it("should transform files", (done) => {
runEmit({
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
"file.txt":
"new::directory/nested/deep-nested/deepnested.txt::directory/nested/nestedfile.txt::",
},
patterns: [
{
from: "directory/**/*.txt",
to: "file.txt",
transformAll(assets) {
const result = assets.reduce((accumulator, asset) => {
const content = asset.data.toString() || 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"],
expectedAssetContent: {
"file.txt":
"directory/directoryfile.txt::directory/nested/deep-nested/deepnested.txt::directory/nested/nestedfile.txt::",
},
patterns: [
{
from: "directory/**/*.txt",
to: "file.txt",
async transformAll(assets) {
const result = assets.reduce((accumulator, asset) => {
// eslint-disable-next-line no-param-reassign
accumulator = `${accumulator}${asset.sourceFilename}::`;
return accumulator;
}, "");

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

it("should transform files with force option enabled", (done) => {
runEmit({
expectedAssetKeys: ["file.txt"],
expectedAssetContent: {
"file.txt":
"directory/directoryfile.txt::directory/nested/deep-nested/deepnested.txt::directory/nested/nestedfile.txt::",
},
patterns: [
{
from: "file.txt",
},
{
from: "directory/**/*.txt",
to: "file.txt",
transformAll(assets) {
const result = assets.reduce((accumulator, asset) => {
// eslint-disable-next-line no-param-reassign
accumulator = `${accumulator}${asset.sourceFilename}::`;
return accumulator;
}, "");

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

it('should warn when "to" option is not defined', (done) => {
runEmit({
expectedAssetKeys: [],
expectedErrors: [
new Error(
`Invalid "pattern.to" for the "pattern.from": "file.txt" and "pattern.transformAll" function. The "to" option must be specified.`
),
],
patterns: [
{
from: "file.txt",
transformAll() {
return "";
},
},
],
})
.then(done)
.catch(done);
});

it("should warn when function throw error", (done) => {
runEmit({
expectedAssetKeys: [],
expectedErrors: [new Error("a failure happened")],
patterns: [
{
from: "directory/**/*.txt",
to: "file.txt",
transformAll() {
// eslint-disable-next-line no-throw-literal
throw new Error("a failure happened");
},
},
],
})
.then(done)
.catch(done);
});

it("should interpolate [fullhash] and [contenthash]", (done) => {
runEmit({
expectedAssetKeys: ["4333a40fa67dfaaaefc9-ac7f6fcb65ddfcc43b2c-file.txt"],
expectedAssetContent: {
"4333a40fa67dfaaaefc9-ac7f6fcb65ddfcc43b2c-file.txt":
"::special::new::::::::::new::::::new::",
},
patterns: [
{
from: "**/*.txt",
to: "[contenthash]-[fullhash]-file.txt",
transformAll(assets) {
const result = assets.reduce((accumulator, asset) => {
// eslint-disable-next-line no-param-reassign
accumulator = `${accumulator}${asset.data}::`;
return accumulator;
}, "");

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

describe("cache", () => {
it('should work with the "memory" cache', async () => {
const compiler = getCompiler({});

new CopyPlugin({
patterns: [
{
from: "directory/**/*.txt",
to: "file.txt",
transformAll(assets) {
const result = assets.reduce((accumulator, asset) => {
const content = asset.data.toString() || asset.sourceFilename;
// eslint-disable-next-line no-param-reassign
accumulator = `${accumulator}${content}::`;
return accumulator;
}, "");

return result;
},
},
],
}).apply(compiler);

const { stats } = await compile(compiler);

expect(stats.compilation.emittedAssets.size).toBe(2);
expect(readAssets(compiler, stats)).toMatchSnapshot("assets");
expect(stats.compilation.errors).toMatchSnapshot("errors");
expect(stats.compilation.warnings).toMatchSnapshot("warnings");

await new Promise(async (resolve) => {
const { stats: newStats } = await compile(compiler);

expect(newStats.compilation.emittedAssets.size).toBe(0);
expect(readAssets(compiler, newStats)).toMatchSnapshot("assets");
expect(newStats.compilation.errors).toMatchSnapshot("errors");
expect(newStats.compilation.warnings).toMatchSnapshot("warnings");

resolve();
});
});
});
273 changes: 0 additions & 273 deletions test/transformPath-option.test.js

This file was deleted.

Loading