@@ -289,28 +289,59 @@ const patternHasGlob = (pattern: string) =>
289289const normalizeSparsePatterns = ( include ?: string [ ] ) =>
290290 ( include ?? [ ] ) . map ( ( pattern ) => pattern . replace ( / \\ / g, "/" ) ) . filter ( Boolean ) ;
291291
292+ const isDirectoryLiteral = ( pattern : string ) => {
293+ if ( pattern . endsWith ( "/" ) ) {
294+ return true ;
295+ }
296+ const base = pattern . split ( "/" ) . pop ( ) ?? "" ;
297+ return ! base . includes ( "." ) ;
298+ } ;
299+
300+ const toNoConePattern = ( pattern : string ) => {
301+ if ( ! patternHasGlob ( pattern ) && isDirectoryLiteral ( pattern ) ) {
302+ return pattern . endsWith ( "/" ) ? pattern : `${ pattern } /` ;
303+ }
304+ return pattern ;
305+ } ;
306+
292307const resolveSparseSpec = ( include ?: string [ ] ) => {
293308 const normalized = normalizeSparsePatterns ( include ) ;
294309 if ( normalized . length === 0 ) {
295310 return { enabled : false , mode : "cone" as const , patterns : [ ] as string [ ] } ;
296311 }
297- const hasDoubleStar = normalized . some ( ( pattern ) => pattern . includes ( "**" ) ) ;
298- const hasLiteral = normalized . some ( ( pattern ) => ! patternHasGlob ( pattern ) ) ;
299- if ( hasDoubleStar || hasLiteral ) {
300- return { enabled : true , mode : "no-cone" as const , patterns : normalized } ;
312+ const conePaths : string [ ] = [ ] ;
313+ let coneEligible = true ;
314+ for ( const pattern of normalized ) {
315+ if ( pattern . includes ( "**" ) ) {
316+ coneEligible = false ;
317+ break ;
318+ }
319+ if ( ! patternHasGlob ( pattern ) ) {
320+ if ( isDirectoryLiteral ( pattern ) ) {
321+ conePaths . push ( pattern . replace ( / \/ + $ / , "" ) ) ;
322+ continue ;
323+ }
324+ coneEligible = false ;
325+ break ;
326+ }
327+ const globIndex = pattern . search ( / [ * ? [ ] / ) ;
328+ const base = globIndex === - 1 ? pattern : pattern . slice ( 0 , globIndex ) ;
329+ const trimmed = base . replace ( / \/ + $ / , "" ) ;
330+ if ( ! trimmed ) {
331+ coneEligible = false ;
332+ break ;
333+ }
334+ conePaths . push ( trimmed ) ;
301335 }
302- const paths = normalized . map ( ( pattern ) => {
303- const starIndex = pattern . indexOf ( "*" ) ;
304- const base = starIndex === - 1 ? pattern : pattern . slice ( 0 , starIndex ) ;
305- return base . replace ( / \/ + $ | \/ $ / , "" ) ;
306- } ) ;
307- const uniquePaths = Array . from (
308- new Set ( paths . filter ( ( value ) => value . length > 0 ) ) ,
309- ) ;
310- if ( uniquePaths . length === 0 ) {
311- return { enabled : true , mode : "no-cone" as const , patterns : normalized } ;
336+ const uniquePaths = Array . from ( new Set ( conePaths . filter ( Boolean ) ) ) ;
337+ if ( coneEligible && uniquePaths . length > 0 ) {
338+ return { enabled : true , mode : "cone" as const , patterns : uniquePaths } ;
312339 }
313- return { enabled : true , mode : "cone" as const , patterns : uniquePaths } ;
340+ return {
341+ enabled : true ,
342+ mode : "no-cone" as const ,
343+ patterns : normalized . map ( toNoConePattern ) ,
344+ } ;
314345} ;
315346
316347const cloneRepo = async ( params : FetchParams , outDir : string ) => {
0 commit comments