CopyWebpackPlugin

免责声明 CopyWebpackPlugin是一个由社区成员维护的第三方包,它可能不具备与 webpack 相同的支持、安全策略或许可证,并且不受 webpack 维护。

npm node tests cover discussion size

将现有单个文件或整个目录复制到构建目录。

入门

首先,你需要安装 copy-webpack-plugin

npm install copy-webpack-plugin --save-dev

yarn add -D copy-webpack-plugin

pnpm add -D copy-webpack-plugin

然后将插件添加到你的 webpack 配置中。例如:

webpack.config.js

const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        { from: "source", to: "dest" },
        { from: "other", to: "public" },
      ],
    }),
  ],
};

[!注意]

copy-webpack-plugin 的设计目的不是复制构建过程中生成的文件。相反,它旨在复制源目录中已存在的文件,作为构建过程的一部分。

[!注意]

如果你希望 webpack-dev-server 在开发过程中将文件写入输出目录,你可以启用 writeToDisk 选项或使用 write-file-webpack-plugin

[!注意]

你可以从 webpack 统计信息 API 中的 资产对象 中获取原始源文件名。

选项

插件用法

webpack.config.js

const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        { from: "source", to: "dest" },
        "path/to/source", // Absolute or relative path, can be files, directories or globs. See examples below.
      ],
      options: {
        concurrency: 100,
      },
    }),
  ],
};

模式

类型

type from = string;

默认值:undefined

要复制文件的 glob 或路径。glob 遵循 fast-glob 模式语法。注意:glob 必须是 string 类型。

[!警告]

如果 from 选项是 glob 模式(即 path\to\file.ext),请不要直接在其中使用 \\,因为在 UNIX 系统上,反斜杠被视为普通字符(而不是路径分隔符)。在 Windows 上,正斜杠和反斜杠都用作分隔符。请改用 /,或使用 Node 的 path 工具来规范化路径。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        "relative/path/to/file.ext",
        "relative/path/to/dir",
        path.resolve(__dirname, "src", "file.ext"),
        path.resolve(__dirname, "src", "dir"),
        "**/*",
        {
          from: "**/*",
        },
        // If absolute path is a `glob` we replace backslashes with forward slashes, because only forward slashes can be used in the `glob`
        path.posix.join(
          path.resolve(__dirname, "src").replace(/\\/g, "/"),
          "*.txt",
        ),
      ],
    }),
  ],
};
适用于 Windows

如果你在 Windows 上的 from 选项中使用绝对文件或文件夹路径,可以使用 Windows 路径分隔符 (\\)

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, "file.txt"),
        },
      ],
    }),
  ],
};

然而,在编写 glob 表达式时,请始终使用正斜杠。有关更多详细信息,请参阅 fast-glob 手册

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          // If absolute path is a `glob` we replace backslashes with forward slashes, because only forward slashes can be used in the `glob`
          from: path.posix.join(
            path.resolve(__dirname, "fixtures").replace(/\\/g, "/"),
            "*.txt",
          ),
        },
      ],
    }),
  ],
};

context 选项的行为取决于 from 值是 globfile 还是 dir。查看更多示例

目标

类型

type to =
  | string
  | ((pathData: { context: string; absoluteFilename?: string }) => string);

默认值:compiler.options.output

string

指定输出路径。

[!警告]

不要直接在 to 路径(即 path\to\dest)选项中使用 \\,因为在 UNIX 系统上,反斜杠被视为普通字符(而不是路径分隔符)。在 Windows 上,正斜杠和反斜杠都用作分隔符。请改用 /,或使用 Node 的 path 工具来规范化路径。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "**/*",
          to: "relative/path/to/dest/",
        },
        {
          from: "**/*",
          to: "/absolute/path/to/dest/",
        },
        {
          from: "**/*",
          to: "[path][name].[contenthash][ext]",
        },
      ],
    }),
  ],
};
function

允许修改写入路径。

[!警告]

不要直接在 to 路径(即 path\to\newFile)选项中使用 \\,因为在 UNIX 系统上,反斜杠被视为普通字符(而不是路径分隔符)。在 Windows 上,正斜杠和反斜杠都用作分隔符。请改用 /,或使用 Node 的 path 工具来规范化路径。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to({ context, absoluteFilename }) {
            return "dest/newPath/[name][ext]";
          },
        },
      ],
    }),
  ],
};

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to({ context, absoluteFilename }) {
            return Promise.resolve("dest/newPath/[name][ext]");
          },
        },
      ],
    }),
  ],
};

