@@ -17,6 +17,8 @@ const hash = require("../cli-engine/hash");
1717const minimatch = require ( "minimatch" ) ;
1818const util = require ( "util" ) ;
1919const fswalk = require ( "@nodelib/fs.walk" ) ;
20+ const globParent = require ( "glob-parent" ) ;
21+ const isPathInside = require ( "is-path-inside" ) ;
2022
2123//-----------------------------------------------------------------------------
2224// Fixup references
@@ -111,38 +113,38 @@ function isGlobPattern(pattern) {
111113 * be ignored, so it is consistent with how ignoring works throughout
112114 * ESLint.
113115 * @param {Object } options The options for this function.
114- * @param {string } options.cwd The directory to search.
116+ * @param {string } options.basePath The directory to search.
115117 * @param {Array<string> } options.patterns An array of glob patterns
116118 * to match.
117119 * @param {FlatConfigArray } options.configs The config array to use for
118120 * determining what to ignore.
119121 * @returns {Promise<Array<string>> } An array of matching file paths
120122 * or an empty array if there are no matches.
121123 */
122- async function globSearch ( { cwd , patterns, configs } ) {
124+ async function globSearch ( { basePath , patterns, configs } ) {
123125
124126 if ( patterns . length === 0 ) {
125127 return [ ] ;
126128 }
127129
128130 const matchers = patterns . map ( pattern => {
129131 const patternToUse = path . isAbsolute ( pattern )
130- ? normalizeToPosix ( path . relative ( cwd , pattern ) )
132+ ? normalizeToPosix ( path . relative ( basePath , pattern ) )
131133 : pattern ;
132134
133135 return new minimatch . Minimatch ( patternToUse ) ;
134136 } ) ;
135137
136- return ( await doFsWalk ( cwd , {
138+ return ( await doFsWalk ( basePath , {
137139
138140 deepFilter ( entry ) {
139- const relativePath = normalizeToPosix ( path . relative ( cwd , entry . path ) ) ;
141+ const relativePath = normalizeToPosix ( path . relative ( basePath , entry . path ) ) ;
140142 const matchesPattern = matchers . some ( matcher => matcher . match ( relativePath , true ) ) ;
141143
142144 return matchesPattern && ! configs . isDirectoryIgnored ( entry . path ) ;
143145 } ,
144146 entryFilter ( entry ) {
145- const relativePath = normalizeToPosix ( path . relative ( cwd , entry . path ) ) ;
147+ const relativePath = normalizeToPosix ( path . relative ( basePath , entry . path ) ) ;
146148
147149 // entries may be directories or files so filter out directories
148150 if ( entry . dirent . isDirectory ( ) ) {
@@ -157,27 +159,48 @@ async function globSearch({ cwd, patterns, configs }) {
157159
158160}
159161
162+ /**
163+ * Performs multiple glob searches in parallel.
164+ * @param {Object } options The options for this function.
165+ * @param {Array<{patterns:Array<string>,rawPatterns:Array<string>}> } options.searches
166+ * An array of glob patterns to match.
167+ * @param {FlatConfigArray } options.configs The config array to use for
168+ * determining what to ignore.
169+ * @returns {Promise<Array<string>> } An array of matching file paths
170+ * or an empty array if there are no matches.
171+ */
172+ async function globMultiSearch ( { searches, configs } ) {
173+
174+ const results = await Promise . all (
175+ [ ...searches ] . map (
176+ ( [ basePath , { patterns } ] ) => globSearch ( { basePath, patterns, configs } )
177+ )
178+ ) ;
179+
180+ return [ ...new Set ( results . flat ( ) ) ] ;
181+ }
182+
160183/**
161184 * Determines if a given glob pattern will return any results.
162185 * Used primarily to help with useful error messages.
163186 * @param {Object } options The options for the function.
164- * @param {string } options.cwd The directory to search.
187+ * @param {string } options.basePath The directory to search.
165188 * @param {string } options.pattern A glob pattern to match.
166189 * @returns {Promise<boolean> } True if there is a glob match, false if not.
167190 */
168- function globMatch ( { cwd , pattern } ) {
191+ function globMatch ( { basePath , pattern } ) {
169192
170193 let found = false ;
171194 const patternToUse = path . isAbsolute ( pattern )
172- ? normalizeToPosix ( path . relative ( cwd , pattern ) )
195+ ? normalizeToPosix ( path . relative ( basePath , pattern ) )
173196 : pattern ;
174197
175198 const matcher = new Minimatch ( patternToUse ) ;
176199
177200 const fsWalkSettings = {
178201
179202 deepFilter ( entry ) {
180- const relativePath = normalizeToPosix ( path . relative ( cwd , entry . path ) ) ;
203+ const relativePath = normalizeToPosix ( path . relative ( basePath , entry . path ) ) ;
181204
182205 return ! found && matcher . match ( relativePath , true ) ;
183206 } ,
@@ -187,7 +210,7 @@ function globMatch({ cwd, pattern }) {
187210 return false ;
188211 }
189212
190- const relativePath = normalizeToPosix ( path . relative ( cwd , entry . path ) ) ;
213+ const relativePath = normalizeToPosix ( path . relative ( basePath , entry . path ) ) ;
191214
192215 if ( matcher . match ( relativePath ) ) {
193216 found = true ;
@@ -201,7 +224,7 @@ function globMatch({ cwd, pattern }) {
201224 return new Promise ( resolve => {
202225
203226 // using a stream so we can exit early because we just need one match
204- const globStream = fswalk . walkStream ( cwd , fsWalkSettings ) ;
227+ const globStream = fswalk . walkStream ( basePath , fsWalkSettings ) ;
205228
206229 globStream . on ( "data" , ( ) => {
207230 globStream . destroy ( ) ;
@@ -242,8 +265,10 @@ async function findFiles({
242265} ) {
243266
244267 const results = [ ] ;
245- const globbyPatterns = [ ] ;
246268 const missingPatterns = [ ] ;
269+ let globbyPatterns = [ ] ;
270+ let rawPatterns = [ ] ;
271+ const searches = new Map ( [ [ cwd , { patterns : globbyPatterns , rawPatterns : [ ] } ] ] ) ;
247272
248273 // check to see if we have explicit files and directories
249274 const filePaths = patterns . map ( filePath => path . resolve ( cwd , filePath ) ) ;
@@ -271,109 +296,76 @@ async function findFiles({
271296 // directories need extensions attached
272297 if ( stat . isDirectory ( ) ) {
273298
274- // filePatterns are all relative to cwd
275- const filePatterns = configs . files
276- . filter ( filePattern => {
277-
278- // can only do this for strings, not functions
279- if ( typeof filePattern !== "string" ) {
280- return false ;
281- }
282-
283- // patterns starting with ** always apply
284- if ( filePattern . startsWith ( "**" ) ) {
285- return true ;
286- }
287-
288- // patterns ending with * are not used for file search
289- if ( filePattern . endsWith ( "*" ) ) {
290- return false ;
291- }
292-
293- // not sure how to handle negated patterns yet
294- if ( filePattern . startsWith ( "!" ) ) {
295- return false ;
296- }
297-
298- // check if the pattern would be inside the config base path or not
299- const fullFilePattern = path . join ( cwd , filePattern ) ;
300- const patternRelativeToConfigBasePath = path . relative ( configs . basePath , fullFilePattern ) ;
301-
302- if ( patternRelativeToConfigBasePath . startsWith ( ".." ) ) {
303- return false ;
304- }
305-
306- // check if the pattern matches
307- if ( minimatch ( filePath , path . dirname ( fullFilePattern ) , { partial : true } ) ) {
308- return true ;
309- }
310-
311- // check if the pattern is inside the directory or not
312- const patternRelativeToFilePath = path . relative ( filePath , fullFilePattern ) ;
313-
314- if ( patternRelativeToFilePath . startsWith ( ".." ) ) {
315- return false ;
316- }
317-
318- return true ;
319- } )
320- . map ( filePattern => {
321- if ( filePattern . startsWith ( "**" ) ) {
322- return path . join ( pattern , filePattern ) ;
323- }
324-
325- // adjust the path to be relative to the cwd
326- return path . relative (
327- cwd ,
328- path . join ( configs . basePath , filePattern )
329- ) ;
330- } )
331- . map ( normalizeToPosix ) ;
332-
333- if ( filePatterns . length ) {
334- globbyPatterns . push ( ...filePatterns ) ;
299+ // group everything in cwd together and split out others
300+ if ( isPathInside ( filePath , cwd ) ) {
301+ ( { patterns : globbyPatterns , rawPatterns } = searches . get ( cwd ) ) ;
302+ } else {
303+ if ( ! searches . has ( filePath ) ) {
304+ searches . set ( filePath , { patterns : [ ] , rawPatterns : [ ] } ) ;
305+ }
306+ ( { patterns : globbyPatterns , rawPatterns } = searches . get ( filePath ) ) ;
335307 }
336308
309+ globbyPatterns . push ( `${ normalizeToPosix ( filePath ) } /**` ) ;
310+ rawPatterns . push ( pattern ) ;
337311 }
338312
339313 return ;
340314 }
341315
342316 // save patterns for later use based on whether globs are enabled
343317 if ( globInputPaths && isGlobPattern ( filePath ) ) {
344- globbyPatterns . push ( pattern ) ;
318+
319+ const basePath = globParent ( filePath ) ;
320+
321+ // group in cwd if possible and split out others
322+ if ( isPathInside ( basePath , cwd ) ) {
323+ ( { patterns : globbyPatterns , rawPatterns } = searches . get ( cwd ) ) ;
324+ } else {
325+ if ( ! searches . has ( basePath ) ) {
326+ searches . set ( basePath , { patterns : [ ] , rawPatterns : [ ] } ) ;
327+ }
328+ ( { patterns : globbyPatterns , rawPatterns } = searches . get ( basePath ) ) ;
329+ }
330+
331+ globbyPatterns . push ( filePath ) ;
332+ rawPatterns . push ( pattern ) ;
345333 } else {
346334 missingPatterns . push ( pattern ) ;
347335 }
348336 } ) ;
349337
350- // note: globbyPatterns can be an empty array
351- const globbyResults = await globSearch ( {
352- cwd,
353- patterns : globbyPatterns ,
354- configs,
355- shouldIgnore : true
338+ const globbyResults = await globMultiSearch ( {
339+ searches,
340+ configs
356341 } ) ;
357342
358343 // if there are no results, tell the user why
359344 if ( ! results . length && ! globbyResults . length ) {
360345
361- // try globby without ignoring anything
362- for ( const globbyPattern of globbyPatterns ) {
346+ for ( const [ basePath , { patterns : patternsToCheck , rawPatterns : patternsToReport } ] of searches ) {
363347
364- // check if there are any matches at all
365- const patternHasMatch = await globMatch ( {
366- cwd,
367- pattern : globbyPattern
368- } ) ;
348+ let index = 0 ;
369349
370- if ( patternHasMatch ) {
371- throw new AllFilesIgnoredError ( globbyPattern ) ;
372- }
350+ // try globby without ignoring anything
351+ for ( const patternToCheck of patternsToCheck ) {
352+
353+ // check if there are any matches at all
354+ const patternHasMatch = await globMatch ( {
355+ basePath,
356+ pattern : patternToCheck
357+ } ) ;
358+
359+ if ( patternHasMatch ) {
360+ throw new AllFilesIgnoredError ( patternsToReport [ index ] ) ;
361+ }
362+
363+ // otherwise no files were found
364+ if ( errorOnUnmatchedPattern ) {
365+ throw new NoFilesFoundError ( patternsToReport [ index ] , globInputPaths ) ;
366+ }
373367
374- // otherwise no files were found
375- if ( errorOnUnmatchedPattern ) {
376- throw new NoFilesFoundError ( globbyPattern , globInputPaths ) ;
368+ index ++ ;
377369 }
378370 }
379371
0 commit comments