summaryrefslogtreecommitdiff
path: root/node_modules/@11ty/recursive-copy/lib
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/@11ty/recursive-copy/lib')
-rw-r--r--node_modules/@11ty/recursive-copy/lib/copy.js436
1 files changed, 436 insertions, 0 deletions
diff --git a/node_modules/@11ty/recursive-copy/lib/copy.js b/node_modules/@11ty/recursive-copy/lib/copy.js
new file mode 100644
index 0000000..8e0b12e
--- /dev/null
+++ b/node_modules/@11ty/recursive-copy/lib/copy.js
@@ -0,0 +1,436 @@
+'use strict';
+
+var Promise = global.Promise;
+var path = require('node:path');
+var EventEmitter = require('node:events').EventEmitter;
+var fs = require('fs');
+var junk = require('junk');
+var errno = require('errno');
+var maximatch = require('maximatch');
+var slash = require('slash');
+
+var CopyError = errno.custom.createError('CopyError');
+
+var EVENT_ERROR = 'error';
+var EVENT_COMPLETE = 'complete';
+var EVENT_CREATE_DIRECTORY_START = 'createDirectoryStart';
+var EVENT_CREATE_DIRECTORY_ERROR = 'createDirectoryError';
+var EVENT_CREATE_DIRECTORY_COMPLETE = 'createDirectoryComplete';
+var EVENT_CREATE_SYMLINK_START = 'createSymlinkStart';
+var EVENT_CREATE_SYMLINK_ERROR = 'createSymlinkError';
+var EVENT_CREATE_SYMLINK_COMPLETE = 'createSymlinkComplete';
+var EVENT_COPY_FILE_START = 'copyFileStart';
+var EVENT_COPY_FILE_ERROR = 'copyFileError';
+var EVENT_COPY_FILE_COMPLETE = 'copyFileComplete';
+
+var mkdirp = fs.promises.mkdir;
+var mkdir = mkdirp;
+var stat = fs.promises.stat;
+var lstat = fs.promises.lstat;
+var readlink = fs.promises.readlink
+var symlink = fs.promises.symlink;
+var readdir = fs.promises.readdir;
+
+module.exports = function(src, dest, options, callback) {
+ if ((arguments.length === 3) && (typeof options === 'function')) {
+ callback = options;
+ options = undefined;
+ }
+ options = options || {};
+
+ var parentDirectory = path.dirname(dest);
+ var shouldExpandSymlinks = Boolean(options.expand);
+
+ var emitter;
+ var hasFinished = false;
+ if (options.debug) { log('Ensuring output directory exists…'); }
+ var promise = ensureDirectoryExists(parentDirectory)
+ .then(function() {
+ if (options.debug) { log('Fetching source paths…'); }
+ return getFilePaths(src, shouldExpandSymlinks)
+ })
+ .then(function(filePaths) {
+ if (options.debug) { log('Filtering source paths…'); }
+ // must filter out explicit copy attempts for dot files (not post-relative path)
+ filePaths = getFilteredPaths(filePaths, undefined, {
+ dot: options.dot,
+ junk: options.junk
+ });
+ var relativePaths = filePaths.map(function(filePath) {
+ return path.relative(src, filePath);
+ });
+ var filteredPaths = getFilteredPaths(relativePaths, options.filter, {
+ dot: options.dot,
+ junk: options.junk
+ });
+ return filteredPaths.map(function(relativePath) {
+ var inputPath = relativePath;
+ var outputPath = options.rename ? options.rename(inputPath) : inputPath;
+ return {
+ src: path.join(src, inputPath),
+ dest: path.join(dest, outputPath)
+ };
+ })
+ })
+ .then(function(operations) {
+ if (options.debug) { log('Copying files…'); }
+ var hasFinishedGetter = function() { return hasFinished; };
+ var emitEvent = function() { emitter.emit.apply(emitter, arguments); };
+ return batch(operations, function(operation) {
+ return copy(operation.src, operation.dest, hasFinishedGetter, emitEvent, options);
+ }, {
+ results: options.results !== false,
+ concurrency: options.concurrency || 255
+ });
+ })
+ .catch(function(error) {
+ if (options.debug) { log('Copy failed'); }
+ if (error instanceof CopyError) {
+ emitter.emit(EVENT_ERROR, error.error, error.data);
+ throw error.error;
+ } else {
+ throw error;
+ }
+ })
+ .then(function(results) {
+ if (options.debug) { log('Copy complete'); }
+ emitter.emit(EVENT_COMPLETE, results);
+ return results;
+ })
+ .then(function(results) {
+ hasFinished = true;
+ return results;
+ })
+ .catch(function(error) {
+ hasFinished = true;
+ throw error;
+ });
+
+ if (typeof callback === 'function') {
+ promise.then(function(results) {
+ callback(null, results);
+ })
+ .catch(function(error) {
+ callback(error);
+ });
+ emitter = new EventEmitter();
+ } else {
+ emitter = withEventEmitter(promise);
+ }
+
+ return emitter;
+};
+
+function batch(inputs, iteratee, options) {
+ var results = options.results ? [] : undefined;
+ if (inputs.length === 0) { return Promise.resolve(results); }
+ return new Promise(function(resolve, reject) {
+ var currentIndex = -1;
+ var activeWorkers = 0;
+ while (currentIndex < Math.min(inputs.length, options.concurrency) - 1) {
+ startWorker(inputs[++currentIndex]);
+ }
+
+ function startWorker(input) {
+ ++activeWorkers;
+ iteratee(input).then(function(result) {
+ --activeWorkers;
+ if (results) { results.push(result); }
+ if (currentIndex < inputs.length - 1) {
+ startWorker(inputs[++currentIndex]);
+ } else if (activeWorkers === 0) {
+ resolve(results);
+ }
+ }).catch(reject);
+ }
+ });
+}
+
+function getFilePaths(src, shouldExpandSymlinks) {
+ return (shouldExpandSymlinks ? stat : lstat)(src)
+ .then(function(stats) {
+ if (stats.isDirectory()) {
+ return getFileListing(src, shouldExpandSymlinks)
+ .then(function(filenames) {
+ return [src].concat(filenames);
+ });
+ } else {
+ return [src];
+ }
+ });
+}
+
+function getFilteredPaths(paths, filter, options) {
+ var useDotFilter = !options.dot;
+ var useJunkFilter = !options.junk;
+ if (!filter && !useDotFilter && !useJunkFilter) { return paths; }
+ return paths.filter(function(path) {
+ if(!useDotFilter || dotFilter(path)) {
+ if(!useJunkFilter || junkFilter(path)) {
+ if(!filter) {
+ return true;
+ }
+ var p = slash(path);
+ // filter might be a string, array, function
+ var m = maximatch(p, filter, options);
+ if(m.length > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ });
+}
+
+function dotFilter(relativePath) {
+ var filename = path.basename(relativePath);
+ return filename.charAt(0) !== '.';
+}
+
+function junkFilter(relativePath) {
+ var filename = path.basename(relativePath);
+ return !junk.is(filename);
+}
+
+function ensureDirectoryExists(path) {
+ return mkdir(path, { recursive: true });
+}
+
+function getFileListing(srcPath, shouldExpandSymlinks) {
+ return readdir(srcPath)
+ .then(function(filenames) {
+ return Promise.all(
+ filenames.map(function(filename) {
+ var filePath = path.join(srcPath, filename);
+ return (shouldExpandSymlinks ? stat : lstat)(filePath)
+ .then(function(stats) {
+ if (stats.isDirectory()) {
+ return getFileListing(filePath, shouldExpandSymlinks)
+ .then(function(childPaths) {
+ return [filePath].concat(childPaths);
+ });
+ } else {
+ return [filePath];
+ }
+ });
+ })
+ )
+ .then(function mergeArrays(arrays) {
+ return Array.prototype.concat.apply([], arrays);
+ });
+ });
+}
+
+function copy(srcPath, destPath, hasFinished, emitEvent, options) {
+ if (options.debug) { log('Preparing to copy ' + srcPath + '…'); }
+ return prepareForCopy(srcPath, destPath, options)
+ .then(function(stats) {
+ if (options.debug) { log('Copying ' + srcPath + '…'); }
+ var copyFunction = getCopyFunction(stats, hasFinished, emitEvent);
+ return copyFunction(srcPath, destPath, stats, options);
+ })
+ .catch(function(error) {
+ if (error instanceof CopyError) {
+ throw error;
+ }
+ var copyError = new CopyError(error.message);
+ copyError.error = error;
+ copyError.data = {
+ src: srcPath,
+ dest: destPath
+ };
+ throw copyError;
+ })
+ .then(function(result) {
+ if (options.debug) { log('Copied ' + srcPath); }
+ return result;
+ });
+}
+
+function prepareForCopy(srcPath, destPath, options) {
+ var shouldExpandSymlinks = Boolean(options.expand);
+ var shouldOverwriteExistingFiles = Boolean(options.overwrite);
+ return (shouldExpandSymlinks ? stat : lstat)(srcPath)
+ .then(function(stats) {
+ return ensureDestinationIsWritable(destPath, stats, shouldOverwriteExistingFiles)
+ .then(function() {
+ return stats;
+ });
+ });
+}
+
+function ensureDestinationIsWritable(destPath, srcStats, shouldOverwriteExistingFiles) {
+ return lstat(destPath)
+ .catch(function(error) {
+ var shouldIgnoreError = error.code === 'ENOENT';
+ if (shouldIgnoreError) { return null; }
+ throw error;
+ })
+ .then(function(destStats) {
+ var destExists = Boolean(destStats);
+ if (!destExists) { return true; }
+
+ var isMergePossible = srcStats.isDirectory() && destStats.isDirectory();
+ if (isMergePossible) { return true; }
+
+ if (shouldOverwriteExistingFiles) {
+ return fs.promises.rm(destPath, { recursive: true, force: true }).then(function(paths) {
+ return true;
+ });
+ } else {
+ throw fsError('EEXIST', destPath);
+ }
+ });
+}
+
+function getCopyFunction(stats, hasFinished, emitEvent) {
+ if (stats.isDirectory()) {
+ return createCopyFunction(copyDirectory, stats, hasFinished, emitEvent, {
+ startEvent: EVENT_CREATE_DIRECTORY_START,
+ completeEvent: EVENT_CREATE_DIRECTORY_COMPLETE,
+ errorEvent: EVENT_CREATE_DIRECTORY_ERROR
+ });
+ } else if (stats.isSymbolicLink()) {
+ return createCopyFunction(copySymlink, stats, hasFinished, emitEvent, {
+ startEvent: EVENT_CREATE_SYMLINK_START,
+ completeEvent: EVENT_CREATE_SYMLINK_COMPLETE,
+ errorEvent: EVENT_CREATE_SYMLINK_ERROR
+ });
+ } else {
+ return createCopyFunction(copyFile, stats, hasFinished, emitEvent, {
+ startEvent: EVENT_COPY_FILE_START,
+ completeEvent: EVENT_COPY_FILE_COMPLETE,
+ errorEvent: EVENT_COPY_FILE_ERROR
+ });
+ }
+}
+
+function createCopyFunction(fn, stats, hasFinished, emitEvent, events) {
+ var startEvent = events.startEvent;
+ var completeEvent = events.completeEvent;
+ var errorEvent = events.errorEvent;
+ return function(srcPath, destPath, stats, options) {
+ // Multiple chains of promises are fired in parallel,
+ // so when one fails we need to prevent any future
+ // copy operations
+ if (hasFinished()) { return Promise.reject(); }
+ var metadata = {
+ src: srcPath,
+ dest: destPath,
+ stats: stats
+ };
+ emitEvent(startEvent, metadata);
+ var parentDirectory = path.dirname(destPath);
+ return ensureDirectoryExists(parentDirectory)
+ .then(function() {
+ return fn(srcPath, destPath, stats, options);
+ })
+ .then(function() {
+ if (!hasFinished()) { emitEvent(completeEvent, metadata); }
+ return metadata;
+ })
+ .catch(function(error) {
+ if (!hasFinished()) { emitEvent(errorEvent, error, metadata); }
+ throw error;
+ });
+ };
+}
+
+function copyFile(srcPath, destPath, stats, options) {
+ return new Promise(function(resolve, reject) {
+ var hasFinished = false;
+
+ var read = fs.createReadStream(srcPath);
+ read.on('error', handleCopyFailed);
+
+ var write = fs.createWriteStream(destPath, {
+ flags: 'w',
+ mode: stats.mode
+ });
+ write.on('error', handleCopyFailed);
+ write.on('finish', function() {
+ fs.utimes(destPath, stats.atime, stats.mtime, function() {
+ hasFinished = true;
+ resolve();
+ });
+ });
+
+ var transformStream = null;
+ if (options.transform) {
+ transformStream = options.transform(srcPath, destPath, stats);
+ if (transformStream) {
+ transformStream.on('error', handleCopyFailed);
+ read.pipe(transformStream).pipe(write);
+ } else {
+ read.pipe(write);
+ }
+ } else {
+ read.pipe(write);
+ }
+
+
+ function handleCopyFailed(error) {
+ if (hasFinished) { return; }
+ hasFinished = true;
+ if (typeof read.close === 'function') {
+ read.close();
+ }
+ if (typeof write.close === 'function') {
+ write.close();
+ }
+ return reject(error);
+ }
+ });
+}
+
+function copySymlink(srcPath, destPath, stats, options) {
+ return readlink(srcPath)
+ .then(function(link) {
+ return symlink(link, destPath);
+ });
+}
+
+function copyDirectory(srcPath, destPath, stats, options) {
+ return mkdir(destPath, { recusirve: true })
+ .catch(function(error) {
+ var shouldIgnoreError = error.code === 'EEXIST';
+ if (shouldIgnoreError) { return; }
+ throw error;
+ });
+}
+
+function fsError(code, path) {
+ var errorType = errno.code[code];
+ var message = errorType.code + ', ' + errorType.description + ' ' + path;
+ var error = new Error(message);
+ error.errno = errorType.errno;
+ error.code = errorType.code;
+ error.path = path;
+ return error;
+}
+
+function log(message) {
+ process.stdout.write(message + '\n');
+}
+
+function withEventEmitter(target) {
+ for (var key in EventEmitter.prototype) {
+ target[key] = EventEmitter.prototype[key];
+ }
+ EventEmitter.call(target);
+ return target;
+}
+
+module.exports.events = {
+ ERROR: EVENT_ERROR,
+ COMPLETE: EVENT_COMPLETE,
+ CREATE_DIRECTORY_START: EVENT_CREATE_DIRECTORY_START,
+ CREATE_DIRECTORY_ERROR: EVENT_CREATE_DIRECTORY_ERROR,
+ CREATE_DIRECTORY_COMPLETE: EVENT_CREATE_DIRECTORY_COMPLETE,
+ CREATE_SYMLINK_START: EVENT_CREATE_SYMLINK_START,
+ CREATE_SYMLINK_ERROR: EVENT_CREATE_SYMLINK_ERROR,
+ CREATE_SYMLINK_COMPLETE: EVENT_CREATE_SYMLINK_COMPLETE,
+ COPY_FILE_START: EVENT_COPY_FILE_START,
+ COPY_FILE_ERROR: EVENT_COPY_FILE_ERROR,
+ COPY_FILE_COMPLETE: EVENT_COPY_FILE_COMPLETE
+};