rollup-plugin-copy
Version:
Copy files and folders using Rollup
157 lines (135 loc) • 4.26 kB
JavaScript
import path from 'path';
import util from 'util';
import fs from 'fs-extra';
import isObject from 'is-plain-object';
import globby from 'globby';
import { green, bold, yellow } from 'colorette';
function stringify(value) {
return util.inspect(value, {
breakLength: Infinity
});
}
async function isFile(filePath) {
const fileStats = await fs.stat(filePath);
return fileStats.isFile();
}
function renameTarget(target, rename, src) {
const parsedPath = path.parse(target);
return typeof rename === 'string' ? rename : rename(parsedPath.name, parsedPath.ext.replace('.', ''), src);
}
async function generateCopyTarget(src, dest, {
flatten,
rename,
transform
}) {
if (transform && !(await isFile(src))) {
throw new Error(`"transform" option works only on files: '${src}' must be a file`);
}
const {
base,
dir
} = path.parse(src);
const destinationFolder = flatten || !flatten && !dir ? dest : dir.replace(dir.split('/')[0], dest);
return {
src,
dest: path.join(destinationFolder, rename ? renameTarget(base, rename, src) : base),
...(transform && {
contents: await transform(await fs.readFile(src), base)
}),
renamed: rename,
transformed: transform
};
}
function copy(options = {}) {
const {
copyOnce = false,
copySync = false,
flatten = true,
hook = 'buildEnd',
targets = [],
verbose = false,
...restPluginOptions
} = options;
let copied = false;
return {
name: 'copy',
[hook]: async () => {
if (copyOnce && copied) {
return;
}
const copyTargets = [];
if (Array.isArray(targets) && targets.length) {
for (const target of targets) {
if (!isObject(target)) {
throw new Error(`${stringify(target)} target must be an object`);
}
const {
dest,
rename,
src,
transform,
...restTargetOptions
} = target;
if (!src || !dest) {
throw new Error(`${stringify(target)} target must have "src" and "dest" properties`);
}
if (rename && typeof rename !== 'string' && typeof rename !== 'function') {
throw new Error(`${stringify(target)} target's "rename" property must be a string or a function`);
}
const matchedPaths = await globby(src, {
expandDirectories: false,
onlyFiles: false,
...restPluginOptions,
...restTargetOptions
});
if (matchedPaths.length) {
for (const matchedPath of matchedPaths) {
const generatedCopyTargets = Array.isArray(dest) ? await Promise.all(dest.map(destination => generateCopyTarget(matchedPath, destination, {
flatten,
rename,
transform
}))) : [await generateCopyTarget(matchedPath, dest, {
flatten,
rename,
transform
})];
copyTargets.push(...generatedCopyTargets);
}
}
}
}
if (copyTargets.length) {
if (verbose) {
console.log(green('copied:'));
}
for (const copyTarget of copyTargets) {
const {
contents,
dest,
src,
transformed
} = copyTarget;
if (transformed) {
await fs.outputFile(dest, contents, restPluginOptions);
} else if (!copySync) {
await fs.copy(src, dest, restPluginOptions);
} else {
fs.copySync(src, dest, restPluginOptions);
}
if (verbose) {
let message = green(` ${bold(src)} → ${bold(dest)}`);
const flags = Object.entries(copyTarget).filter(([key, value]) => ['renamed', 'transformed'].includes(key) && value).map(([key]) => key.charAt(0).toUpperCase());
if (flags.length) {
message = `${message} ${yellow(`[${flags.join(', ')}]`)}`;
}
console.log(message);
}
}
} else if (verbose) {
console.log(yellow('no items to copy'));
}
copied = true;
}
};
}
export { copy as default };