glob.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import { Minimatch } from 'minimatch';
  2. import { fileURLToPath } from 'node:url';
  3. import { PathScurry, PathScurryDarwin, PathScurryPosix, PathScurryWin32, } from 'path-scurry';
  4. import { Pattern } from './pattern.js';
  5. import { GlobStream, GlobWalker } from './walker.js';
  6. // if no process global, just call it linux.
  7. // so we default to case-sensitive, / separators
  8. const defaultPlatform = (typeof process === 'object' &&
  9. process &&
  10. typeof process.platform === 'string') ?
  11. process.platform
  12. : 'linux';
  13. /**
  14. * An object that can perform glob pattern traversals.
  15. */
  16. export class Glob {
  17. absolute;
  18. cwd;
  19. root;
  20. dot;
  21. dotRelative;
  22. follow;
  23. ignore;
  24. magicalBraces;
  25. mark;
  26. matchBase;
  27. maxDepth;
  28. nobrace;
  29. nocase;
  30. nodir;
  31. noext;
  32. noglobstar;
  33. pattern;
  34. platform;
  35. realpath;
  36. scurry;
  37. stat;
  38. signal;
  39. windowsPathsNoEscape;
  40. withFileTypes;
  41. includeChildMatches;
  42. /**
  43. * The options provided to the constructor.
  44. */
  45. opts;
  46. /**
  47. * An array of parsed immutable {@link Pattern} objects.
  48. */
  49. patterns;
  50. /**
  51. * All options are stored as properties on the `Glob` object.
  52. *
  53. * See {@link GlobOptions} for full options descriptions.
  54. *
  55. * Note that a previous `Glob` object can be passed as the
  56. * `GlobOptions` to another `Glob` instantiation to re-use settings
  57. * and caches with a new pattern.
  58. *
  59. * Traversal functions can be called multiple times to run the walk
  60. * again.
  61. */
  62. constructor(pattern, opts) {
  63. /* c8 ignore start */
  64. if (!opts)
  65. throw new TypeError('glob options required');
  66. /* c8 ignore stop */
  67. this.withFileTypes = !!opts.withFileTypes;
  68. this.signal = opts.signal;
  69. this.follow = !!opts.follow;
  70. this.dot = !!opts.dot;
  71. this.dotRelative = !!opts.dotRelative;
  72. this.nodir = !!opts.nodir;
  73. this.mark = !!opts.mark;
  74. if (!opts.cwd) {
  75. this.cwd = '';
  76. }
  77. else if (opts.cwd instanceof URL || opts.cwd.startsWith('file://')) {
  78. opts.cwd = fileURLToPath(opts.cwd);
  79. }
  80. this.cwd = opts.cwd || '';
  81. this.root = opts.root;
  82. this.magicalBraces = !!opts.magicalBraces;
  83. this.nobrace = !!opts.nobrace;
  84. this.noext = !!opts.noext;
  85. this.realpath = !!opts.realpath;
  86. this.absolute = opts.absolute;
  87. this.includeChildMatches = opts.includeChildMatches !== false;
  88. this.noglobstar = !!opts.noglobstar;
  89. this.matchBase = !!opts.matchBase;
  90. this.maxDepth =
  91. typeof opts.maxDepth === 'number' ? opts.maxDepth : Infinity;
  92. this.stat = !!opts.stat;
  93. this.ignore = opts.ignore;
  94. if (this.withFileTypes && this.absolute !== undefined) {
  95. throw new Error('cannot set absolute and withFileTypes:true');
  96. }
  97. if (typeof pattern === 'string') {
  98. pattern = [pattern];
  99. }
  100. this.windowsPathsNoEscape =
  101. !!opts.windowsPathsNoEscape ||
  102. opts.allowWindowsEscape ===
  103. false;
  104. if (this.windowsPathsNoEscape) {
  105. pattern = pattern.map(p => p.replace(/\\/g, '/'));
  106. }
  107. if (this.matchBase) {
  108. if (opts.noglobstar) {
  109. throw new TypeError('base matching requires globstar');
  110. }
  111. pattern = pattern.map(p => (p.includes('/') ? p : `./**/${p}`));
  112. }
  113. this.pattern = pattern;
  114. this.platform = opts.platform || defaultPlatform;
  115. this.opts = { ...opts, platform: this.platform };
  116. if (opts.scurry) {
  117. this.scurry = opts.scurry;
  118. if (opts.nocase !== undefined &&
  119. opts.nocase !== opts.scurry.nocase) {
  120. throw new Error('nocase option contradicts provided scurry option');
  121. }
  122. }
  123. else {
  124. const Scurry = opts.platform === 'win32' ? PathScurryWin32
  125. : opts.platform === 'darwin' ? PathScurryDarwin
  126. : opts.platform ? PathScurryPosix
  127. : PathScurry;
  128. this.scurry = new Scurry(this.cwd, {
  129. nocase: opts.nocase,
  130. fs: opts.fs,
  131. });
  132. }
  133. this.nocase = this.scurry.nocase;
  134. // If you do nocase:true on a case-sensitive file system, then
  135. // we need to use regexps instead of strings for non-magic
  136. // path portions, because statting `aBc` won't return results
  137. // for the file `AbC` for example.
  138. const nocaseMagicOnly = this.platform === 'darwin' || this.platform === 'win32';
  139. const mmo = {
  140. // default nocase based on platform
  141. ...opts,
  142. dot: this.dot,
  143. matchBase: this.matchBase,
  144. nobrace: this.nobrace,
  145. nocase: this.nocase,
  146. nocaseMagicOnly,
  147. nocomment: true,
  148. noext: this.noext,
  149. nonegate: true,
  150. optimizationLevel: 2,
  151. platform: this.platform,
  152. windowsPathsNoEscape: this.windowsPathsNoEscape,
  153. debug: !!this.opts.debug,
  154. };
  155. const mms = this.pattern.map(p => new Minimatch(p, mmo));
  156. const [matchSet, globParts] = mms.reduce((set, m) => {
  157. set[0].push(...m.set);
  158. set[1].push(...m.globParts);
  159. return set;
  160. }, [[], []]);
  161. this.patterns = matchSet.map((set, i) => {
  162. const g = globParts[i];
  163. /* c8 ignore start */
  164. if (!g)
  165. throw new Error('invalid pattern object');
  166. /* c8 ignore stop */
  167. return new Pattern(set, g, 0, this.platform);
  168. });
  169. }
  170. async walk() {
  171. // Walkers always return array of Path objects, so we just have to
  172. // coerce them into the right shape. It will have already called
  173. // realpath() if the option was set to do so, so we know that's cached.
  174. // start out knowing the cwd, at least
  175. return [
  176. ...(await new GlobWalker(this.patterns, this.scurry.cwd, {
  177. ...this.opts,
  178. maxDepth: this.maxDepth !== Infinity ?
  179. this.maxDepth + this.scurry.cwd.depth()
  180. : Infinity,
  181. platform: this.platform,
  182. nocase: this.nocase,
  183. includeChildMatches: this.includeChildMatches,
  184. }).walk()),
  185. ];
  186. }
  187. walkSync() {
  188. return [
  189. ...new GlobWalker(this.patterns, this.scurry.cwd, {
  190. ...this.opts,
  191. maxDepth: this.maxDepth !== Infinity ?
  192. this.maxDepth + this.scurry.cwd.depth()
  193. : Infinity,
  194. platform: this.platform,
  195. nocase: this.nocase,
  196. includeChildMatches: this.includeChildMatches,
  197. }).walkSync(),
  198. ];
  199. }
  200. stream() {
  201. return new GlobStream(this.patterns, this.scurry.cwd, {
  202. ...this.opts,
  203. maxDepth: this.maxDepth !== Infinity ?
  204. this.maxDepth + this.scurry.cwd.depth()
  205. : Infinity,
  206. platform: this.platform,
  207. nocase: this.nocase,
  208. includeChildMatches: this.includeChildMatches,
  209. }).stream();
  210. }
  211. streamSync() {
  212. return new GlobStream(this.patterns, this.scurry.cwd, {
  213. ...this.opts,
  214. maxDepth: this.maxDepth !== Infinity ?
  215. this.maxDepth + this.scurry.cwd.depth()
  216. : Infinity,
  217. platform: this.platform,
  218. nocase: this.nocase,
  219. includeChildMatches: this.includeChildMatches,
  220. }).streamSync();
  221. }
  222. /**
  223. * Default sync iteration function. Returns a Generator that
  224. * iterates over the results.
  225. */
  226. iterateSync() {
  227. return this.streamSync()[Symbol.iterator]();
  228. }
  229. [Symbol.iterator]() {
  230. return this.iterateSync();
  231. }
  232. /**
  233. * Default async iteration function. Returns an AsyncGenerator that
  234. * iterates over the results.
  235. */
  236. iterate() {
  237. return this.stream()[Symbol.asyncIterator]();
  238. }
  239. [Symbol.asyncIterator]() {
  240. return this.iterate();
  241. }
  242. }
  243. //# sourceMappingURL=glob.js.map