/* * globule * https://github.com/cowboy/node-globule * * Copyright (c) 2018 "Cowboy" Ben Alman * Licensed under the MIT license. */ 'use strict'; var fs = require('fs'); var path = require('path'); var _ = require('lodash'); var glob = require('glob'); var minimatch = require('minimatch'); // The module. var globule = exports; // Process specified wildcard glob patterns or filenames against a // callback, excluding and uniquing files in the result set. function processPatterns(patterns, options, fn) { var result = []; _.each(patterns, function(pattern) { // The first character is not ! (inclusion). Add all matching filepaths // to the result set. if (pattern.indexOf('!') !== 0) { result = _.union(result, fn(pattern)); return; } // The first character is ! (exclusion). Remove any filepaths from the // result set that match this pattern, sans leading !. var filterFn = minimatch.filter(pattern.slice(1), options); result = _.filter(result, function(filepath) { return !filterFn(filepath); }); }); return result; } // Normalize paths to be unix-style. var pathSeparatorRe = /[\/\\]/g; function normalizePath(path) { return path.replace(pathSeparatorRe, '/'); } // Match a filepath or filepaths against one or more wildcard patterns. Returns // all matching filepaths. This behaves just like minimatch.match, but supports // any number of patterns. globule.match = function(patterns, filepaths, options) { // Return empty set if either patterns or filepaths was omitted. if (patterns == null || filepaths == null) { return []; } // Normalize patterns and filepaths to flattened arrays. patterns = _.isArray(patterns) ? _.flattenDeep(patterns) : [patterns]; filepaths = _.isArray(filepaths) ? _.flattenDeep(filepaths) : [filepaths]; // Return empty set if there are no patterns or filepaths. if (patterns.length === 0 || filepaths.length === 0) { return []; } // Return all matching filepaths. return processPatterns(patterns, options, function(pattern) { return minimatch.match(filepaths, pattern, options || {}); }); }; // Match a filepath or filepaths against one or more wildcard patterns. Returns // true if any of the patterns match. globule.isMatch = function() { return globule.match.apply(null, arguments).length > 0; }; // Return an array of all file paths that match the given wildcard patterns. globule.find = function() { var args = _.toArray(arguments); // If the last argument is an options object, remove it from args. var options = _.isPlainObject(args[args.length - 1]) ? args.pop() : {}; // If options.src was specified, use it. Otherwise, use all non-options // arguments. Flatten nested arrays. var patterns; if (options.src) { patterns = _.isArray(options.src) ? _.flattenDeep(options.src) : [options.src]; } else { patterns = _.flattenDeep(args); } // Return empty set if there are no patterns. if (patterns.length === 0) { return []; } var srcBase = options.srcBase || options.cwd; // Create glob-specific options object. var globOptions = _.extend({}, options); if (srcBase) { globOptions.cwd = srcBase; } // Get all matching filepaths. var matches = processPatterns(patterns, options, function(pattern) { return glob.sync(pattern, globOptions); }); // If srcBase and prefixBase were specified, prefix srcBase to matched paths. if (srcBase && options.prefixBase) { matches = matches.map(function(filepath) { return normalizePath(path.join(srcBase, filepath)); }); } // Filter result set? if (options.filter) { matches = matches.filter(function(filepath) { // If srcBase was specified but prefixBase was NOT, prefix srcBase // temporarily, for filtering. if (srcBase && !options.prefixBase) { filepath = normalizePath(path.join(srcBase, filepath)); } try { if (_.isFunction(options.filter)) { return options.filter(filepath, options); } else { // If the file is of the right type and exists, this should work. return fs.statSync(filepath)[options.filter](); } } catch(err) { // Otherwise, it's probably not the right type. return false; } }); } return matches; }; var extDotRe = { first: /(\.[^\/]*)?$/, last: /(\.[^\/\.]*)?$/, }; function rename(dest, options) { // Flatten path? if (options.flatten) { dest = path.basename(dest); } // Change the extension? if (options.ext) { dest = dest.replace(extDotRe[options.extDot], options.ext); } // Join dest and destBase? if (options.destBase) { dest = path.join(options.destBase, dest); } return dest; } // Build a mapping of src-dest filepaths from the given set of filepaths. globule.mapping = function(filepaths, options) { // Return empty set if filepaths was omitted. if (filepaths == null) { return []; } options = _.defaults({}, options, { extDot: 'first', rename: rename, }); var files = []; var fileByDest = {}; // Find all files matching pattern, using passed-in options. filepaths.forEach(function(src) { // Generate destination filename. var dest = options.rename(src, options); // Prepend srcBase to all src paths. if (options.srcBase) { src = path.join(options.srcBase, src); } // Normalize filepaths to be unix-style. dest = normalizePath(dest); src = normalizePath(src); // 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; }; // Return a mapping of src-dest filepaths from files matching the given // wildcard patterns. globule.findMapping = function() { var args = _.toArray(arguments); // If the last argument is an options object, remove it from args. var options = _.isPlainObject(args[args.length - 1]) ? args.pop() : {}; // Generate mapping from found filepaths. return globule.mapping(globule.find(args, options), options); };