Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 44 additions & 47 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,41 +289,41 @@ class CopyPlugin {
logger.debug(`found "${from}"`);

// `globby`/`fast-glob` return the relative path when the path contains special characters on windows
const absoluteFrom = path.resolve(pattern.context, from);
const absoluteFilename = path.resolve(pattern.context, from);
const relativeFrom = pattern.flatten
? path.basename(absoluteFrom)
: path.relative(pattern.context, absoluteFrom);
let webpackTo =
? path.basename(absoluteFilename)
: path.relative(pattern.context, absoluteFilename);
let filename =
pattern.toType === 'dir'
? path.join(pattern.to, relativeFrom)
: pattern.to;

if (path.isAbsolute(webpackTo)) {
webpackTo = path.relative(compiler.options.output.path, webpackTo);
if (path.isAbsolute(filename)) {
filename = path.relative(compiler.options.output.path, filename);
}

logger.log(`determined that "${from}" should write to "${webpackTo}"`);
logger.log(`determined that "${from}" should write to "${filename}"`);

const sourceFilename = normalizePath(
path.relative(pattern.compilerContext, absoluteFrom)
path.relative(pattern.compilerContext, absoluteFilename)
);

return { absoluteFrom, sourceFilename, relativeFrom, webpackTo };
return { absoluteFilename, sourceFilename, filename };
});

