/** * archiver-utils * * Copyright (c) 2012-2014 Chris Talkington, contributors. * Licensed under the MIT license. * https://github.com/archiverjs/node-archiver/blob/master/LICENSE-MIT */ var fs = require('graceful-fs'); var path = require('path'); var flatten = require('lodash.flatten'); var difference = require('lodash.difference'); var union = require('lodash.union'); var isPlainObject = require('lodash.isplainobject'); var glob = require('glob'); var file = module.exports = {}; var pathSeparatorRe = /[\/\\]/g; // Process specified wildcard glob patterns or filenames against a // callback, excluding and uniquing files in the result set. var processPatterns = function(patterns, fn) { // Filepaths to return. var result = []; // Iterate over flattened patterns array. flatten(patterns).forEach(function(pattern) { // If the first character is ! it should be omitted var exclusion = pattern.indexOf('!') === 0; // If the pattern is an exclusion, remove the ! if (exclusion) { pattern = pattern.slice(1); } // Find all matching files for this pattern. var matches = fn(pattern); if (exclusion) { // If an exclusion, remove matching files. result = difference(result, matches); } else { // Otherwise add matching files. result = union(result, matches); } }); return result; }; // True if the file path exists. file.exists = function() { var filepath = path.join.apply(path, arguments); return fs.existsSync(filepath); }; // Return an array of all file paths that match the given wildcard patterns. file.expand = function(...args) { // If the first argument is an options object, save those options to pass // into the File.prototype.glob.sync method. var options = isPlainObject(args[0]) ? args.shift() : {}; // Use the first argument if it's an Array, otherwise convert the arguments // object to an array and use that. var patterns = Array.isArray(args[0]) ? args[0] : args; // Return empty set if there are no patterns or filepaths. if (patterns.length === 0) { return []; } // Return all matching filepaths. var matches = processPatterns(patterns, function(pattern) { // Find all matching files for this pattern. return glob.sync(pattern, options); }); // Filter result set? if (options.filter) { matches = matches.filter(function(filepath) { filepath = path.join(options.cwd || '', filepath); try { if (typeof options.filter === 'function') { return options.filter(filepath); } else { // If the file is of the right type and exists, this should work. return fs.statSync(filepath)[options.filter](); } } catch(e) { // Otherwise, it's probably not the right type. return false; } }); } return matches; }; // Build a multi task "files" object dynamically. file.expandMapping = function(patterns, destBase, options) { options = Object.assign({ rename: function(destBase, destPath) { return path.join(destBase || '', destPath); } }, options); var files = []; var fileByDest = {}; // Find all files matching pattern, using passed-in options. file.expand(options, patterns).forEach(function(src) { var destPath = src; // Flatten? if (options.flatten) { destPath = path.basename(destPath); } // Change the extension? if (options.ext) { destPath = destPath.replace(/(\.[^\/]*)?$/, options.ext); } // Generate destination filename. var dest = options.rename(destBase, destPath, options); // Prepend cwd to src path if necessary. if (options.cwd) { src = path.join(options.cwd, src); } // Normalize filepaths to be unix-style. dest = dest.replace(pathSeparatorRe, '/'); src = src.replace(pathSeparatorRe, '/'); // Map correct src path to dest path. if (fileByDest[dest]) { // If dest already exists, push this src onto that dest's src array. fileByDest[dest].src.push(src); } else { // Otherwise create a new src-dest file mapping object. files.push({ src: [src], dest: dest, }); // And store a reference for later use. fileByDest[dest] = files[files.length - 1]; } }); return files; }; // reusing bits of grunt's multi-task source normalization file.normalizeFilesArray = function(data) { var files = []; data.forEach(function(obj) { var prop; if ('src' in obj || 'dest' in obj) { files.push(obj); } }); if (files.length === 0) { return []; } files = _(files).chain().forEach(function(obj) { if (!('src' in obj) || !obj.src) { return; } // Normalize .src properties to flattened array. if (Array.isArray(obj.src)) { obj.src = flatten(obj.src); } else { obj.src = [obj.src]; } }).map(function(obj) { // Build options object, removing unwanted properties. var expandOptions = Object.assign({}, obj); delete expandOptions.src; delete expandOptions.dest; // Expand file mappings. if (obj.expand) { return file.expandMapping(obj.src, obj.dest, expandOptions).map(function(mapObj) { // Copy obj properties to result. var result = Object.assign({}, obj); // Make a clone of the orig obj available. result.orig = Object.assign({}, obj); // Set .src and .dest, processing both as templates. result.src = mapObj.src; result.dest = mapObj.dest; // Remove unwanted properties. ['expand', 'cwd', 'flatten', 'rename', 'ext'].forEach(function(prop) { delete result[prop]; }); return result; }); } // Copy obj properties to result, adding an .orig property. var result = Object.assign({}, obj); // Make a clone of the orig obj available. result.orig = Object.assign({}, obj); if ('src' in result) { // Expose an expand-on-demand getter method as .src. Object.defineProperty(result, 'src', { enumerable: true, get: function fn() { var src; if (!('result' in fn)) { src = obj.src; // If src is an array, flatten it. Otherwise, make it into an array. src = Array.isArray(src) ? flatten(src) : [src]; // Expand src files, memoizing result. fn.result = file.expand(expandOptions, src); } return fn.result; } }); } if ('dest' in result) { result.dest = obj.dest; } return result; }).flatten().value(); return files; };