'use strict'; const fs = require('fs'); const arrayUnion = require('array-union'); const glob = require('glob'); const fastGlob = require('fast-glob'); const dirGlob = require('dir-glob'); const gitignore = require('./gitignore'); const DEFAULT_FILTER = () => false; const isNegative = pattern => pattern[0] === '!'; const assertPatternsInput = patterns => { if (!patterns.every(x => typeof x === 'string')) { throw new TypeError('Patterns must be a string or an array of strings'); } }; const checkCwdOption = options => { if (options && options.cwd && !fs.statSync(options.cwd).isDirectory()) { throw new Error('The `cwd` option must be a path to a directory'); } }; const generateGlobTasks = (patterns, taskOptions) => { patterns = arrayUnion([].concat(patterns)); assertPatternsInput(patterns); checkCwdOption(taskOptions); const globTasks = []; taskOptions = Object.assign({ ignore: [], expandDirectories: true }, taskOptions); patterns.forEach((pattern, i) => { if (isNegative(pattern)) { return; } const ignore = patterns .slice(i) .filter(isNegative) .map(pattern => pattern.slice(1)); const options = Object.assign({}, taskOptions, { ignore: taskOptions.ignore.concat(ignore) }); globTasks.push({pattern, options}); }); return globTasks; }; const globDirs = (task, fn) => { let options = {}; if (task.options.cwd) { options.cwd = task.options.cwd; } if (Array.isArray(task.options.expandDirectories)) { options = Object.assign(options, {files: task.options.expandDirectories}); } else if (typeof task.options.expandDirectories === 'object') { options = Object.assign(options, task.options.expandDirectories); } return fn(task.pattern, options); }; const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; const globToTask = task => glob => { const {options} = task; if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { options.ignore = dirGlob.sync(options.ignore); } return { pattern: glob, options }; }; const globby = (patterns, options) => { let globTasks; try { globTasks = generateGlobTasks(patterns, options); } catch (error) { return Promise.reject(error); } const getTasks = Promise.all(globTasks.map(task => Promise.resolve(getPattern(task, dirGlob)) .then(globs => Promise.all(globs.map(globToTask(task)))) )) .then(tasks => arrayUnion(...tasks)); const getFilter = () => { return Promise.resolve( options && options.gitignore ? gitignore({cwd: options.cwd, ignore: options.ignore}) : DEFAULT_FILTER ); }; return getFilter() .then(filter => { return getTasks .then(tasks => Promise.all(tasks.map(task => fastGlob(task.pattern, task.options)))) .then(paths => arrayUnion(...paths)) .then(paths => paths.filter(p => !filter(p))); }); }; module.exports = globby; // TODO: Remove this for the next major release module.exports.default = globby; module.exports.sync = (patterns, options) => { const globTasks = generateGlobTasks(patterns, options); const getFilter = () => { return options && options.gitignore ? gitignore.sync({cwd: options.cwd, ignore: options.ignore}) : DEFAULT_FILTER; }; const tasks = globTasks.reduce((tasks, task) => { const newTask = getPattern(task, dirGlob.sync).map(globToTask(task)); return tasks.concat(newTask); }, []); const filter = getFilter(); return tasks.reduce( (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), [] ).filter(p => !filter(p)); }; module.exports.generateGlobTasks = generateGlobTasks; module.exports.hasMagic = (patterns, options) => [] .concat(patterns) .some(pattern => glob.hasMagic(pattern, options)); module.exports.gitignore = gitignore;