11import path , { posix } from 'node:path' ;
22import { type Options as FdirOptions , fdir } from 'fdir' ;
33import picomatch from 'picomatch' ;
4- import { escapePath , getPartialMatcher , isDynamicPattern , splitPattern } from './utils.ts' ;
4+ import { escapePath , getPartialMatcher , isDynamicPattern , log , splitPattern } from './utils.ts' ;
55
66const PARENT_DIRECTORY = / ^ ( \/ ? \. \. ) + / ;
77const ESCAPING_BACKSLASHES = / \\ (? = [ ( ) [ \] { } ! * + ? @ | ] ) / g;
@@ -22,7 +22,7 @@ export interface GlobOptions {
2222 debug ?: boolean ;
2323}
2424
25- interface InternalProperties {
25+ interface InternalProps {
2626 root : string ;
2727 commonPath : string [ ] | null ;
2828 depthOffset : number ;
@@ -32,7 +32,7 @@ function normalizePattern(
3232 pattern : string ,
3333 expandDirectories : boolean ,
3434 cwd : string ,
35- properties : InternalProperties ,
35+ props : InternalProps ,
3636 isIgnore : boolean
3737) {
3838 let result : string = pattern ;
@@ -53,35 +53,36 @@ function normalizePattern(
5353 const parentDirectoryMatch = PARENT_DIRECTORY . exec ( result ) ;
5454 if ( parentDirectoryMatch ?. [ 0 ] ) {
5555 const potentialRoot = posix . join ( cwd , parentDirectoryMatch [ 0 ] ) ;
56- if ( properties . root . length > potentialRoot . length ) {
57- properties . root = potentialRoot ;
58- properties . depthOffset = - ( parentDirectoryMatch [ 0 ] . length + 1 ) / 3 ;
56+ if ( props . root . length > potentialRoot . length ) {
57+ props . root = potentialRoot ;
58+ props . depthOffset = - ( parentDirectoryMatch [ 0 ] . length + 1 ) / 3 ;
5959 }
60- } else if ( ! isIgnore && properties . depthOffset >= 0 ) {
60+ } else if ( ! isIgnore && props . depthOffset >= 0 ) {
6161 const parts = splitPattern ( result ) ;
62- properties . commonPath ??= parts ;
62+ props . commonPath ??= parts ;
6363
64- const newCommonPath = [ ] ;
64+ const newCommonPath : string [ ] = [ ] ;
65+ const length = Math . min ( props . commonPath . length , parts . length ) ;
6566
66- for ( let i = 0 ; i < Math . min ( properties . commonPath . length , parts . length ) ; i ++ ) {
67+ for ( let i = 0 ; i < length ; i ++ ) {
6768 const part = parts [ i ] ;
6869
6970 if ( part === '**' && ! parts [ i + 1 ] ) {
7071 newCommonPath . pop ( ) ;
7172 break ;
7273 }
7374
74- if ( part !== properties . commonPath [ i ] || isDynamicPattern ( part ) || i === parts . length - 1 ) {
75+ if ( part !== props . commonPath [ i ] || isDynamicPattern ( part ) || i === parts . length - 1 ) {
7576 break ;
7677 }
7778
7879 newCommonPath . push ( part ) ;
7980 }
8081
81- properties . depthOffset = newCommonPath . length ;
82- properties . commonPath = newCommonPath ;
82+ props . depthOffset = newCommonPath . length ;
83+ props . commonPath = newCommonPath ;
8384
84- properties . root = newCommonPath . length > 0 ? `${ cwd } /${ newCommonPath . join ( '/' ) } ` : cwd ;
85+ props . root = newCommonPath . length > 0 ? `${ cwd } /${ newCommonPath . join ( '/' ) } ` : cwd ;
8586 }
8687
8788 return result ;
@@ -90,7 +91,7 @@ function normalizePattern(
9091function processPatterns (
9192 { patterns, ignore = [ ] , expandDirectories = true } : GlobOptions ,
9293 cwd : string ,
93- properties : InternalProperties
94+ props : InternalProps
9495) {
9596 if ( typeof patterns === 'string' ) {
9697 patterns = [ patterns ] ;
@@ -111,22 +112,19 @@ function processPatterns(
111112 continue ;
112113 }
113114 // don't handle negated patterns here for consistency with fast-glob
114- if ( ! pattern . startsWith ( '!' ) || pattern [ 1 ] === '(' ) {
115- const newPattern = normalizePattern ( pattern , expandDirectories , cwd , properties , true ) ;
116- ignorePatterns . push ( newPattern ) ;
115+ if ( pattern [ 0 ] !== '!' || pattern [ 1 ] === '(' ) {
116+ ignorePatterns . push ( normalizePattern ( pattern , expandDirectories , cwd , props , true ) ) ;
117117 }
118118 }
119119
120120 for ( const pattern of patterns ) {
121121 if ( ! pattern ) {
122122 continue ;
123123 }
124- if ( ! pattern . startsWith ( '!' ) || pattern [ 1 ] === '(' ) {
125- const newPattern = normalizePattern ( pattern , expandDirectories , cwd , properties , false ) ;
126- matchPatterns . push ( newPattern ) ;
124+ if ( pattern [ 0 ] !== '!' || pattern [ 1 ] === '(' ) {
125+ matchPatterns . push ( normalizePattern ( pattern , expandDirectories , cwd , props , false ) ) ;
127126 } else if ( pattern [ 1 ] !== '!' || pattern [ 2 ] === '(' ) {
128- const newPattern = normalizePattern ( pattern . slice ( 1 ) , expandDirectories , cwd , properties , true ) ;
129- ignorePatterns . push ( newPattern ) ;
127+ ignorePatterns . push ( normalizePattern ( pattern . slice ( 1 ) , expandDirectories , cwd , props , true ) ) ;
130128 }
131129 }
132130
@@ -148,35 +146,44 @@ function processPath(path: string, cwd: string, root: string, isDirectory: boole
148146 return getRelativePath ( relativePath , cwd , root ) ;
149147}
150148
149+ function formatPaths ( paths : string [ ] , cwd : string , root : string ) {
150+ for ( let i = paths . length - 1 ; i >= 0 ; i -- ) {
151+ const path = paths [ i ] ;
152+ paths [ i ] = getRelativePath ( path , cwd , root ) + ( ! path || path . endsWith ( '/' ) ? '/' : '' ) ;
153+ }
154+ return paths ;
155+ }
156+
151157function crawl ( options : GlobOptions , cwd : string , sync : false ) : Promise < string [ ] > ;
152158function crawl ( options : GlobOptions , cwd : string , sync : true ) : string [ ] ;
153159function crawl ( options : GlobOptions , cwd : string , sync : boolean ) {
154160 if ( Array . isArray ( options . patterns ) && options . patterns . length === 0 ) {
155161 return sync ? [ ] : Promise . resolve ( [ ] ) ;
156162 }
157163
158- const properties = {
164+ const props = {
159165 root : cwd ,
160166 commonPath : null ,
161167 depthOffset : 0
162168 } ;
163169
164- const processed = processPatterns ( options , cwd , properties ) ;
170+ const processed = processPatterns ( options , cwd , props ) ;
171+ const nocase = options . caseSensitiveMatch === false ;
165172
166173 const matcher = picomatch ( processed . match , {
167174 dot : options . dot ,
168- nocase : options . caseSensitiveMatch === false ,
175+ nocase,
169176 ignore : processed . ignore
170177 } ) ;
171178
172179 const ignore = picomatch ( processed . ignore , {
173180 dot : options . dot ,
174- nocase : options . caseSensitiveMatch === false
181+ nocase
175182 } ) ;
176183
177184 const partialMatcher = getPartialMatcher ( processed . match , {
178185 dot : options . dot ,
179- nocase : options . caseSensitiveMatch === false
186+ nocase
180187 } ) ;
181188
182189 if ( process . env . TINYGLOBBY_DEBUG ) {
@@ -188,30 +195,30 @@ function crawl(options: GlobOptions, cwd: string, sync: boolean) {
188195 filters : [
189196 options . debug
190197 ? ( p , isDirectory ) => {
191- const path = processPath ( p , cwd , properties . root , isDirectory , options . absolute ) ;
198+ const path = processPath ( p , cwd , props . root , isDirectory , options . absolute ) ;
192199 const matches = matcher ( path ) ;
193200
194201 if ( matches ) {
195- console . log ( `[tinyglobby ${ new Date ( ) . toLocaleTimeString ( 'es' ) } ] matched ${ path } ` ) ;
202+ log ( `matched ${ path } ` ) ;
196203 }
197204
198205 return matches ;
199206 }
200- : ( p , isDirectory ) => matcher ( processPath ( p , cwd , properties . root , isDirectory , options . absolute ) )
207+ : ( p , isDirectory ) => matcher ( processPath ( p , cwd , props . root , isDirectory , options . absolute ) )
201208 ] ,
202209 exclude : options . debug
203210 ? ( _ , p ) => {
204- const relativePath = processPath ( p , cwd , properties . root , true , true ) ;
211+ const relativePath = processPath ( p , cwd , props . root , true , true ) ;
205212 const skipped = ( relativePath !== '.' && ! partialMatcher ( relativePath ) ) || ignore ( relativePath ) ;
206213
207214 if ( ! skipped ) {
208- console . log ( `[tinyglobby ${ new Date ( ) . toLocaleTimeString ( 'es' ) } ] crawling ${ p } ` ) ;
215+ log ( `crawling ${ p } ` ) ;
209216 }
210217
211218 return skipped ;
212219 }
213220 : ( _ , p ) => {
214- const relativePath = processPath ( p , cwd , properties . root , true , true ) ;
221+ const relativePath = processPath ( p , cwd , props . root , true , true ) ;
215222 return ( relativePath !== '.' && ! partialMatcher ( relativePath ) ) || ignore ( relativePath ) ;
216223 } ,
217224 pathSeparator : '/' ,
@@ -220,7 +227,7 @@ function crawl(options: GlobOptions, cwd: string, sync: boolean) {
220227 } ;
221228
222229 if ( options . deep ) {
223- fdirOptions . maxDepth = Math . round ( options . deep - properties . depthOffset ) ;
230+ fdirOptions . maxDepth = Math . round ( options . deep - props . depthOffset ) ;
224231 }
225232
226233 if ( options . absolute ) {
@@ -241,19 +248,15 @@ function crawl(options: GlobOptions, cwd: string, sync: boolean) {
241248 fdirOptions . includeDirs = true ;
242249 }
243250
244- // backslashes are removed so that inferred roots like `C:/New folder \\(1\\)` work
245- properties . root = properties . root . replace ( BACKSLASHES , '' ) ;
246- const api = new fdir ( fdirOptions ) . crawl ( properties . root ) ;
251+ props . root = props . root . replace ( BACKSLASHES , '' ) ;
252+ const root = props . root ;
253+ const api = new fdir ( fdirOptions ) . crawl ( root ) ;
247254
248- if ( cwd === properties . root || options . absolute ) {
255+ if ( cwd === root || options . absolute ) {
249256 return sync ? api . sync ( ) : api . withPromise ( ) ;
250257 }
251258
252- return sync
253- ? api . sync ( ) . map ( p => getRelativePath ( p , cwd , properties . root ) + ( ! p || p . endsWith ( '/' ) ? '/' : '' ) )
254- : api
255- . withPromise ( )
256- . then ( paths => paths . map ( p => getRelativePath ( p , cwd , properties . root ) + ( ! p || p . endsWith ( '/' ) ? '/' : '' ) ) ) ;
259+ return sync ? formatPaths ( api . sync ( ) , cwd , root ) : api . withPromise ( ) . then ( paths => formatPaths ( paths , cwd , root ) ) ;
257260}
258261
259262export function glob ( patterns : string | string [ ] , options ?: Omit < GlobOptions , 'patterns' > ) : Promise < string [ ] > ;
0 commit comments