index.js 3.7 KB

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