return Promise.all(
files.map(async (file) => {
// If this came from a glob, add it to the file watchlist
if (pattern.fromType === 'glob') {
logger.debug(`add ${file.absoluteFrom} as fileDependencies`);
logger.debug(`add ${file.absoluteFilename} as fileDependencies`);

compilation.fileDependencies.add(file.absoluteFrom);
compilation.fileDependencies.add(file.absoluteFilename);
}

let source;

if (cache) {
const cacheEntry = await cache.getPromise(file.relativeFrom, null);
const cacheEntry = await cache.getPromise(file.sourceFilename, null);

if (cacheEntry) {
const isValidSnapshot = await CopyPlugin.checkSnapshotValid(
Expand All @@ -344,20 +344,20 @@ class CopyPlugin {
startTime = Date.now();
}

logger.debug(`reading "${file.absoluteFrom}" to write to assets`);
logger.debug(`reading "${file.absoluteFilename}" to write to assets`);

let data;

try {
data = await readFile(inputFileSystem, file.absoluteFrom);
data = await readFile(inputFileSystem, file.absoluteFilename);
} catch (error) {
compilation.errors.push(error);

return;
}

if (pattern.transform) {
logger.log(`transforming content for "${file.absoluteFrom}"`);
logger.log(`transforming content for "${file.absoluteFilename}"`);

if (pattern.cacheTransform) {
const cacheDirectory = pattern.cacheTransform.directory
Expand All @@ -377,7 +377,7 @@ class CopyPlugin {
if (typeof pattern.cacheTransform.keys === 'function') {
defaultCacheKeys = await pattern.cacheTransform.keys(
defaultCacheKeys,
file.absoluteFrom
file.absoluteFilename
);
} else {
defaultCacheKeys = {
Expand All @@ -392,21 +392,21 @@ class CopyPlugin {
const result = await cacache.get(cacheDirectory, cacheKeys);

logger.debug(
`getting cached transformation for "${file.absoluteFrom}"`
`getting cached transformation for "${file.absoluteFilename}"`
);

({ data } = result);
} catch (_ignoreError) {
data = await pattern.transform(data, file.absoluteFrom);
data = await pattern.transform(data, file.absoluteFilename);

logger.debug(
`caching transformation for "${file.absoluteFrom}"`
`caching transformation for "${file.absoluteFilename}"`
);

await cacache.put(cacheDirectory, cacheKeys, data);
}
} else {
data = await pattern.transform(data, file.absoluteFrom);
data = await pattern.transform(data, file.absoluteFilename);
}
}

Expand All @@ -416,10 +416,10 @@ class CopyPlugin {
const snapshot = await CopyPlugin.createSnapshot(
compilation,
startTime,
file.relativeFrom
file.sourceFilename
);

await cache.storePromise(file.relativeFrom, null, {
await cache.storePromise(file.sourceFilename, null, {
source,
snapshot,
});
Expand All @@ -428,25 +428,25 @@ class CopyPlugin {

if (pattern.toType === 'template') {
logger.log(
`interpolating template "${file.webpackTo}" for "${file.relativeFrom}"`
`interpolating template "${file.filename}" for "${file.sourceFilename}"`
);

// If it doesn't have an extension, remove it from the pattern
// ie. [name].[ext] or [name][ext] both become [name]
if (!path.extname(file.relativeFrom)) {
if (!path.extname(file.absoluteFilename)) {
// eslint-disable-next-line no-param-reassign
file.webpackTo = file.webpackTo.replace(/\.?\[ext]/g, '');
file.filename = file.filename.replace(/\.?\[ext]/g, '');
}

// eslint-disable-next-line no-param-reassign
file.immutable = /\[(?:([^:\]]+):)?(?:hash|contenthash)(?::([a-z]+\d*))?(?::(\d+))?\]/gi.test(
file.webpackTo
file.filename
);

// eslint-disable-next-line no-param-reassign
file.webpackTo = loaderUtils.interpolateName(
{ resourcePath: file.absoluteFrom },
file.webpackTo,
file.filename = loaderUtils.interpolateName(
{ resourcePath: file.absoluteFilename },
file.filename,
{
content: source.source(),
context: pattern.context,
Expand All @@ -455,27 +455,27 @@ class CopyPlugin {

// Bug in `loader-utils`, package convert `\\` to `/`, need fix in loader-utils
// eslint-disable-next-line no-param-reassign
file.webpackTo = path.normalize(file.webpackTo);
file.filename = path.normalize(file.filename);
}

if (pattern.transformPath) {
logger.log(
`transforming path "${file.webpackTo}" for "${file.absoluteFrom}"`
`transforming path "${file.filename}" for "${file.absoluteFilename}"`
);

// eslint-disable-next-line no-param-reassign
file.immutable = false;
// eslint-disable-next-line no-param-reassign
file.webpackTo = await pattern.transformPath(
file.webpackTo,
file.absoluteFrom
file.filename = await pattern.transformPath(
file.filename,
file.absoluteFilename
);
}

// eslint-disable-next-line no-param-reassign
file.source = source;
// eslint-disable-next-line no-param-reassign
file.targetPath = normalizePath(file.webpackTo);
file.filename = normalizePath(file.filename);
// eslint-disable-next-line no-param-reassign
file.force = pattern.force;

Expand Down Expand Up @@ -532,10 +532,9 @@ class CopyPlugin {
.filter(Boolean)
.forEach((asset) => {
const {
absoluteFilename,
sourceFilename,
absoluteFrom,
targetPath,
webpackTo,
filename,
source,
force,
} = asset;
Expand All @@ -544,17 +543,17 @@ class CopyPlugin {
/* istanbul ignore if */
if (typeof compilation.emitAsset !== 'function') {
// eslint-disable-next-line no-param-reassign
compilation.assets[targetPath] = source;
compilation.assets[filename] = source;

return;
}

const existingAsset = compilation.getAsset(targetPath);
const existingAsset = compilation.getAsset(filename);

if (existingAsset) {
if (force) {
logger.log(
`force updating "${webpackTo}" to compilation assets from "${absoluteFrom}"`
`force updating "${filename}" to compilation assets from "${absoluteFilename}"`
);

const info = { copied: true, sourceFilename };
Expand All @@ -563,20 +562,18 @@ class CopyPlugin {
info.immutable = true;
}

compilation.updateAsset(targetPath, source, info);
compilation.updateAsset(filename, source, info);

return;
}

logger.log(
`skipping "${webpackTo}", because it already exists`
);
logger.log(`skipping "${filename}", because it already exists`);

return;
}

logger.log(
`writing "${webpackTo}" to compilation assets from "${absoluteFrom}"`
`writing "${filename}" to compilation assets from "${absoluteFilename}"`
);

const info = { copied: true, sourceFilename };
Expand All @@ -585,7 +582,7 @@ class CopyPlugin {
info.immutable = true;
}

compilation.emitAsset(targetPath, source, info);
compilation.emitAsset(filename, source, info);
});

logger.debug('end to adding additional assets');
Expand Down
119 changes: 118 additions & 1 deletion test/CopyPlugin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ describe('CopyPlugin', () => {
});

it('should work with the "filesystem" cache', async () => {
const cacheDirectory = path.resolve(__dirname, './outputs/.cache');
const cacheDirectory = path.resolve(__dirname, './outputs/.cache/simple');

await del(cacheDirectory);

Expand Down Expand Up @@ -810,6 +810,123 @@ describe('CopyPlugin', () => {
resolve();
});
});

it('should work with the "filesystem" cache and multi compiler mode', async () => {
const cacheDirectoryA = path.resolve(
__dirname,
'./outputs/.cache/multi-compiler/a'
);
const cacheDirectoryB = path.resolve(
__dirname,
'./outputs/.cache/multi-compiler/b'
);

await del([cacheDirectoryA, cacheDirectoryB]);

const compiler = webpack([
{
mode: 'development',
context: path.resolve(__dirname, './fixtures'),
entry: path.resolve(__dirname, './helpers/enter.js'),
output: {
path: path.resolve(__dirname, './outputs/multi-compiler/dist/a'),
},
stats: {
source: true,
},
cache: {
type: 'filesystem',
cacheDirectory: cacheDirectoryA,
},
plugins: [
new CopyPlugin({
patterns: [
{
from: path.resolve(__dirname, './fixtures/directory'),
},
],
}),
],
},
{
mode: 'development',
entry: path.resolve(__dirname, './helpers/enter.js'),
output: {
path: path.resolve(__dirname, './outputs/multi-compiler/dist/b'),
},
stats: {
source: true,
},
cache: {
type: 'filesystem',
cacheDirectory: cacheDirectoryB,
},
plugins: [
new CopyPlugin({
patterns: [
{
context: path.resolve(__dirname, './fixtures'),
from: path.resolve(__dirname, './fixtures/directory'),
},
],
}),
],
},
]);

compiler.compilers.forEach((item) => {
const outputFileSystem = createFsFromVolume(new Volume());
// Todo remove when we drop webpack@4 support
outputFileSystem.join = path.join.bind(path);

// eslint-disable-next-line no-param-reassign
item.outputFileSystem = outputFileSystem;
});

const { stats } = await compile(compiler);

stats.stats.forEach((item, index) => {
if (webpack.version[0] === '4') {
expect(
Object.keys(item.compilation.assets).filter(
(assetName) => item.compilation.assets[assetName].emitted
).length
).toBe(5);
} else {
expect(item.compilation.emittedAssets.size).toBe(5);
}

expect(item.compilation.errors).toMatchSnapshot('errors');
expect(item.compilation.warnings).toMatchSnapshot('warnings');
expect(readAssets(compiler.compilers[index], item)).toMatchSnapshot(
'assets'
);
});

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

newStats.stats.forEach((item, index) => {
if (webpack.version[0] === '4') {
expect(
Object.keys(item.compilation.assets).filter(
(assetName) => item.compilation.assets[assetName].emitted
).length
).toBe(4);
} else {
expect(item.compilation.emittedAssets.size).toBe(0);
}

expect(item.compilation.errors).toMatchSnapshot('errors');
expect(item.compilation.warnings).toMatchSnapshot('warnings');
expect(readAssets(compiler.compilers[index], item)).toMatchSnapshot(
'assets'
);
});

resolve();
});
});
});

describe('stats', () => {
Expand Down
Loading