🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more →
Socket
Book a DemoInstallSign in
Socket

copy-webpack-plugin

Package Overview
Dependencies
Maintainers
3
Versions
81
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

copy-webpack-plugin - npm Package Compare versions

Comparing version

to
11.0.0

dist/utils.js

1053

dist/index.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
const path = require("path");
var _path = _interopRequireDefault(require("path"));
const {
validate
} = require("schema-utils");
var _os = _interopRequireDefault(require("os"));
const serialize = require("serialize-javascript");
var _crypto = _interopRequireDefault(require("crypto"));
const normalizePath = require("normalize-path");
var _webpack = _interopRequireDefault(require("webpack"));
const globParent = require("glob-parent");
var _schemaUtils = require("schema-utils");
const fastGlob = require("fast-glob"); // @ts-ignore
var _pLimit = _interopRequireDefault(require("p-limit"));
var _globby = _interopRequireDefault(require("globby"));
const {
version
} = require("../package.json");
var _findCacheDir = _interopRequireDefault(require("find-cache-dir"));
const schema = require("./options.json");
var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
const {
readFile,
stat,
throttleAll
} = require("./utils");
var _cacache = _interopRequireDefault(require("cacache"));
const template = /\[\\*([\w:]+)\\*\]/i;
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
var _loaderUtils = _interopRequireDefault(require("loader-utils"));
/** @typedef {import("webpack").Compiler} Compiler */
var _normalizePath = _interopRequireDefault(require("normalize-path"));
/** @typedef {import("webpack").Compilation} Compilation */
var _globParent = _interopRequireDefault(require("glob-parent"));
/** @typedef {import("webpack").WebpackError} WebpackError */
var _fastGlob = _interopRequireDefault(require("fast-glob"));
/** @typedef {import("webpack").Asset} Asset */
var _package = require("../package.json");
/** @typedef {import("globby").Options} GlobbyOptions */
var _options = _interopRequireDefault(require("./options.json"));
/** @typedef {import("globby").GlobEntry} GlobEntry */
var _promisify = require("./utils/promisify");
/** @typedef {ReturnType<Compilation["getLogger"]>} WebpackLogger */
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/** @typedef {ReturnType<Compilation["getCache"]>} CacheFacade */
// webpack 5 exposes the sources property to ensure the right version of webpack-sources is used
const {
RawSource
} = // eslint-disable-next-line global-require
_webpack.default.sources || require("webpack-sources");
/** @typedef {ReturnType<ReturnType<Compilation["getCache"]>["getLazyHashedEtag"]>} Etag */
const template = /(\[ext\])|(\[name\])|(\[path\])|(\[folder\])|(\[emoji(?::(\d+))?\])|(\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*))?(?::(\d+))?\])|(\[\d+\])/;
/** @typedef {ReturnType<Compilation["fileSystemInfo"]["mergeSnapshots"]>} Snapshot */
/**
* @typedef {boolean} Force
*/
/**
* @typedef {Object} CopiedResult
* @property {string} sourceFilename
* @property {string} absoluteFilename
* @property {string} filename
* @property {Asset["source"]} source
* @property {Force | undefined} force
* @property {Record<string, any>} info
*/
/**
* @typedef {string} StringPattern
*/
/**
* @typedef {boolean} NoErrorOnMissing
*/
/**
* @typedef {string} Context
*/
/**
* @typedef {string} From
*/
/**
* @callback ToFunction
* @param {{ context: string, absoluteFilename?: string }} pathData
* @return {string | Promise<string>}
*/
/**
* @typedef {string | ToFunction} To
*/
/**
* @typedef {"dir" | "file" | "template"} ToType
*/
/**
* @callback TransformerFunction
* @param {Buffer} input
* @param {string} absoluteFilename
* @returns {string | Buffer | Promise<string> | Promise<Buffer>}
*/
/**
* @typedef {{ keys: { [key: string]: any } } | { keys: ((defaultCacheKeys: { [key: string]: any }, absoluteFilename: string) => Promise<{ [key: string]: any }>) }} TransformerCacheObject
*/
/**
* @typedef {Object} TransformerObject
* @property {TransformerFunction} transformer
* @property {boolean | TransformerCacheObject} [cache]
*/
/**
* @typedef {TransformerFunction | TransformerObject} Transform
*/
/**
* @callback Filter
* @param {string} filepath
* @returns {boolean | Promise<boolean>}
*/
/**
* @callback TransformAllFunction
* @param {{ data: Buffer, sourceFilename: string, absoluteFilename: string }[]} data
* @returns {string | Buffer | Promise<string> | Promise<Buffer>}
*/
/**
* @typedef { Record<string, any> | ((item: { absoluteFilename: string, sourceFilename: string, filename: string, toType: ToType }) => Record<string, any>) } Info
*/
/**
* @typedef {Object} ObjectPattern
* @property {From} from
* @property {GlobbyOptions} [globOptions]
* @property {Context} [context]
* @property {To} [to]
* @property {ToType} [toType]
* @property {Info} [info]
* @property {Filter} [filter]
* @property {Transform} [transform]
* @property {TransformAllFunction} [transformAll]
* @property {Force} [force]
* @property {number} [priority]
* @property {NoErrorOnMissing} [noErrorOnMissing]
*/
/**
* @typedef {StringPattern | ObjectPattern} Pattern
*/
/**
* @typedef {Object} AdditionalOptions
* @property {number} [concurrency]
*/
/**
* @typedef {Object} PluginOptions
* @property {Pattern[]} patterns
* @property {AdditionalOptions} [options]
*/
class CopyPlugin {
constructor(options = {}) {
(0, _schemaUtils.validate)(_options.default, options, {
/**
* @param {PluginOptions} [options]
*/
constructor(options = {
patterns: []
}) {
validate(
/** @type {Schema} */
schema, options, {
name: "Copy Plugin",
baseDataPath: "options"
});
/**
* @private
* @type {Pattern[]}
*/
this.patterns = options.patterns;
/**
* @private
* @type {AdditionalOptions}
*/
this.options = options.options || {};
}
/**
* @private
* @param {Compilation} compilation
* @param {number} startTime
* @param {string} dependency
* @returns {Promise<Snapshot | undefined>}
*/
static async createSnapshot(compilation, startTime, dependency) {
if (!compilation.fileSystemInfo) {
return;
} // eslint-disable-next-line consistent-return
// eslint-disable-next-line consistent-return
return new Promise((resolve, reject) => {
compilation.fileSystemInfo.createSnapshot(startTime, [dependency], // eslint-disable-next-line no-undefined
compilation.fileSystemInfo.createSnapshot(startTime, [dependency], // @ts-ignore
// eslint-disable-next-line no-undefined
undefined, // eslint-disable-next-line no-undefined

@@ -77,13 +212,18 @@ undefined, null, (error, snapshot) => {

resolve(snapshot);
resolve(
/** @type {Snapshot} */
snapshot);
});
});
}
/**
* @private
* @param {Compilation} compilation
* @param {Snapshot} snapshot
* @returns {Promise<boolean | undefined>}
*/
static async checkSnapshotValid(compilation, snapshot) {
if (!compilation.fileSystemInfo) {
return;
} // eslint-disable-next-line consistent-return
// eslint-disable-next-line consistent-return
return new Promise((resolve, reject) => {

@@ -100,21 +240,64 @@ compilation.fileSystemInfo.checkSnapshotValid(snapshot, (error, isValid) => {

}
/**
* @private
* @param {Compiler} compiler
* @param {Compilation} compilation
* @param {Buffer} source
* @returns {string}
*/
static async runPattern(compiler, compilation, logger, cache, inputPattern, index) {
const pattern = typeof inputPattern === "string" ? {
from: inputPattern
} : { ...inputPattern
static getContentHash(compiler, compilation, source) {
const {
outputOptions
} = compilation;
const {
hashDigest,
hashDigestLength,
hashFunction,
hashSalt
} = outputOptions;
const hash = compiler.webpack.util.createHash(
/** @type {string} */
hashFunction);
if (hashSalt) {
hash.update(hashSalt);
}
hash.update(source);
const fullContentHash = hash.digest(hashDigest);
return fullContentHash.toString().slice(0, hashDigestLength);
}
/**
* @private
* @param {typeof import("globby").globby} globby
* @param {Compiler} compiler
* @param {Compilation} compilation
* @param {WebpackLogger} logger
* @param {CacheFacade} cache
* @param {ObjectPattern & { context: string }} inputPattern
* @param {number} index
* @returns {Promise<Array<CopiedResult | undefined> | undefined>}
*/
static async runPattern(globby, compiler, compilation, logger, cache, inputPattern, index) {
const {
RawSource
} = compiler.webpack.sources;
const pattern = { ...inputPattern
};
pattern.fromOrigin = pattern.from;
pattern.from = _path.default.normalize(pattern.from);
pattern.compilerContext = compiler.context;
pattern.context = _path.default.normalize(typeof pattern.context !== "undefined" ? !_path.default.isAbsolute(pattern.context) ? _path.default.join(pattern.compilerContext, pattern.context) : pattern.context : pattern.compilerContext);
logger.log(`starting to process a pattern from '${pattern.from}' using '${pattern.context}' context`);
const originalFrom = pattern.from;
const normalizedOriginalFrom = path.normalize(originalFrom);
logger.log(`starting to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context`);
let absoluteFrom;
if (_path.default.isAbsolute(pattern.from)) {
pattern.absoluteFrom = pattern.from;
if (path.isAbsolute(normalizedOriginalFrom)) {
absoluteFrom = normalizedOriginalFrom;
} else {
pattern.absoluteFrom = _path.default.resolve(pattern.context, pattern.from);
absoluteFrom = path.resolve(pattern.context, normalizedOriginalFrom);
}
logger.debug(`getting stats for '${pattern.absoluteFrom}'...`);
logger.debug(`getting stats for '${absoluteFrom}'...`);
const {

@@ -126,20 +309,32 @@ inputFileSystem

try {
stats = await (0, _promisify.stat)(inputFileSystem, pattern.absoluteFrom);
stats = await stat(inputFileSystem, absoluteFrom);
} catch (error) {// Nothing
}
/**
* @type {"file" | "dir" | "glob"}
*/
let fromType;
if (stats) {
if (stats.isDirectory()) {
pattern.fromType = "dir";
logger.debug(`determined '${pattern.absoluteFrom}' is a directory`);
fromType = "dir";
logger.debug(`determined '${absoluteFrom}' is a directory`);
} else if (stats.isFile()) {
pattern.fromType = "file";
logger.debug(`determined '${pattern.absoluteFrom}' is a file`);
fromType = "file";
logger.debug(`determined '${absoluteFrom}' is a file`);
} else {
logger.debug(`determined '${pattern.absoluteFrom}' is a glob`);
// Fallback
fromType = "glob";
logger.debug(`determined '${absoluteFrom}' is unknown`);
}
} // eslint-disable-next-line no-param-reassign
} else {
fromType = "glob";
logger.debug(`determined '${absoluteFrom}' is a glob`);
}
/** @type {GlobbyOptions & { objectMode: true }} */
pattern.globOptions = { ...{
const globOptions = { ...{
followSymbolicLinks: true

@@ -152,218 +347,181 @@ },

}
}; // TODO remove after drop webpack@4
}; // @ts-ignore
if (compiler.webpack && inputFileSystem.lstat && inputFileSystem.stat && inputFileSystem.lstatSync && inputFileSystem.statSync && inputFileSystem.readdir && inputFileSystem.readdirSync) {
pattern.globOptions.fs = inputFileSystem;
}
globOptions.fs = inputFileSystem;
let glob;
switch (pattern.fromType) {
switch (fromType) {
case "dir":
compilation.contextDependencies.add(pattern.absoluteFrom);
logger.debug(`added '${pattern.absoluteFrom}' as a context dependency`);
/* eslint-disable no-param-reassign */
compilation.contextDependencies.add(absoluteFrom);
logger.debug(`added '${absoluteFrom}' as a context dependency`);
pattern.context = absoluteFrom;
glob = path.posix.join(fastGlob.escapePath(normalizePath(path.resolve(absoluteFrom))), "**/*");
absoluteFrom = path.join(absoluteFrom, "**/*");
pattern.context = pattern.absoluteFrom;
pattern.glob = _path.default.posix.join(_fastGlob.default.escapePath((0, _normalizePath.default)(_path.default.resolve(pattern.absoluteFrom))), "**/*");
pattern.absoluteFrom = _path.default.join(pattern.absoluteFrom, "**/*");
if (typeof pattern.globOptions.dot === "undefined") {
pattern.globOptions.dot = true;
if (typeof globOptions.dot === "undefined") {
globOptions.dot = true;
}
/* eslint-enable no-param-reassign */
break;
case "file":
compilation.fileDependencies.add(pattern.absoluteFrom);
logger.debug(`added '${pattern.absoluteFrom}' as a file dependency`);
/* eslint-disable no-param-reassign */
compilation.fileDependencies.add(absoluteFrom);
logger.debug(`added '${absoluteFrom}' as a file dependency`);
pattern.context = path.dirname(absoluteFrom);
glob = fastGlob.escapePath(normalizePath(path.resolve(absoluteFrom)));
pattern.context = _path.default.dirname(pattern.absoluteFrom);
pattern.glob = _fastGlob.default.escapePath((0, _normalizePath.default)(_path.default.resolve(pattern.absoluteFrom)));
if (typeof pattern.globOptions.dot === "undefined") {
pattern.globOptions.dot = true;
if (typeof globOptions.dot === "undefined") {
globOptions.dot = true;
}
/* eslint-enable no-param-reassign */
break;
case "glob":
default:
{
const contextDependencies = _path.default.normalize((0, _globParent.default)(pattern.absoluteFrom));
const contextDependencies = path.normalize(globParent(absoluteFrom));
compilation.contextDependencies.add(contextDependencies);
logger.debug(`added '${contextDependencies}' as a context dependency`);
/* eslint-disable no-param-reassign */
pattern.fromType = "glob";
pattern.glob = _path.default.isAbsolute(pattern.fromOrigin) ? pattern.fromOrigin : _path.default.posix.join(_fastGlob.default.escapePath((0, _normalizePath.default)(_path.default.resolve(pattern.context))), pattern.fromOrigin);
/* eslint-enable no-param-reassign */
glob = path.isAbsolute(originalFrom) ? originalFrom : path.posix.join(fastGlob.escapePath(normalizePath(path.resolve(pattern.context))), originalFrom);
}
}
logger.log(`begin globbing '${pattern.glob}'...`);
let paths;
logger.log(`begin globbing '${glob}'...`);
/**
* @type {GlobEntry[]}
*/
let globEntries;
try {
paths = await (0, _globby.default)(pattern.glob, pattern.globOptions);
globEntries = await globby(glob, globOptions);
} catch (error) {
compilation.errors.push(error);
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
if (paths.length === 0) {
if (globEntries.length === 0) {
if (pattern.noErrorOnMissing) {
logger.log(`finished to process a pattern from '${pattern.from}' using '${pattern.context}' context to '${pattern.to}'`);
logger.log(`finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context to '${pattern.to}'`);
return;
}
const missingError = new Error(`unable to locate '${pattern.glob}' glob`);
compilation.errors.push(missingError);
const missingError = new Error(`unable to locate '${glob}' glob`);
compilation.errors.push(
/** @type {WebpackError} */
missingError);
return;
}
/**
* @type {Array<CopiedResult | undefined>}
*/
const filteredPaths = (await Promise.all(paths.map(async item => {
// Exclude directories
if (!item.dirent.isFile()) {
return false;
}
if (pattern.filter) {
let isFiltered;
let copiedResult;
try {
isFiltered = await pattern.filter(item.path);
} catch (error) {
compilation.errors.push(error);
return false;
try {
copiedResult = await Promise.all(globEntries.map(
/**
* @param {GlobEntry} globEntry
* @returns {Promise<CopiedResult | undefined>}
*/
async globEntry => {
// Exclude directories
if (!globEntry.dirent.isFile()) {
return;
}
if (!isFiltered) {
logger.log(`skip '${item.path}', because it was filtered`);
}
if (pattern.filter) {
let isFiltered;
return isFiltered ? item : false;
}
try {
isFiltered = await pattern.filter(globEntry.path);
} catch (error) {
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
return item;
}))).filter(item => item);
if (!isFiltered) {
logger.log(`skip '${globEntry.path}', because it was filtered`);
return;
}
}
if (filteredPaths.length === 0) {
// TODO should be error in the next major release
logger.log(`finished to process a pattern from '${pattern.from}' using '${pattern.context}' context to '${pattern.to}'`);
return;
}
const from = globEntry.path;
logger.debug(`found '${from}'`); // `globby`/`fast-glob` return the relative path when the path contains special characters on windows
const files = await Promise.all(filteredPaths.map(async item => {
const from = item.path;
logger.debug(`found '${from}'`); // `globby`/`fast-glob` return the relative path when the path contains special characters on windows
const absoluteFilename = path.resolve(pattern.context, from);
const to = typeof pattern.to === "function" ? await pattern.to({
context: pattern.context,
absoluteFilename
}) : path.normalize(typeof pattern.to !== "undefined" ? pattern.to : "");
const toType = pattern.toType ? pattern.toType : template.test(to) ? "template" : path.extname(to) === "" || to.slice(-1) === path.sep ? "dir" : "file";
logger.log(`'to' option '${to}' determinated as '${toType}'`);
const relativeFrom = path.relative(pattern.context, absoluteFilename);
let filename = toType === "dir" ? path.join(to, relativeFrom) : to;
const absoluteFilename = _path.default.resolve(pattern.context, from);
if (path.isAbsolute(filename)) {
filename = path.relative(
/** @type {string} */
compiler.options.output.path, filename);
}
pattern.to = typeof pattern.to !== "function" ? _path.default.normalize(typeof pattern.to !== "undefined" ? pattern.to : "") : await pattern.to({
context: pattern.context,
absoluteFilename
});
logger.log(`determined that '${from}' should write to '${filename}'`);
const sourceFilename = normalizePath(path.relative(compiler.context, absoluteFilename)); // If this came from a glob or dir, add it to the file dependencies
const isToDirectory = _path.default.extname(pattern.to) === "" || pattern.to.slice(-1) === _path.default.sep;
if (fromType === "dir" || fromType === "glob") {
compilation.fileDependencies.add(absoluteFilename);
logger.debug(`added '${absoluteFilename}' as a file dependency`);
}
switch (true) {
// if toType already exists
case !!pattern.toType:
break;
let cacheEntry;
logger.debug(`getting cache for '${absoluteFilename}'...`);
case template.test(pattern.to):
pattern.toType = "template";
break;
try {
cacheEntry = await cache.getPromise(`${sourceFilename}|${index}`, null);
} catch (error) {
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
/**
* @type {Asset["source"] | undefined}
*/
case isToDirectory:
pattern.toType = "dir";
break;
default:
pattern.toType = "file";
}
let source;
logger.log(`'to' option '${pattern.to}' determinated as '${pattern.toType}'`);
const relativeFrom = pattern.flatten ? _path.default.basename(absoluteFilename) : _path.default.relative(pattern.context, absoluteFilename);
let filename = pattern.toType === "dir" ? _path.default.join(pattern.to, relativeFrom) : pattern.to;
if (cacheEntry) {
logger.debug(`found cache for '${absoluteFilename}'...`);
let isValidSnapshot;
logger.debug(`checking snapshot on valid for '${absoluteFilename}'...`);
if (_path.default.isAbsolute(filename)) {
filename = _path.default.relative(compiler.options.output.path, filename);
}
logger.log(`determined that '${from}' should write to '${filename}'`);
const sourceFilename = (0, _normalizePath.default)(_path.default.relative(pattern.compilerContext, absoluteFilename));
return {
absoluteFilename,
sourceFilename,
filename
};
}));
let assets;
try {
assets = await Promise.all(files.map(async file => {
const {
absoluteFilename,
sourceFilename,
filename
} = file;
const result = {
absoluteFilename,
sourceFilename,
filename,
force: pattern.force,
info: typeof pattern.info === "function" ? pattern.info(file) || {} : pattern.info || {}
}; // If this came from a glob or dir, add it to the file dependencies
if (pattern.fromType === "dir" || pattern.fromType === "glob") {
compilation.fileDependencies.add(absoluteFilename);
logger.debug(`added '${absoluteFilename}' as a file dependency`);
}
if (cache) {
let cacheEntry;
logger.debug(`getting cache for '${absoluteFilename}'...`);
try {
cacheEntry = await cache.getPromise(`${sourceFilename}|${index}`, null);
isValidSnapshot = await CopyPlugin.checkSnapshotValid(compilation, cacheEntry.snapshot);
} catch (error) {
compilation.errors.push(error);
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
if (cacheEntry) {
logger.debug(`found cache for '${absoluteFilename}'...`);
let isValidSnapshot;
logger.debug(`checking snapshot on valid for '${absoluteFilename}'...`);
try {
isValidSnapshot = await CopyPlugin.checkSnapshotValid(compilation, cacheEntry.snapshot);
} catch (error) {
compilation.errors.push(error);
return;
}
if (isValidSnapshot) {
logger.debug(`snapshot for '${absoluteFilename}' is valid`);
result.source = cacheEntry.source;
} else {
logger.debug(`snapshot for '${absoluteFilename}' is invalid`);
}
if (isValidSnapshot) {
logger.debug(`snapshot for '${absoluteFilename}' is valid`);
({
source
} = cacheEntry);
} else {
logger.debug(`missed cache for '${absoluteFilename}'`);
logger.debug(`snapshot for '${absoluteFilename}' is invalid`);
}
} else {
logger.debug(`missed cache for '${absoluteFilename}'`);
}
if (!result.source) {
let startTime;
if (cache) {
startTime = Date.now();
}
if (!source) {
const startTime = Date.now();
logger.debug(`reading '${absoluteFilename}'...`);

@@ -373,5 +531,7 @@ let data;

try {
data = await (0, _promisify.readFile)(inputFileSystem, absoluteFilename);
data = await readFile(inputFileSystem, absoluteFilename);
} catch (error) {
compilation.errors.push(error);
compilation.errors.push(
/** @type {WebpackError} */
error);
return;

@@ -381,30 +541,75 @@ }

logger.debug(`read '${absoluteFilename}'`);
result.source = new RawSource(data);
source = new RawSource(data);
let snapshot;
logger.debug(`creating snapshot for '${absoluteFilename}'...`);
if (cache) {
let snapshot;
logger.debug(`creating snapshot for '${absoluteFilename}'...`);
try {
snapshot = await CopyPlugin.createSnapshot(compilation, startTime, absoluteFilename);
} catch (error) {
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
if (snapshot) {
logger.debug(`created snapshot for '${absoluteFilename}'`);
logger.debug(`storing cache for '${absoluteFilename}'...`);
try {
snapshot = await CopyPlugin.createSnapshot(compilation, startTime, absoluteFilename);
await cache.storePromise(`${sourceFilename}|${index}`, null, {
source,
snapshot
});
} catch (error) {
compilation.errors.push(error);
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
if (snapshot) {
logger.debug(`created snapshot for '${absoluteFilename}'`);
logger.debug(`storing cache for '${absoluteFilename}'...`);
logger.debug(`stored cache for '${absoluteFilename}'`);
}
}
try {
await cache.storePromise(`${sourceFilename}|${index}`, null, {
source: result.source,
snapshot
});
} catch (error) {
compilation.errors.push(error);
return;
if (pattern.transform) {
/**
* @type {TransformerObject}
*/
const transformObj = typeof pattern.transform === "function" ? {
transformer: pattern.transform
} : pattern.transform;
if (transformObj.transformer) {
logger.log(`transforming content for '${absoluteFilename}'...`);
const buffer = source.buffer();
if (transformObj.cache) {
// TODO: remove in the next major release
const hasher = compiler.webpack && compiler.webpack.util && compiler.webpack.util.createHash ? compiler.webpack.util.createHash("xxhash64") : // eslint-disable-next-line global-require
require("crypto").createHash("md4");
const defaultCacheKeys = {
version,
sourceFilename,
transform: transformObj.transformer,
contentHash: hasher.update(buffer).digest("hex"),
index
};
const cacheKeys = `transform|${serialize(typeof transformObj.cache === "boolean" ? defaultCacheKeys : typeof transformObj.cache.keys === "function" ? await transformObj.cache.keys(defaultCacheKeys, absoluteFilename) : { ...defaultCacheKeys,
...transformObj.cache.keys
})}`;
logger.debug(`getting transformation cache for '${absoluteFilename}'...`);
const cacheItem = cache.getItemCache(cacheKeys, cache.getLazyHashedEtag(source));
source = await cacheItem.getPromise();
logger.debug(source ? `found transformation cache for '${absoluteFilename}'` : `no transformation cache for '${absoluteFilename}'`);
if (!source) {
const transformed = await transformObj.transformer(buffer, absoluteFilename);
source = new RawSource(transformed);
logger.debug(`caching transformation for '${absoluteFilename}'...`);
await cacheItem.storePromise(source);
logger.debug(`cached transformation for '${absoluteFilename}'`);
}
logger.debug(`stored cache for '${absoluteFilename}'`);
} else {
source = new RawSource(await transformObj.transformer(buffer, absoluteFilename));
}

@@ -414,135 +619,262 @@ }

if (pattern.transform) {
logger.log(`transforming content for '${absoluteFilename}'...`);
const buffer = result.source.source();
let info = typeof pattern.info === "undefined" ? {} : typeof pattern.info === "function" ? pattern.info({
absoluteFilename,
sourceFilename,
filename,
toType
}) || {} : pattern.info || {};
if (pattern.cacheTransform) {
const defaultCacheKeys = {
version: _package.version,
if (toType === "template") {
logger.log(`interpolating template '${filename}' for '${sourceFilename}'...`);
const contentHash = CopyPlugin.getContentHash(compiler, compilation, source.buffer());
const ext = path.extname(sourceFilename);
const base = path.basename(sourceFilename);
const name = base.slice(0, base.length - ext.length);
const data = {
filename: normalizePath(path.relative(pattern.context, absoluteFilename)),
contentHash,
chunk: {
name,
id:
/** @type {string} */
sourceFilename,
transform: pattern.transform,
contentHash: _crypto.default.createHash("md4").update(buffer).digest("hex"),
index
};
const cacheKeys = `transform|${(0, _serializeJavascript.default)(typeof pattern.cacheTransform.keys === "function" ? await pattern.cacheTransform.keys(defaultCacheKeys, absoluteFilename) : { ...defaultCacheKeys,
...pattern.cacheTransform.keys
})}`;
let cacheItem;
let cacheDirectory;
logger.debug(`getting transformation cache for '${absoluteFilename}'...`); // webpack@5 API
hash: contentHash
}
};
const {
path: interpolatedFilename,
info: assetInfo
} = compilation.getPathWithInfo(normalizePath(filename), data);
info = { ...info,
...assetInfo
};
filename = interpolatedFilename;
logger.log(`interpolated template '${filename}' for '${sourceFilename}'`);
} else {
filename = normalizePath(filename);
} // eslint-disable-next-line consistent-return
if (cache) {
cacheItem = cache.getItemCache(cacheKeys, cache.getLazyHashedEtag(result.source));
result.source = await cacheItem.getPromise();
} else {
cacheDirectory = pattern.cacheTransform.directory ? pattern.cacheTransform.directory : typeof pattern.cacheTransform === "string" ? pattern.cacheTransform : (0, _findCacheDir.default)({
name: "copy-webpack-plugin"
}) || _os.default.tmpdir();
let cached;
try {
cached = await _cacache.default.get(cacheDirectory, cacheKeys);
} catch (error) {
logger.debug(`no transformation cache for '${absoluteFilename}'...`);
} // eslint-disable-next-line no-undefined
return {
sourceFilename,
absoluteFilename,
filename,
source,
info,
force: pattern.force
};
}));
} catch (error) {
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
if (copiedResult.length === 0) {
if (pattern.noErrorOnMissing) {
logger.log(`finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context to '${pattern.to}'`);
return;
}
result.source = cached ? new RawSource(cached.data) : undefined;
}
const missingError = new Error(`unable to locate '${glob}' glob after filtering paths`);
compilation.errors.push(
/** @type {WebpackError} */
missingError);
return;
}
logger.debug(result.source ? `found transformation cache for '${absoluteFilename}'` : `no transformation cache for '${absoluteFilename}'`);
logger.log(`finished to process a pattern from '${normalizedOriginalFrom}' using '${pattern.context}' context`); // eslint-disable-next-line consistent-return
if (!result.source) {
const transformed = await pattern.transform(buffer, absoluteFilename);
result.source = new RawSource(transformed);
logger.debug(`caching transformation for '${absoluteFilename}'...`); // webpack@5 API
return copiedResult;
}
/**
* @param {Compiler} compiler
*/
if (cache) {
await cacheItem.storePromise(result.source);
} else {
try {
await _cacache.default.put(cacheDirectory, cacheKeys, transformed);
} catch (error) {
compilation.errors.push(error);
return;
}
}
logger.debug(`cached transformation for '${absoluteFilename}'`);
}
} else {
result.source = new RawSource(await pattern.transform(buffer, absoluteFilename));
apply(compiler) {
const pluginName = this.constructor.name;
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
const logger = compilation.getLogger("copy-webpack-plugin");
const cache = compilation.getCache("CopyWebpackPlugin");
/**
* @type {typeof import("globby").globby}
*/
let globby;
compilation.hooks.processAssets.tapAsync({
name: "copy-webpack-plugin",
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
}, async (unusedAssets, callback) => {
if (typeof globby === "undefined") {
try {
// @ts-ignore
({
globby
} = await import("globby"));
} catch (error) {
callback(
/** @type {Error} */
error);
return;
}
}
if (pattern.toType === "template") {
logger.log(`interpolating template '${filename}' for '${sourceFilename}'...`); // If it doesn't have an extension, remove it from the pattern
// ie. [name].[ext] or [name][ext] both become [name]
logger.log("starting to add additional assets...");
const copiedResultMap = new Map();
/**
* @type {(() => Promise<void>)[]}
*/
if (!_path.default.extname(absoluteFilename)) {
// eslint-disable-next-line no-param-reassign
result.filename = result.filename.replace(/\.?\[ext]/g, "");
} // eslint-disable-next-line no-param-reassign
const scheduledTasks = [];
this.patterns.map(
/**
* @param {Pattern} item
* @param {number} index
* @return {number}
*/
(item, index) => scheduledTasks.push(async () => {
/**
* @type {ObjectPattern}
*/
const normalizedPattern = typeof item === "string" ? {
from: item
} : { ...item
};
const context = typeof normalizedPattern.context === "undefined" ? compiler.context : path.isAbsolute(normalizedPattern.context) ? normalizedPattern.context : path.join(compiler.context, normalizedPattern.context);
normalizedPattern.context = context;
/**
* @type {Array<CopiedResult | undefined> | undefined}
*/
let copiedResult;
result.immutable = /\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*))?(?::(\d+))?\]/gi.test(result.filename); // eslint-disable-next-line no-param-reassign
try {
copiedResult = await CopyPlugin.runPattern(globby, compiler, compilation, logger, cache,
/** @type {ObjectPattern & { context: string }} */
normalizedPattern, index);
} catch (error) {
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
result.filename = _loaderUtils.default.interpolateName({
resourcePath: absoluteFilename
}, result.filename, {
content: result.source.source(),
context: pattern.context
}); // Bug in `loader-utils`, package convert `\\` to `/`, need fix in loader-utils
// eslint-disable-next-line no-param-reassign
if (!copiedResult) {
return;
}
/**
* @type {Array<CopiedResult>}
*/
result.filename = _path.default.normalize(result.filename);
logger.log(`interpolated template '${filename}' for '${sourceFilename}'`);
}
if (pattern.transformPath) {
logger.log(`transforming '${result.filename}' for '${absoluteFilename}'...`); // eslint-disable-next-line no-param-reassign
let filteredCopiedResult = copiedResult.filter(
/**
* @param {CopiedResult | undefined} result
* @returns {result is CopiedResult}
*/
result => Boolean(result));
result.immutable = false; // eslint-disable-next-line no-param-reassign
if (typeof normalizedPattern.transformAll !== "undefined") {
if (typeof normalizedPattern.to === "undefined") {
compilation.errors.push(
/** @type {WebpackError} */
new Error(`Invalid "pattern.to" for the "pattern.from": "${normalizedPattern.from}" and "pattern.transformAll" function. The "to" option must be specified.`));
return;
}
result.filename = await pattern.transformPath(result.filename, absoluteFilename);
logger.log(`transformed new '${result.filename}' for '${absoluteFilename}'...`);
} // eslint-disable-next-line no-param-reassign
filteredCopiedResult.sort((a, b) => a.absoluteFilename > b.absoluteFilename ? 1 : a.absoluteFilename < b.absoluteFilename ? -1 : 0);
const mergedEtag = filteredCopiedResult.length === 1 ? cache.getLazyHashedEtag(filteredCopiedResult[0].source) : filteredCopiedResult.reduce(
/**
* @param {Etag} accumulator
* @param {CopiedResult} asset
* @param {number} i
* @return {Etag}
*/
// @ts-ignore
(accumulator, asset, i) => {
// eslint-disable-next-line no-param-reassign
accumulator = cache.mergeEtags(i === 1 ? cache.getLazyHashedEtag(
/** @type {CopiedResult}*/
accumulator.source) : accumulator, cache.getLazyHashedEtag(asset.source));
return accumulator;
});
const cacheItem = cache.getItemCache(`transformAll|${serialize({
version,
from: normalizedPattern.from,
to: normalizedPattern.to,
transformAll: normalizedPattern.transformAll
})}`, mergedEtag);
let transformedAsset = await cacheItem.getPromise();
if (!transformedAsset) {
transformedAsset = {
filename: normalizedPattern.to
};
result.filename = (0, _normalizePath.default)(result.filename); // eslint-disable-next-line consistent-return
try {
transformedAsset.data = await normalizedPattern.transformAll(filteredCopiedResult.map(asset => {
return {
data: asset.source.buffer(),
sourceFilename: asset.sourceFilename,
absoluteFilename: asset.absoluteFilename
};
}));
} catch (error) {
compilation.errors.push(
/** @type {WebpackError} */
error);
return;
}
return result;
}));
} catch (error) {
compilation.errors.push(error);
return;
}
const filename = typeof normalizedPattern.to === "function" ? await normalizedPattern.to({
context
}) : normalizedPattern.to;
logger.log(`finished to process a pattern from '${pattern.from}' using '${pattern.context}' context to '${pattern.to}'`); // eslint-disable-next-line consistent-return
if (template.test(filename)) {
const contentHash = CopyPlugin.getContentHash(compiler, compilation, transformedAsset.data);
const {
path: interpolatedFilename,
info: assetInfo
} = compilation.getPathWithInfo(normalizePath(filename), {
contentHash,
chunk: {
id: "unknown-copied-asset",
hash: contentHash
}
});
transformedAsset.filename = interpolatedFilename;
transformedAsset.info = assetInfo;
}
return assets;
}
const {
RawSource
} = compiler.webpack.sources;
transformedAsset.source = new RawSource(transformedAsset.data);
transformedAsset.force = normalizedPattern.force;
await cacheItem.storePromise(transformedAsset);
}
apply(compiler) {
const pluginName = this.constructor.name;
const limit = (0, _pLimit.default)(this.options.concurrency || 100);
compiler.hooks.thisCompilation.tap(pluginName, compilation => {
const logger = compilation.getLogger("copy-webpack-plugin");
const cache = compilation.getCache ? compilation.getCache("CopyWebpackPlugin") : // eslint-disable-next-line no-undefined
undefined;
compilation.hooks.additionalAssets.tapAsync("copy-webpack-plugin", async callback => {
logger.log("starting to add additional assets...");
let assets;
filteredCopiedResult = [transformedAsset];
}
try {
assets = await Promise.all(this.patterns.map((item, index) => limit(async () => CopyPlugin.runPattern(compiler, compilation, logger, cache, item, index))));
} catch (error) {
compilation.errors.push(error);
callback();
return;
} // Avoid writing assets inside `p-limit`, because it creates concurrency.
const priority = normalizedPattern.priority || 0;
if (!copiedResultMap.has(priority)) {
copiedResultMap.set(priority, []);
}
copiedResultMap.get(priority).push(...filteredCopiedResult);
}));
await throttleAll(this.options.concurrency || 100, scheduledTasks);
const copiedResult = [...copiedResultMap.entries()].sort((a, b) => a[0] - b[0]); // Avoid writing assets inside `p-limit`, because it creates concurrency.
// It could potentially lead to an error - 'Multiple assets emit different content to the same filename'
assets.reduce((acc, val) => acc.concat(val), []).filter(Boolean).forEach(asset => {
copiedResult.reduce((acc, val) => acc.concat(val[1]), []).filter(Boolean).forEach(
/**
* @param {CopiedResult} result
* @returns {void}
*/
result => {
const {

@@ -554,12 +886,3 @@ absoluteFilename,

force
} = asset; // For old version webpack 4
/* istanbul ignore if */
if (typeof compilation.emitAsset !== "function") {
// eslint-disable-next-line no-param-reassign
compilation.assets[filename] = source;
return;
}
} = result;
const existingAsset = compilation.getAsset(filename);

@@ -573,10 +896,5 @@

};
if (asset.immutable) {
info.immutable = true;
}
logger.log(`force updating '${filename}' from '${absoluteFilename}' to compilation assets, because it already exists...`);
compilation.updateAsset(filename, source, { ...info,
...asset.info
...result.info
});

@@ -595,10 +913,5 @@ logger.log(`force updated '${filename}' from '${absoluteFilename}' to compilation assets, because it already exists`);

};
if (asset.immutable) {
info.immutable = true;
}
logger.log(`writing '${filename}' from '${absoluteFilename}' to compilation assets...`);
compilation.emitAsset(filename, source, { ...info,
...asset.info
...result.info
});

@@ -616,4 +929,7 @@ logger.log(`written '${filename}' from '${absoluteFilename}' to compilation assets`);

formatFlag
}) => // eslint-disable-next-line no-undefined
copied ? green(formatFlag("copied")) : undefined);
}) => copied ?
/** @type {Function} */
green(
/** @type {Function} */
formatFlag("copied")) : "");
});

@@ -626,3 +942,2 @@ }

var _default = CopyPlugin;
exports.default = _default;
module.exports = CopyPlugin;

@@ -9,2 +9,4 @@ {

"type": "string",
"description": "Glob or path from where we copy files.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#from",
"minLength": 1

@@ -20,19 +22,41 @@ },

}
]
],
"description": "Output path.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#to"
},
"context": {
"type": "string"
"type": "string",
"description": "A path that determines how to interpret the 'from' path.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#context"
},
"globOptions": {
"type": "object"
"type": "object",
"description": "Allows to configute the glob pattern matching library used by the plugin.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#globoptions"
},
"filter": {
"instanceof": "Function"
"instanceof": "Function",
"description": "Allows to filter copied assets.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#filter"
},
"transformAll": {
"instanceof": "Function",
"description": "Allows you to modify the contents of multiple files and save the result to one file.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#transformall"
},
"toType": {
"enum": ["dir", "file", "template"]
"enum": ["dir", "file", "template"],
"description": "Determinate what is to option - directory, file or template.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#totype"
},
"force": {
"type": "boolean"
"type": "boolean",
"description": "Overwrites files already in 'compilation.assets' (usually added by other plugins/loaders).",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#force"
},
"priority": {
"type": "number",
"description": "Allows to specify the priority of copying files with the same destination name.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#priority"
},
"info": {

@@ -46,34 +70,45 @@ "anyOf": [

}
]
],
"description": "Allows to add assets info.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#info"
},
"flatten": {
"type": "boolean"
},
"transform": {
"instanceof": "Function"
},
"cacheTransform": {
"description": "Allows to modify the file contents.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#transform",
"anyOf": [
{
"type": "boolean"
"instanceof": "Function"
},
{
"type": "string"
},
{
"type": "object",
"additionalProperties": false,
"properties": {
"directory": {
"type": "string",
"absolutePath": true
"transformer": {
"instanceof": "Function",
"description": "Allows to modify the file contents.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#transformer"
},
"keys": {
"cache": {
"description": "Enables/disables and configure caching.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#cache",
"anyOf": [
{
"type": "object",
"additionalProperties": true
"type": "boolean"
},
{
"instanceof": "Function"
"type": "object",
"additionalProperties": false,
"properties": {
"keys": {
"anyOf": [
{
"type": "object",
"additionalProperties": true
},
{
"instanceof": "Function"
}
]
}
}
}

@@ -86,7 +121,6 @@ ]

},
"transformPath": {
"instanceof": "Function"
},
"noErrorOnMissing": {
"type": "boolean"
"type": "boolean",
"description": "Doesn't generate an error on missing file(s).",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#noerroronmissing"
}

@@ -123,3 +157,5 @@ },

"concurrency": {
"type": "number"
"type": "number",
"description": "Limits the number of simultaneous requests to fs.",
"link": "https://github.com/webpack-contrib/copy-webpack-plugin#concurrency"
}

@@ -126,0 +162,0 @@ }

{
"name": "copy-webpack-plugin",
"version": "6.4.1",
"version": "11.0.0",
"description": "Copy files && directories with webpack",

@@ -14,15 +14,19 @@ "license": "MIT",

},
"main": "dist/cjs.js",
"main": "dist/index.js",
"types": "types/index.d.ts",
"engines": {
"node": ">= 10.13.0"
"node": ">= 14.15.0"
},
"scripts": {
"start": "npm run build -- -w",
"clean": "del-cli dist",
"clean": "del-cli dist types",
"prebuild": "npm run clean",
"build": "cross-env NODE_ENV=production babel src -d dist --copy-files",
"build:types": "tsc --declaration --emitDeclarationOnly --outDir types --rootDir src && prettier \"types/**/*.ts\" --write",
"build:code": "cross-env NODE_ENV=production babel src -d dist --copy-files",
"build": "npm-run-all -p \"build:**\"",
"commitlint": "commitlint --from=master",
"security": "npm audit",
"security": "npm audit --production",
"lint:prettier": "prettier --list-different .",
"lint:js": "eslint --cache .",
"lint:types": "tsc --pretty --noEmit",
"lint": "npm-run-all -l -p \"lint:**\"",

@@ -34,52 +38,50 @@ "test:only": "cross-env NODE_ENV=test jest",

"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"
"dist",
"types"
],
"peerDependencies": {
"webpack": "^4.37.0 || ^5.0.0"
"webpack": "^5.1.0"
},
"dependencies": {
"cacache": "^15.0.5",
"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.11",
"glob-parent": "^6.0.1",
"globby": "^13.1.1",
"normalize-path": "^3.0.0",
"p-limit": "^3.0.2",
"schema-utils": "^3.0.0",
"serialize-javascript": "^5.0.1",
"webpack-sources": "^1.4.3"
"schema-utils": "^4.0.0",
"serialize-javascript": "^6.0.0"
},
"devDependencies": {
"@babel/cli": "^7.12.1",
"@babel/core": "^7.12.3",
"@babel/preset-env": "^7.12.1",
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@webpack-contrib/defaults": "^6.3.0",
"@babel/cli": "^7.17.10",
"@babel/core": "^7.17.10",
"@babel/eslint-parser": "^7.16.5",
"@babel/preset-env": "^7.17.12",
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"@types/glob-parent": "^5.1.1",
"@types/normalize-path": "^3.0.0",
"@types/serialize-javascript": "^5.0.2",
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
"babel-jest": "^26.6.3",
"chokidar": "^3.4.3",
"cross-env": "^7.0.2",
"babel-jest": "^28.1.0",
"cross-env": "^7.0.3",
"del": "^6.0.0",
"del-cli": "^3.0.1",
"eslint": "^7.13.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-import": "^2.22.1",
"file-loader": "^6.1.1",
"husky": "^4.3.0",
"del-cli": "^4.0.1",
"eslint": "^8.15.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"file-loader": "^6.2.0",
"husky": "^8.0.1",
"is-gzip": "^2.0.0",
"jest": "^26.6.3",
"lint-staged": "^10.5.1",
"memfs": "^3.2.0",
"jest": "^28.1.0",
"lint-staged": "^12.4.1",
"memfs": "^3.4.1",
"mkdirp": "^1.0.4",
"npm-run-all": "^4.1.5",
"prettier": "^2.1.2",
"standard-version": "^9.0.0",
"webpack": "^5.4.0"
"prettier": "^2.6.2",
"standard-version": "^9.3.1",
"typescript": "^4.6.4",
"webpack": "^5.72.1"
},

@@ -86,0 +88,0 @@ "keywords": [

@@ -25,5 +25,17 @@ <div align="center">

```console
$ npm install copy-webpack-plugin --save-dev
npm install copy-webpack-plugin --save-dev
```
or
```console
yarn add -D copy-webpack-plugin
```
or
```console
pnpm add -D copy-webpack-plugin
```
Then add the plugin to your `webpack` config. For example:

@@ -48,3 +60,3 @@

> â„šī¸ `webpack-copy-plugin` is not designed to copy files generated from the build process; rather, it is to copy files that already exist in the source tree, as part of the build process.
> â„šī¸ `copy-webpack-plugin` is not designed to copy files generated from the build process; rather, it is to copy files that already exist in the source tree, as part of the build process.

@@ -57,2 +69,5 @@ > â„šī¸ If you want `webpack-dev-server` to write files to the output directory during development, you can force it with the [`writeToDisk`](https://github.com/webpack/webpack-dev-middleware#writetodisk) option or the [`write-file-webpack-plugin`](https://github.com/gajus/write-file-webpack-plugin).

- **[`patterns`](#patterns)**
- **[`options`](#options-1)**
The plugin's signature:

@@ -70,3 +85,3 @@

{ from: "source", to: "dest" },
{ from: "other", to: "public" },
"path/to/source", // absolute or relative, files/directories/globs - see below for examples
],

@@ -81,26 +96,28 @@ options: {

### Patterns
### `Patterns`
| Name | Type | Default | Description |
| :-------------------------------------: | :-------------------------: | :---------------------------------------------: | :---------------------------------------------------------------------------------------------------- |
| [`from`](#from) | `{String}` | `undefined` | Glob or path from where we ҁopy files. |
| [`to`](#to) | `{String\|Function}` | `compiler.options.output` | Output path. |
| [`context`](#context) | `{String}` | `options.context \|\| compiler.options.context` | A path that determines how to interpret the `from` path. |
| [`globOptions`](#globoptions) | `{Object}` | `undefined` | [Options][glob-options] passed to the glob pattern matching library including `ignore` option. |
| [`filter`](#filter) | `{Function}` | `undefined` | Allows to filter copied assets. |
| [`toType`](#totype) | `{String}` | `undefined` | Determinate what is `to` option - directory, file or template. |
| [`force`](#force) | `{Boolean}` | `false` | Overwrites files already in `compilation.assets` (usually added by other plugins/loaders). |
| [`flatten`](#flatten) | `{Boolean}` | `false` | Removes all directory references and only copies file names. |
| [`transform`](#transform) | `{Function}` | `undefined` | Allows to modify the file contents. |
| [`cacheTransform`](#cacheTransform) | `{Boolean\|String\|Object}` | `false` | Enable `transform` caching. You can use `{ cache: { key: 'my-cache-key' } }` to invalidate the cache. |
| [`transformPath`](#transformpath) | `{Function}` | `undefined` | Allows to modify the writing path. |
| [`noErrorOnMissing`](#noerroronmissing) | `{Boolean}` | `false` | Doesn't generate an error on missing file(s). |
| [`info`](#info) | `{Object\|Function}` | `undefined` | Allows to add assets info. |
- [`from`](#from)
- [`to`](#to)
- [`context`](#context)
- [`globOptions`](#globoptions)
- [`filter`](#filter)
- [`toType`](#totype)
- [`force`](#force)
- [`priority`](#priority)
- [`transform`](#transform)
- [`transformAll`](#transformAll)
- [`noErrorOnMissing`](#noerroronmissing)
- [`info`](#info)
#### `from`
Type: `String`
Type:
```ts
type from = string;
```
Default: `undefined`
Glob or path from where we ҁopy files.
Glob or path from where we copy files.
Globs accept [fast-glob pattern-syntax](https://github.com/mrmlnc/fast-glob#pattern-syntax).

@@ -183,6 +200,13 @@ Glob can only be a `string`.

Type: `String|Function`
Type:
```ts
type to =
| string
| ((pathData: { context: string; absoluteFilename?: string }) => string);
```
Default: `compiler.options.output`
##### String
##### `string`

@@ -212,3 +236,3 @@ Output path.

from: "**/*",
to: "[path][name].[contenthash].[ext]",
to: "[path][name].[contenthash][ext]",
},

@@ -221,3 +245,3 @@ ],

##### Function
##### `function`

@@ -240,3 +264,3 @@ Allows to modify the writing path.

to({ context, absoluteFilename }) {
return "dest/newPath";
return "dest/newPath/[name][ext]";
},

@@ -259,5 +283,4 @@ },

from: "src/*.png",
to: "dest/",
to({ context, absoluteFilename }) {
return Promise.resolve("dest/newPath");
return Promise.resolve("dest/newPath/[name][ext]");
},

@@ -273,3 +296,8 @@ },

Type: `String`
Type:
```ts
type context = string;
```
Default: `options.context|compiler.options.context`

@@ -303,4 +331,2 @@

Also, `context` indicates how to interpret the search results. Further, he is considered in this role.
To determine the structure from which the found resources will be copied to the destination folder, the `context` option is used.

@@ -318,6 +344,11 @@

Type: `Object`
Type:
```ts
type globOptions = import("globby").Options;
```
Default: `undefined`
Allows to configute the glob pattern matching library used by the plugin. [See the list of supported options][glob-options]
Allows to configure the glob pattern matching library used by the plugin. [See the list of supported options][glob-options]
To exclude files from the selection, you should use [globOptions.ignore option](https://github.com/mrmlnc/fast-glob#ignore)

@@ -348,6 +379,11 @@

Type: `Function`
Type:
```ts
type filter = (filepath: string) => boolean;
```
Default: `undefined`
> â„šī¸ To ignore files by path please use the [`globOptions.ignore`]((#globoptions) option.
> â„šī¸ To ignore files by path please use the [`globOptions.ignore`](#globoptions) option.

@@ -384,3 +420,8 @@ **webpack.config.js**

Type: `String`
Type:
```ts
type toType = "dir" | "file" | "template";
```
Default: `undefined`

@@ -393,7 +434,7 @@

| Name | Type | Default | Description |
| :--------------: | :--------: | :---------: | :------------------------------------------------------------------------------------------------- |
| **`'dir'`** | `{String}` | `undefined` | If `to` has no extension or ends on `'/'` |
| **`'file'`** | `{String}` | `undefined` | If `to` is not a directory and is not a template |
| **`'template'`** | `{String}` | `undefined` | If `to` contains [a template pattern](https://github.com/webpack-contrib/file-loader#placeholders) |
| Name | Type | Default | Description |
| :---------------------------: | :------: | :---------: | :--------------------------------------------------------------------------------------------------- |
| **[`'dir'`](#dir)** | `string` | `undefined` | If `to` has no extension or ends on `'/'` |
| **[`'file'`](#file)** | `string` | `undefined` | If `to` is not a directory and is not a template |
| **[`'template'`](#template)** | `string` | `undefined` | If `to` contains [a template pattern](https://webpack.js.org/configuration/output/#template-strings) |

@@ -451,3 +492,3 @@ ##### `'dir'`

from: "src/",
to: "dest/[name].[hash].[ext]",
to: "dest/[name].[contenthash][ext]",
toType: "template",

@@ -463,3 +504,8 @@ },

Type: `Boolean`
Type:
```ts
type force = boolean;
```
Default: `false`

@@ -487,11 +533,16 @@

#### `flatten`
#### `priority`
Type: `Boolean`
Default: `false`
Type:
Removes all directory references and only copies file names.
```ts
type priority = number;
```
> âš ī¸ If files have the same name, the result is non-deterministic.
Default: `0`
Allows to specify the priority of copying files with the same destination name.
Files for patterns with higher priority will be copied later.
To overwrite files, the [`force`](#force) option must be enabled.
**webpack.config.js**

@@ -504,7 +555,15 @@

patterns: [
// Copied second and will overwrite "dir_2/file.txt"
{
from: "src/**/*",
to: "dest/",
flatten: true,
from: "dir_1/file.txt",
to: "newfile.txt",
force: true,
priority: 10,
},
// Copied first
{
from: "dir_2/file.txt",
to: "newfile.txt",
priority: 5,
},
],

@@ -518,3 +577,13 @@ }),

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

@@ -524,2 +593,4 @@

##### `function`
**webpack.config.js**

@@ -547,2 +618,19 @@

##### `object`
| Name | Default | Description |
| :-------------------------------: | :---------: | :--------------------------------------------------------------------------------------------------------------- |
| **[`transformer`](#transformer)** | `undefined` | Allows to modify the file contents. |
| **[`cache`](#cache)** | `false` | Enable `transform` caching. You can use `transform: { cache: { key: 'my-cache-key' } }` to invalidate the cache. |
###### `transformer`
Type:
```ts
type transformer = (input: string, absoluteFilename: string) => string;
```
Default: `undefined`
**webpack.config.js**

@@ -558,4 +646,8 @@

to: "dest/",
transform(content, path) {
return Promise.resolve(optimize(content));
// The `content` argument is a [`Buffer`](https://nodejs.org/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);
},
},

@@ -569,14 +661,2 @@ },

#### `cacheTransform`
Type: `Boolean|String|Object`
Default: `false`
Enable/disable and configure caching.
Default path to cache directory: `node_modules/.cache/copy-webpack-plugin`.
##### `Boolean`
Enables/Disable `transform` caching.
**webpack.config.js**

@@ -592,6 +672,7 @@

to: "dest/",
transform(content, path) {
return optimize(content);
transform: {
transformer(content, path) {
return Promise.resolve(optimize(content));
},
},
cacheTransform: true,
},

@@ -604,8 +685,40 @@ ],

##### `String`
###### `cache`
Enables `transform` caching and setup cache directory.
Type:
```ts
type cache =
| boolean
| {
keys: {
[key: string]: any;
};
}
| {
keys: (
defaultCacheKeys: {
[key: string]: any;
},
absoluteFilename: string
) => Promise<{
[key: string]: any;
}>;
}
| undefined;
```
Default: `false`
**webpack.config.js**
Enable/disable and configure caching.
Default path to cache directory: `node_modules/.cache/copy-webpack-plugin`.
###### `boolean`
Enables/Disable `transform` caching.
**webpack.config.js**
```js

@@ -619,7 +732,8 @@ module.exports = {

to: "dest/",
transform(content, path) {
return optimize(content);
transform: {
transformer(content, path) {
return optimize(content);
},
cache: true,
},
// Should be absolute
cacheTransform: path.resolve(__dirname, "cache-directory"),
},

@@ -632,3 +746,3 @@ ],

##### `Object`
##### `object`

@@ -647,12 +761,14 @@ Enables `transform` caching and setup cache directory and invalidation keys.

to: "dest/",
transform(content, path) {
return optimize(content);
},
cacheTransform: {
directory: path.resolve(__dirname, "cache-directory"),
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",
transform: {
transformer(content, path) {
return optimize(content);
},
cache: {
directory: path.resolve(__dirname, "cache-directory"),
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",
},
},
},

@@ -680,14 +796,16 @@ },

to: "dest/",
transform(content, path) {
return optimize(content);
},
cacheTransform: {
directory: path.resolve(__dirname, "cache-directory"),
keys: (defaultCacheKeys, absoluteFrom) => {
const keys = getCustomCacheInvalidationKeysSync();
transform: {
transformer(content, path) {
return optimize(content);
},
cache: {
directory: path.resolve(__dirname, "cache-directory"),
keys: (defaultCacheKeys, absoluteFrom) => {
const keys = getCustomCacheInvalidationKeysSync();
return {
...defaultCacheKeys,
keys,
};
return {
...defaultCacheKeys,
keys,
};
},
},

@@ -714,14 +832,16 @@ },

to: "dest/",
transform(content, path) {
return optimize(content);
},
cacheTransform: {
directory: path.resolve(__dirname, "cache-directory"),
keys: async (defaultCacheKeys, absoluteFrom) => {
const keys = await getCustomCacheInvalidationKeysAsync();
transform: {
transformer(content, path) {
return optimize(content);
},
cache: {
directory: path.resolve(__dirname, "cache-directory"),
keys: async (defaultCacheKeys, absoluteFrom) => {
const keys = await getCustomCacheInvalidationKeysAsync();
return {
...defaultCacheKeys,
keys,
};
return {
...defaultCacheKeys,
keys,
};
},
},

@@ -736,12 +856,21 @@ },

#### `transformPath`
#### `transformAll`
Type: `Function`
Type:
```ts
type transformAll = (
data: {
data: Buffer;
sourceFilename: string;
absoluteFilename: string;
}[]
) => any;
```
Default: `undefined`
Allows to modify the writing path.
Allows you to modify the contents of multiple files and save the result to one file.
> âš ī¸ Don't return directly `\\` in `transformPath` (i.e `path\to\newFile`) option because on UNIX the backslash is a valid character inside a path component, i.e., it's not a separator.
> On Windows, the forward slash and the backward slash are both separators.
> Instead please use `/` or `path` methods.
> â„šī¸ The `to` option must be specified and point to a file. It is allowed to use only `[contenthash]` and `[fullhash]` template strings.

@@ -756,26 +885,16 @@ **webpack.config.js**

{
from: "src/*.png",
to: "dest/",
transformPath(targetPath, absolutePath) {
return "newPath";
},
},
],
}),
],
};
```
from: "src/**/*.txt",
to: "dest/file.txt",
// The `assets` argument is an assets array for 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://nodejs.org/api/buffer.html) object, it could be converted to a `String` to be processed using `content.toString()`
const content = asset.data;
**webpack.config.js**
accumulator = `${accumulator}${content}\n`;
return accumulator;
}, "");
```js
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: "src/*.png",
to: "dest/",
transformPath(targetPath, absolutePath) {
return Promise.resolve("newPath");
return result;
},

@@ -791,6 +910,11 @@ },

Type: `Boolean`
Type:
```ts
type noErrorOnMissing = boolean;
```
Default: `false`
Doesn't generate an error on missing file(s);
Doesn't generate an error on missing file(s).

@@ -814,3 +938,15 @@ ```js

Type: `Object|Function<Object>`
Type:
```ts
type info =
| Record<string, any>
| ((item: {
absoluteFilename: string;
sourceFilename: string;
filename: string;
toType: ToType;
}) => Record<string, any>);
```
Default: `undefined`

@@ -860,8 +996,14 @@

| Name | Type | Default | Description |
| :---------------------------: | :--------: | :-----: | :----------------------------------------------- |
| [`concurrency`](#concurrency) | `{Number}` | `100` | Limits the number of simultaneous requests to fs |
- [`concurrency`](#concurrency)
#### `concurrency`
type:
```ts
type concurrency = number;
```
default: `100`
limits the number of simultaneous requests to fs

@@ -1078,2 +1220,112 @@

#### Flatten copy
Removes all directory references and only copies file names.
> âš ī¸ If files have the same name, the result is non-deterministic.
**webpack.config.js**
```js
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: "src/**/*",
to: "[name][ext]",
},
],
}),
],
};
```
Result:
```txt
file-1.txt
file-2.txt
nested-file.txt
```
#### Copy in new directory
**webpack.config.js**
```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",
},
],
}),
],
};
```
Result:
```txt
"newdirectory/file-1.txt",
"newdirectory/nestedfile.txt",
"newdirectory/nested/deep-nested/deepnested.txt",
"newdirectory/nested/nestedfile.txt",
```
#### Skip running JavaScript files through a minimizer
Useful if you need to simply copy `*.js` files to destination "as is" without evaluating and minimizing them using Terser.
**webpack.config.js**
```js
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
"relative/path/to/file.ext",
{
from: "**/*",
// Terser skip this file for minimization
info: { minimized: true },
},
],
}),
],
};
```
##### `yarn workspaces` and `monorepos`
When using `yarn workspaces` or` monorepos`, relative copy paths from node_modules can be broken due to the way packages are hoisting.
To avoid this, should explicitly specify where to copy the files from using `require.resolve`.
**webpack.config.js**
```js
module.exports = {
plugins: [
new CopyPlugin({
patterns: [
{
from: `${path.dirname(
require.resolve(`${moduleName}/package.json`)
)}/target`,
to: "target",
},
],
}),
],
};
```
## Contributing

@@ -1080,0 +1332,0 @@