context

类型

type context = string;

默认值:options.context|compiler.options.context

定义用于两个目的的基础目录

  1. 它被前置到 from 路径。

  2. 它从结果路径的开头移除。

[!警告]

不要直接在 to 路径(即 path\to\newFile)选项中使用 \\,因为在 UNIX 系统上,反斜杠被视为普通字符(而不是路径分隔符)。在 Windows 上,正斜杠和反斜杠都用作分隔符。请改用 /,或使用 Node 的 path 工具来规范化路径。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.txt",
          to: "dest/",
          context: "app/",
        },
      ],
    }),
  ],
};

context 可以是绝对路径或相对路径。如果它是相对路径,则会基于 compiler.options.context 转换为绝对路径。

from 使用 glob 模式时,你应该明确定义 context。否则,插件将根据 from 的性质自动设置它。

  • 如果 from 是文件,则 context 默认为文件所在目录。结果路径将仅是文件名。

  • 如果 from 是目录,context 将设置为同一目录。结果路径将包含该目录的内容(包括子目录),相对于它。

context 的用法通过这些 示例 进行了说明。

globOptions

[!警告]

onlyDirectories 无效,因为此插件旨在复制文件,而非单独复制目录。

类型

type globOptions = import("tinyglobby").GlobOptions;

默认值:undefined

允许你配置插件使用的 glob 模式匹配库。查看支持的选项列表。要排除要复制的文件,请使用 globOptions.ignore 选项

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "public/**/*",
          globOptions: {
            dot: true,
            gitignore: true,
            ignore: ["**/file.*", "**/ignored-directory/**"],
          },
        },
      ],
    }),
  ],
};

filter

类型

type filter = (filepath: string) => boolean;

默认值:undefined

[!注意]

要按路径(例如,按扩展名或名称)忽略文件,请优先使用 [globOptions.ignore] 选项。

webpack.config.js

const fs = require("fs").promise;

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "public/**/*",
          filter: async (resourcePath) => {
            const data = await fs.promises.readFile(resourcePath);
            const content = data.toString();

            if (content === "my-custom-content") {
              return false;
            }

            return true;
          },
        },
      ],
    }),
  ],
};

toType

类型

type toType = "dir" | "file" | "template";

默认值:undefined

确定 to 选项的类型——它是目录、文件还是模板。有时很难判断 to 是什么,例如 path/to/dir-with.ext。如果你想将文件复制到目录中,应该明确将类型设置为 dir。在大多数情况下,插件会自动确定正确的 type,因此你通常不需要手动设置此选项。

名称类型默认值描述
'dir'stringundefined使用的 to 没有扩展名或以 '/' 结尾。
'file'stringundefinedto 是一个文件路径,而不是目录或模板时使用。
'template'stringundefinedto 包含 模板模式 时使用。
'dir'

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "path/to/file.txt",
          to: "directory/with/extension.ext",
          toType: "dir",
        },
      ],
    }),
  ],
};
'file'

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "path/to/file.txt",
          to: "file/without/extension",
          toType: "file",
        },
      ],
    }),
  ],
};
'template'

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/",
          to: "dest/[name].[contenthash][ext]",
          toType: "template",
        },
      ],
    }),
  ],
};

force

类型

type force = boolean;

默认值:false

覆盖 compilation.assets 中已存在的文件(通常由其他插件或加载器添加)。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/**/*",
          to: "dest/",
          force: true,
        },
      ],
    }),
  ],
};

priority

类型

type priority = number;

默认值:0

允许指定具有相同目标文件名的文件复制优先级。优先级较高的模式的文件将稍后复制。要启用覆盖,必须将 force 选项设置为 truewebpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        // Copied second and will overwrite "dir_2/file.txt"
        {
          from: "dir_1/file.txt",
          to: "newfile.txt",
          force: true,
          priority: 10,
        },
        // Copied first
        {
          from: "dir_2/file.txt",
          to: "newfile.txt",
          priority: 5,
        },
      ],
    }),
  ],
};

transform

类型

type transform =
  | {
      transformer: (input: string, absoluteFilename: string) => string | Buffer;
      cache?: boolean | TransformerCacheObject | undefined;
    }
  | ((input: string, absoluteFilename: string) => string | Buffer);

默认值:undefined

允许你在文件写入输出目录之前修改其内容。

