@@ -121,14 +121,26 @@ export class DockerIgnoreMatcher implements IgnoreMatcher {
121121 private patterns : CompiledPattern [ ] ;
122122
123123 constructor ( patterns : string [ ] ) {
124- this . patterns = patterns . map ( ( raw ) => new CompiledPattern ( raw ) ) ;
124+ this . patterns = patterns . map ( ( raw ) => {
125+ // Normalize pattern immediately upon construction
126+ let cleaned = raw ;
127+ if ( cleaned . startsWith ( '/' ) ) cleaned = cleaned . slice ( 1 ) ;
128+ if ( cleaned . startsWith ( './' ) ) cleaned = cleaned . slice ( 2 ) ;
129+ if ( cleaned . endsWith ( '/' ) && cleaned !== '/' ) cleaned = cleaned . slice ( 0 , - 1 ) ;
130+ return new CompiledPattern ( cleaned , raw ) ;
131+ } ) ;
125132 }
126133
127134 matches ( filePath : string ) : boolean {
128135 // Expect paths relative to context root, forward-slash separated
129136 let normalized = filePath . replace ( / \\ / g, '/' ) ;
130137
138+ // Strip leading ./ if present
131139 if ( normalized . startsWith ( './' ) ) normalized = normalized . slice ( 2 ) ;
140+
141+ // Strip trailing slash if present (unless it's just root)
142+ if ( normalized . endsWith ( '/' ) && normalized !== '/' ) normalized = normalized . slice ( 0 , - 1 ) ;
143+
132144 if ( ! normalized ) normalized = '.' ;
133145
134146 const parts = normalized . split ( '/' ) ;
@@ -172,14 +184,13 @@ class CompiledPattern {
172184 private readonly cleaned : string ;
173185 private readonly regexp : RegExp ;
174186
175- constructor ( pattern : string ) {
187+ constructor ( pattern : string , raw ?: string ) {
188+ this . raw = raw || pattern ;
176189 if ( pattern . startsWith ( '!' ) ) {
177190 this . exclusion = true ;
178- this . raw = pattern ;
179191 this . cleaned = pattern . slice ( 1 ) ;
180192 } else {
181193 this . exclusion = false ;
182- this . raw = pattern ;
183194 this . cleaned = pattern ;
184195 }
185196
0 commit comments