blob: 2efe36192e06b4fa212275a5cd4c5af54205e168 [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001/*
2 * globule
3 * https://github.com/cowboy/node-globule
4 *
5 * Copyright (c) 2018 "Cowboy" Ben Alman
6 * Licensed under the MIT license.
7 */
8
9'use strict';
10
11var fs = require('fs');
12var path = require('path');
13
14var _ = require('lodash');
15var glob = require('glob');
16var minimatch = require('minimatch');
17
18// The module.
19var globule = exports;
20
21// Process specified wildcard glob patterns or filenames against a
22// callback, excluding and uniquing files in the result set.
23function processPatterns(patterns, options, fn) {
24 var result = [];
25 _.each(patterns, function(pattern) {
26 // The first character is not ! (inclusion). Add all matching filepaths
27 // to the result set.
28 if (pattern.indexOf('!') !== 0) {
29 result = _.union(result, fn(pattern));
30 return;
31 }
32 // The first character is ! (exclusion). Remove any filepaths from the
33 // result set that match this pattern, sans leading !.
34 var filterFn = minimatch.filter(pattern.slice(1), options);
35 result = _.filter(result, function(filepath) {
36 return !filterFn(filepath);
37 });
38 });
39 return result;
40}
41
42// Normalize paths to be unix-style.
43var pathSeparatorRe = /[\/\\]/g;
44function normalizePath(path) {
45 return path.replace(pathSeparatorRe, '/');
46}
47
48// Match a filepath or filepaths against one or more wildcard patterns. Returns
49// all matching filepaths. This behaves just like minimatch.match, but supports
50// any number of patterns.
51globule.match = function(patterns, filepaths, options) {
52 // Return empty set if either patterns or filepaths was omitted.
53 if (patterns == null || filepaths == null) { return []; }
54 // Normalize patterns and filepaths to flattened arrays.
55 patterns = _.isArray(patterns) ? _.flattenDeep(patterns) : [patterns];
56 filepaths = _.isArray(filepaths) ? _.flattenDeep(filepaths) : [filepaths];
57 // Return empty set if there are no patterns or filepaths.
58 if (patterns.length === 0 || filepaths.length === 0) { return []; }
59 // Return all matching filepaths.
60 return processPatterns(patterns, options, function(pattern) {
61 return minimatch.match(filepaths, pattern, options || {});
62 });
63};
64
65// Match a filepath or filepaths against one or more wildcard patterns. Returns
66// true if any of the patterns match.
67globule.isMatch = function() {
68 return globule.match.apply(null, arguments).length > 0;
69};
70
71// Return an array of all file paths that match the given wildcard patterns.
72globule.find = function() {
73 var args = _.toArray(arguments);
74 // If the last argument is an options object, remove it from args.
75 var options = _.isPlainObject(args[args.length - 1]) ? args.pop() : {};
76 // If options.src was specified, use it. Otherwise, use all non-options
77 // arguments. Flatten nested arrays.
78 var patterns;
79 if (options.src) {
80 patterns = _.isArray(options.src) ? _.flattenDeep(options.src) : [options.src];
81 } else {
82 patterns = _.flattenDeep(args);
83 }
84 // Return empty set if there are no patterns.
85 if (patterns.length === 0) { return []; }
86 var srcBase = options.srcBase || options.cwd;
87 // Create glob-specific options object.
88 var globOptions = _.extend({}, options);
89 if (srcBase) {
90 globOptions.cwd = srcBase;
91 }
92 // Get all matching filepaths.
93 var matches = processPatterns(patterns, options, function(pattern) {
94 return glob.sync(pattern, globOptions);
95 });
96 // If srcBase and prefixBase were specified, prefix srcBase to matched paths.
97 if (srcBase && options.prefixBase) {
98 matches = matches.map(function(filepath) {
99 return normalizePath(path.join(srcBase, filepath));
100 });
101 }
102 // Filter result set?
103 if (options.filter) {
104 matches = matches.filter(function(filepath) {
105 // If srcBase was specified but prefixBase was NOT, prefix srcBase
106 // temporarily, for filtering.
107 if (srcBase && !options.prefixBase) {
108 filepath = normalizePath(path.join(srcBase, filepath));
109 }
110 try {
111 if (_.isFunction(options.filter)) {
112 return options.filter(filepath, options);
113 } else {
114 // If the file is of the right type and exists, this should work.
115 return fs.statSync(filepath)[options.filter]();
116 }
117 } catch(err) {
118 // Otherwise, it's probably not the right type.
119 return false;
120 }
121 });
122 }
123 return matches;
124};
125
126var extDotRe = {
127 first: /(\.[^\/]*)?$/,
128 last: /(\.[^\/\.]*)?$/,
129};
130function rename(dest, options) {
131 // Flatten path?
132 if (options.flatten) {
133 dest = path.basename(dest);
134 }
135 // Change the extension?
136 if (options.ext) {
137 dest = dest.replace(extDotRe[options.extDot], options.ext);
138 }
139 // Join dest and destBase?
140 if (options.destBase) {
141 dest = path.join(options.destBase, dest);
142 }
143 return dest;
144}
145
146// Build a mapping of src-dest filepaths from the given set of filepaths.
147globule.mapping = function(filepaths, options) {
148 // Return empty set if filepaths was omitted.
149 if (filepaths == null) { return []; }
150 options = _.defaults({}, options, {
151 extDot: 'first',
152 rename: rename,
153 });
154 var files = [];
155 var fileByDest = {};
156 // Find all files matching pattern, using passed-in options.
157 filepaths.forEach(function(src) {
158 // Generate destination filename.
159 var dest = options.rename(src, options);
160 // Prepend srcBase to all src paths.
161 if (options.srcBase) {
162 src = path.join(options.srcBase, src);
163 }
164 // Normalize filepaths to be unix-style.
165 dest = normalizePath(dest);
166 src = normalizePath(src);
167 // Map correct src path to dest path.
168 if (fileByDest[dest]) {
169 // If dest already exists, push this src onto that dest's src array.
170 fileByDest[dest].src.push(src);
171 } else {
172 // Otherwise create a new src-dest file mapping object.
173 files.push({
174 src: [src],
175 dest: dest,
176 });
177 // And store a reference for later use.
178 fileByDest[dest] = files[files.length - 1];
179 }
180 });
181 return files;
182};
183
184// Return a mapping of src-dest filepaths from files matching the given
185// wildcard patterns.
186globule.findMapping = function() {
187 var args = _.toArray(arguments);
188 // If the last argument is an options object, remove it from args.
189 var options = _.isPlainObject(args[args.length - 1]) ? args.pop() : {};
190 // Generate mapping from found filepaths.
191 return globule.mapping(globule.find(args, options), options);
192};