function

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to: "dest/",
          // The `content` argument is a [`Buffer`](https://node.org.cn/api/buffer.html) object, it could be converted to a `String` to be processed using `content.toString()`
          // The `absoluteFrom` argument is a `String`, it is absolute path from where the file is being copied
          transform(content, absoluteFrom) {
            return optimize(content);
          },
        },
      ],
    }),
  ],
};
object
名称默认值描述
transformerundefined允许你修改文件内容。
cachefalsetransform 启用缓存。你可以使用 transform: { cache: { key: 'my-cache-key' } } 在需要时手动使缓存失效。
transformer

类型

type transformer = (input: string, absoluteFilename: string) => string;

默认值:undefined

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to: "dest/",
          // The `content` argument is a [`Buffer`](https://node.org.cn/api/buffer.html) object, it could be converted to a `String` to be processed using `content.toString()`
          // The `absoluteFrom` argument is a `String`, it is absolute path from where the file is being copied
          transform: {
            transformer(content, absoluteFrom) {
              return optimize(content);
            },
          },
        },
      ],
    }),
  ],
};

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to: "dest/",
          transform: {
            transformer(content, path) {
              return Promise.resolve(optimize(content));
            },
          },
        },
      ],
    }),
  ],
};
cache

类型

type cache =
  | boolean
  | {
      keys: {
        [key: string]: any;
      };
    }
  | {
      keys: (
        defaultCacheKeys: {
          [key: string]: any;
        },
        absoluteFilename: string,
      ) => Promise<{
        [key: string]: any;
      }>;
    }
  | undefined;

默认值:false

webpack.config.js

启用或禁用缓存并配置其行为。默认情况下,缓存目录位于:node_modules/.cache/copy-webpack-plugin

boolean

启用/禁用 transform 缓存。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to: "dest/",
          transform: {
            transformer(content, path) {
              return optimize(content);
            },
            cache: true,
          },
        },
      ],
    }),
  ],
};
object

启用 transform 缓存并设置失效键。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to: "dest/",
          transform: {
            transformer(content, path) {
              return optimize(content);
            },
            cache: {
              keys: {
                // May be useful for invalidating cache based on external values
                // For example, you can invalid cache based on `process.version` - { node: process.version }
                key: "value",
              },
            },
          },
        },
      ],
    }),
  ],
};

你可以使用函数设置失效键。

简单函数

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to: "dest/",
          transform: {
            transformer(content, path) {
              return optimize(content);
            },
            cache: {
              keys: (defaultCacheKeys, absoluteFrom) => {
                const keys = getCustomCacheInvalidationKeysSync();

                return {
                  ...defaultCacheKeys,
                  keys,
                };
              },
            },
          },
        },
      ],
    }),
  ],
};

异步函数

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/*.png",
          to: "dest/",
          transform: {
            transformer(content, path) {
              return optimize(content);
            },
            cache: {
              keys: async (defaultCacheKeys, absoluteFrom) => {
                const keys = await getCustomCacheInvalidationKeysAsync();

                return {
                  ...defaultCacheKeys,
                  keys,
                };
              },
            },
          },
        },
      ],
    }),
  ],
};

transformAll

类型

type transformAll = (
  data: {
    data: Buffer;
    sourceFilename: string;
    absoluteFilename: string;
  }[],
) => any;

默认值:undefined

允许你修改多个文件的内容并将合并结果保存到一个文件中。

[!注意]

必须指定 to 选项并指向一个文件。文件名中只允许使用 [contenthash][fullhash] 模板字符串。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/**/*.txt",
          to: "dest/file.txt",
          // The `assets` argument is an array of assets matched by the pattern `from` ("src/**/*.txt")
          transformAll(assets) {
            const result = assets.reduce((accumulator, asset) => {
              // The asset content can be obtained from `asset.source` using `source` method.
              // The asset content is a [`Buffer`](https://node.org.cn/api/buffer.html) object, it could be converted to a `String` to be processed using `content.toString()`
              const content = asset.data;

              accumulator = `${accumulator}${content}\n`;
              return accumulator;
            }, "");

            return result;
          },
        },
      ],
    }),
  ],
};

noErrorOnMissing

类型

type noErrorOnMissing = boolean;

默认值:false

如果文件缺失,不生成错误。

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

info

类型

type info =
  | Record<string, any>
  | ((item: {
      absoluteFilename: string;
      sourceFilename: string;
      filename: string;
      toType: ToType;
    }) => Record<string, any>);

