如何通过CopyWebpackPlugin过滤Webpack已打包文件仅复制未打包文件?
Great question! I’ve run into this exact need before when working on projects where I wanted to avoid duplicating files that Webpack already handled. Here are two reliable approaches to make CopyWebpackPlugin only copy unbundled files:
Approach 1: Use the filter Option with Compilation Assets
CopyWebpackPlugin’s filter function lets you skip files conditionally, and you can leverage Webpack’s compilation object to check which files have already been bundled. This works because the compilation tracks all assets that Webpack has processed.
Here’s a concrete example:
const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { plugins: [ new CopyWebpackPlugin({ patterns: [ { from: 'src/assets/', to: 'assets/', filter: async (resourcePath, context) => { // Grab the compilation object from the plugin context const compilation = context.compilation; // Generate the expected output path for the current file // Adjust this logic to match your project's file structure and output rules const relativeFromPath = resourcePath.replace(context.context, '').slice(1); const outputPath = `assets/${relativeFromPath.split('src/assets/')[1]}`; // Return true to copy the file, false to skip it return !compilation.assets[outputPath]; } } ] }) ] };
How it works:
- The
filterfunction runs for every file matched by yourfrompattern. - We retrieve the
compilationobject, which holds all assets Webpack has already bundled (stored incompilation.assets). - We generate the path the file would take in your output directory, then check if that path already exists in the bundled assets. If not, we copy it; if yes, we skip it.
Pro tip: If your Webpack config uses hashes in filenames (like [contenthash]), you’ll need to adjust the path matching logic to account for that—for example, stripping the hash from bundled asset keys to compare against the original file path.
Approach 2: Collect Bundled Files First with a Custom Hook
If you need more control over which files are considered "bundled," you can use a custom Webpack hook to collect all bundled files before CopyWebpackPlugin runs, then use that list to filter files.
Example configuration:
const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { plugins: [ // Custom plugin to collect bundled files { apply: (compiler) => { compiler.hooks.afterCompile.tap('TrackBundledFiles', (compilation) => { // Store all bundled asset paths in a custom property on compilation compilation.bundledAssets = Object.keys(compilation.assets); }); } }, new CopyWebpackPlugin({ patterns: [ { from: 'src/assets/', to: 'assets/', filter: (resourcePath, context) => { // Map the source file path to its expected output path const assetOutputPath = resourcePath.replace( `${context.context}/src/assets/`, 'assets/' ); // Skip if the file is already in the bundled assets list return !context.compilation.bundledAssets.includes(assetOutputPath); } } ] }) ] };
How it works:
- We use Webpack’s
afterCompilehook to run code right after Webpack finishes compiling. This hook gives us access to the final list of bundled assets. - We store the list of bundled asset paths in a custom property on the
compilationobject. - In CopyWebpackPlugin’s
filterfunction, we check if the current file’s expected output path is in that list—if not, we copy it.
Key Notes
- Always test your path matching logic thoroughly, especially if your project has nested directories or custom output rules.
- If you’re using Webpack 5+, make sure you’re on a recent version of CopyWebpackPlugin (v6 or higher) to ensure the
filterfunction and context work as expected.
内容的提问来源于stack exchange,提问作者Simme Nilsson




