blob: 22bb640a85f142654623062f45ceb86b3c7a4a13 [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001'use strict';
2const arrayUnion = require('array-union');
3const glob = require('glob');
4const fastGlob = require('fast-glob');
5const dirGlob = require('dir-glob');
6const gitignore = require('./gitignore');
7
8const DEFAULT_FILTER = () => false;
9
10const isNegative = pattern => pattern[0] === '!';
11
12const assertPatternsInput = patterns => {
13 if (!patterns.every(x => typeof x === 'string')) {
14 throw new TypeError('Patterns must be a string or an array of strings');
15 }
16};
17
18const generateGlobTasks = (patterns, taskOpts) => {
19 patterns = [].concat(patterns);
20 assertPatternsInput(patterns);
21
22 const globTasks = [];
23
24 taskOpts = Object.assign({
25 ignore: [],
26 expandDirectories: true
27 }, taskOpts);
28
29 patterns.forEach((pattern, i) => {
30 if (isNegative(pattern)) {
31 return;
32 }
33
34 const ignore = patterns
35 .slice(i)
36 .filter(isNegative)
37 .map(pattern => pattern.slice(1));
38
39 const opts = Object.assign({}, taskOpts, {
40 ignore: taskOpts.ignore.concat(ignore)
41 });
42
43 globTasks.push({pattern, opts});
44 });
45
46 return globTasks;
47};
48
49const globDirs = (task, fn) => {
50 let opts = {cwd: task.opts.cwd};
51
52 if (Array.isArray(task.opts.expandDirectories)) {
53 opts = Object.assign(opts, {files: task.opts.expandDirectories});
54 } else if (typeof task.opts.expandDirectories === 'object') {
55 opts = Object.assign(opts, task.opts.expandDirectories);
56 }
57
58 return fn(task.pattern, opts);
59};
60
61const getPattern = (task, fn) => task.opts.expandDirectories ? globDirs(task, fn) : [task.pattern];
62
63module.exports = (patterns, opts) => {
64 let globTasks;
65
66 try {
67 globTasks = generateGlobTasks(patterns, opts);
68 } catch (err) {
69 return Promise.reject(err);
70 }
71
72 const getTasks = Promise.all(globTasks.map(task => Promise.resolve(getPattern(task, dirGlob))
73 .then(globs => Promise.all(globs.map(glob => ({
74 pattern: glob,
75 opts: task.opts
76 }))))
77 ))
78 .then(tasks => arrayUnion.apply(null, tasks));
79
80 const getFilter = () => {
81 return Promise.resolve(
82 opts && opts.gitignore ?
83 gitignore({cwd: opts.cwd, ignore: opts.ignore}) :
84 DEFAULT_FILTER
85 );
86 };
87
88 return getFilter()
89 .then(filter => {
90 return getTasks
91 .then(tasks => Promise.all(tasks.map(task => fastGlob(task.pattern, task.opts))))
92 .then(paths => arrayUnion.apply(null, paths))
93 .then(paths => paths.filter(p => !filter(p)));
94 });
95};
96
97module.exports.sync = (patterns, opts) => {
98 const globTasks = generateGlobTasks(patterns, opts);
99
100 const getFilter = () => {
101 return opts && opts.gitignore ?
102 gitignore.sync({cwd: opts.cwd, ignore: opts.ignore}) :
103 DEFAULT_FILTER;
104 };
105
106 const tasks = globTasks.reduce((tasks, task) => {
107 const newTask = getPattern(task, dirGlob.sync).map(glob => ({
108 pattern: glob,
109 opts: task.opts
110 }));
111 return tasks.concat(newTask);
112 }, []);
113
114 const filter = getFilter();
115
116 return tasks.reduce(
117 (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.opts)),
118 []
119 ).filter(p => !filter(p));
120};
121
122module.exports.generateGlobTasks = generateGlobTasks;
123
124module.exports.hasMagic = (patterns, opts) => []
125 .concat(patterns)
126 .some(pattern => glob.hasMagic(pattern, opts));
127
128module.exports.gitignore = gitignore;