默认值:undefined

允许添加资产信息。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        "relative/path/to/file.ext",
        {
          from: "**/*",
          // Terser skip this file for minification
          info: { minimized: true },
        },
      ],
    }),
  ],
};

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        "relative/path/to/file.ext",
        {
          from: "**/*",
          // Terser skip this file for minimization
          info: (file) => ({ minimized: true }),
        },
      ],
    }),
  ],
};

选项

concurrency

type

type concurrency = number;

默认值:100

限制同时对文件系统进行请求的数量。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [...patterns],
      options: { concurrency: 50 },
    }),
  ],
};

示例

from 的不同变体(globfiledir)。

考虑以下文件结构

src/directory-nested/deep-nested/deepnested-file.txt
src/directory-nested/nested-file.txt
源是 Glob

你在 from 中指定的所有内容都将包含在结果中

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/directory-nested/**/*",
        },
      ],
    }),
  ],
};

结果

src/directory-nested/deep-nested/deepnested-file.txt,
src/directory-nested/nested-file.txt

如果你不希望结果路径以 src/directory-nested/ 开头,那么你应该将 src/directory-nested/ 移动到 context,这样 from 中只剩下 glob 模式 **/*

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "**/*",
          context: path.resolve(__dirname, "src", "directory-nested"),
        },
      ],
    }),
  ],
};

结果

deep-nested/deepnested-file.txt,
nested-file.txt
源是目录

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(__dirname, "src", "directory-nested"),
        },
      ],
    }),
  ],
};

结果

deep-nested/deepnested-file.txt,
nested-file.txt

从技术上讲,这等同于使用 **/* 并将预定义上下文设置为指定目录。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "**/*",
          context: path.resolve(__dirname, "src", "directory-nested"),
        },
      ],
    }),
  ],
};

结果

deep-nested/deepnested-file.txt,
nested-file.txt
源是文件
module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: path.resolve(
            __dirname,
            "src",
            "directory-nested",
            "nested-file.txt",
          ),
        },
      ],
    }),
  ],
};

结果

nested-file.txt

从技术上讲,这是一个文件名,其预定义上下文等于文件所在目录 path.dirname(pathToFile)

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "nested-file.txt",
          context: path.resolve(__dirname, "src", "directory-nested"),
        },
      ],
    }),
  ],
};

结果

nested-file.txt

忽略文件

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: path.posix.join(
            path.resolve(__dirname, "src").replace(/\\/g, "/"),
            "**/*",
          ),
          globOptions: {
            ignore: [
              // Ignore all `txt` files
              "**/*.txt",
              // Ignore all files in all subdirectories
              "**/subdir/**",
            ],
          },
        },
      ],
    }),
  ],
};

平铺复制

移除所有目录引用,只复制文件名。

[!警告]

如果文件同名,结果将是不确定的。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: "src/**/*",
          to: "[name][ext]",
        },
      ],
    }),
  ],
};

结果

file-1.txt
file-2.txt
nested-file.txt

复制到新目录

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          // When copying files starting with a dot, must specify the toType option
          // toType: "file",
          to({ context, absoluteFilename }) {
            return `newdirectory/${path.relative(context, absoluteFilename)}`;
          },
          from: "directory",
        },
      ],
    }),
  ],
};

结果

"newdirectory/file-1.txt",
"newdirectory/nestedfile.txt",
"newdirectory/nested/deep-nested/deepnested.txt",
"newdirectory/nested/nestedfile.txt",

跳过通过最小化器处理 JavaScript 文件

如果你需要简单地将 *.js 文件“原样”复制到目标目录,而无需使用 Terser 对其进行评估和最小化,这会很有用。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        "relative/path/to/file.ext",
        {
          from: "**/*",
          // Terser skip this file for minimization
          info: { minimized: true },
        },
      ],
    }),
  ],
};
yarn workspacesmonorepos

当使用 yarn workspacesmonorepos 时,由于包提升的方式,从 node_modules 的相对复制路径可能会中断。为避免这种情况,你应该通过使用 require.resolve 明确指定从何处复制文件。

webpack.config.js

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: `${path.dirname(
            require.resolve(`${moduleName}/package.json`),
          )}/target`,
          to: "target",
        },
      ],
    }),
  ],
};

贡献

我们欢迎所有贡献!

如果你是新用户,请在提交问题或拉取请求之前花一些时间阅读我们的贡献指南。

贡献

许可证

MIT