@cjsa/cpy
Version:
Copy files
223 lines (215 loc) • 8.8 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// index.js
var _process = require('process'); var _process2 = _interopRequireDefault(_process);
var _events = require('events'); var _events2 = _interopRequireDefault(_events);
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
var _os = require('os'); var _os2 = _interopRequireDefault(_os);
var _pmap = require('p-map'); var _pmap2 = _interopRequireDefault(_pmap);
var _arrify = require('arrify'); var _arrify2 = _interopRequireDefault(_arrify);
var _cpfile = require('cp-file'); var _cpfile2 = _interopRequireDefault(_cpfile);
var _pfilter = require('p-filter'); var _pfilter2 = _interopRequireDefault(_pfilter);
var _globby = require('globby');
var _micromatch = require('micromatch'); var _micromatch2 = _interopRequireDefault(_micromatch);
// cpy-error.js
var _nestederrorstacks = require('nested-error-stacks'); var _nestederrorstacks2 = _interopRequireDefault(_nestederrorstacks);
var CpyError = class extends _nestederrorstacks2.default {
constructor(message, nested) {
super(message, nested);
Object.assign(this, nested);
this.name = "CpyError";
}
};
// glob-pattern.js
var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
var _junk = require('junk');
var GlobPattern = class {
constructor(pattern, destination, options) {
this.path = pattern;
this.originalPath = pattern;
this.destination = destination;
this.options = options;
this.isDirectory = false;
if (!_globby.isDynamicPattern.call(void 0, pattern) && _fs2.default.existsSync(pattern) && _fs2.default.lstatSync(pattern).isDirectory()) {
this.path = [pattern, "**"].join("/");
this.isDirectory = true;
}
}
get name() {
return _path2.default.basename(this.originalPath);
}
get normalizedPath() {
const segments = this.originalPath.split("/");
const magicIndex = segments.findIndex((item) => item ? _globby.isDynamicPattern.call(void 0, item) : false);
const normalized = segments.slice(0, magicIndex).join("/");
if (normalized) {
return _path2.default.isAbsolute(normalized) ? normalized : _path2.default.join(this.options.cwd, normalized);
}
return this.destination;
}
hasMagic() {
return _globby.isDynamicPattern.call(void 0, this.options.flat ? this.path : this.originalPath);
}
getMatches() {
let matches = _globby.globbySync.call(void 0, this.path, {
...this.options,
dot: true,
absolute: true,
onlyFiles: true
});
if (this.options.ignoreJunk) {
matches = matches.filter((file) => _junk.isNotJunk.call(void 0, _path2.default.basename(file)));
}
return matches;
}
};
// index.js
var defaultConcurrency = (_os2.default.cpus().length || 1) * 2;
var defaultOptions = {
ignoreJunk: true,
flat: false,
cwd: _process2.default.cwd()
};
var Entry = class {
constructor(source, relativePath, pattern) {
this.path = source.split("/").join(_path2.default.sep);
this.relativePath = relativePath.split("/").join(_path2.default.sep);
this.pattern = pattern;
Object.freeze(this);
}
get name() {
return _path2.default.basename(this.path);
}
get nameWithoutExtension() {
return _path2.default.basename(this.path, _path2.default.extname(this.path));
}
get extension() {
return _path2.default.extname(this.path).slice(1);
}
};
var expandPatternsWithBraceExpansion = (patterns) => patterns.flatMap((pattern) => _micromatch2.default.braces(pattern, {
expand: true,
nodupes: true
}));
var preprocessDestinationPath = ({ entry, destination, options }) => {
if (entry.pattern.hasMagic()) {
if (options.flat) {
if (_path2.default.isAbsolute(destination)) {
return _path2.default.join(destination, entry.name);
}
return _path2.default.join(options.cwd, destination, entry.name);
}
return _path2.default.join(destination, _path2.default.relative(entry.pattern.normalizedPath, entry.path));
}
if (_path2.default.isAbsolute(destination)) {
return _path2.default.join(destination, entry.name);
}
if (entry.pattern.isDirectory && _path2.default.relative(options.cwd, entry.path).startsWith("..")) {
return _path2.default.join(options.cwd, destination, _path2.default.basename(entry.pattern.originalPath), _path2.default.relative(entry.pattern.originalPath, entry.path));
}
if (!entry.pattern.isDirectory && entry.path === entry.relativePath) {
return _path2.default.join(options.cwd, destination, _path2.default.basename(entry.pattern.originalPath), _path2.default.relative(entry.pattern.originalPath, entry.path));
}
if (!entry.pattern.isDirectory && options.flat) {
return _path2.default.join(options.cwd, destination, _path2.default.basename(entry.pattern.originalPath));
}
return _path2.default.join(options.cwd, destination, _path2.default.relative(options.cwd, entry.path));
};
var renameFile = (source, rename) => {
const filename = _path2.default.basename(source, _path2.default.extname(source));
const fileExtension = _path2.default.extname(source);
const directory = _path2.default.dirname(source);
if (typeof rename === "string") {
return _path2.default.join(directory, rename);
}
if (typeof rename === "function") {
return _path2.default.join(directory, `${rename(filename)}${fileExtension}`);
}
return source;
};
function cpy(source, destination, { concurrency = defaultConcurrency, ...options } = {}) {
const copyStatus = /* @__PURE__ */ new Map();
const progressEmitter = new (0, _events2.default)();
options = {
...defaultOptions,
...options
};
const promise = (async () => {
let entries = [];
let completedFiles = 0;
let completedSize = 0;
let patterns = expandPatternsWithBraceExpansion(_arrify2.default.call(void 0, source)).map((string) => string.replace(/\\/g, "/"));
const sources = patterns.filter((item) => !item.startsWith("!"));
const ignore = patterns.filter((item) => item.startsWith("!"));
if (sources.length === 0 || !destination) {
throw new CpyError("`source` and `destination` required");
}
patterns = patterns.map((pattern) => new GlobPattern(pattern, destination, { ...options, ignore }));
for (const pattern of patterns) {
let matches = [];
try {
matches = pattern.getMatches();
} catch (error) {
throw new CpyError(`Cannot glob \`${pattern.originalPath}\`: ${error.message}`, error);
}
if (matches.length === 0 && !_globby.isDynamicPattern.call(void 0, pattern.originalPath) && !_globby.isDynamicPattern.call(void 0, ignore)) {
throw new CpyError(`Cannot copy \`${pattern.originalPath}\`: the file doesn't exist`);
}
entries = [
...entries,
...matches.map((sourcePath) => new Entry(sourcePath, _path2.default.relative(options.cwd, sourcePath), pattern))
];
}
if (options.filter !== void 0) {
entries = await _pfilter2.default.call(void 0, entries, options.filter, { concurrency: 1024 });
}
if (entries.length === 0) {
progressEmitter.emit("progress", {
totalFiles: 0,
percent: 1,
completedFiles: 0,
completedSize: 0
});
}
const fileProgressHandler = (event) => {
const fileStatus = copyStatus.get(event.sourcePath) || {
writtenBytes: 0,
percent: 0
};
if (fileStatus.writtenBytes !== event.writtenBytes || fileStatus.percent !== event.percent) {
completedSize -= fileStatus.writtenBytes;
completedSize += event.writtenBytes;
if (event.percent === 1 && fileStatus.percent !== 1) {
completedFiles++;
}
copyStatus.set(event.sourcePath, {
writtenBytes: event.writtenBytes,
percent: event.percent
});
progressEmitter.emit("progress", {
totalFiles: entries.length,
percent: completedFiles / entries.length,
completedFiles,
completedSize
});
}
};
return _pmap2.default.call(void 0, entries, async (entry) => {
const to = renameFile(preprocessDestinationPath({
entry,
destination,
options
}), options.rename);
try {
await _cpfile2.default.call(void 0, entry.path, to, options).on("progress", fileProgressHandler);
} catch (error) {
throw new CpyError(`Cannot copy from \`${entry.relativePath}\` to \`${to}\`: ${error.message}`, error);
}
return to;
}, { concurrency });
})();
promise.on = (...arguments_) => {
progressEmitter.on(...arguments_);
return promise;
};
return promise;
}
exports.default = cpy;