@@ -159,13 +159,12 @@ function pathsepsToPosix(value: string, isWindows: boolean): string {
159159}
160160
161161/**
162- * A Windows drive at the root of a path: the `C:` in `C:/tmp/x`, or `C:` alone .
162+ * A Windows drive at the root of an absolute path: the `C:` in `C:/tmp/x`.
163163 *
164- * The lookahead is what limits this to a drive *root*. Windows also reads `C:tmp`
165- * as naming a drive, but relative to the current directory on it, which is not a
166- * place this module can resolve.
164+ * Require a separator after the colon. Both `C:` and `C:tmp` are relative to
165+ * the current directory on that drive, which this module cannot safely infer.
167166 */
168- const DRIVE_PREFIX = / ^ ( [ a - z A - Z ] ) : (? = \/ | $ ) /
167+ const DRIVE_PREFIX = / ^ ( [ a - z A - Z ] ) : (? = \/ ) /
169168
170169/**
171170 * The Windows volume in matcher notation: /C for a drive, /UNC/server/share
@@ -201,6 +200,11 @@ function isAbsolutePath(value: string, isWindows: boolean): boolean {
201200 return value . startsWith ( "/" ) || ( isWindows && DRIVE_PREFIX . test ( value ) )
202201}
203202
203+ /** Windows drive-relative paths depend on per-drive process state, not the task workspace. */
204+ function isDriveRelativePath ( value : string , isWindows : boolean ) : boolean {
205+ return isWindows && / ^ [ a - z A - Z ] : (? ! \/ ) / . test ( value )
206+ }
207+
204208function escapesWorkspace ( posixPath : string ) : boolean {
205209 return posixPath . split ( "/" ) . includes ( ".." )
206210}
@@ -309,7 +313,13 @@ function toMatcherPatternWithStrategy(
309313 // as gitignore's escape character and a legal filename character.
310314 let normalized = pathsepsToPosix ( pattern . slice ( negation . length ) , isWindows )
311315
312- if ( ! normalized . trim ( ) || normalized === "." || normalized === "~" || normalized . endsWith ( "/" ) ) {
316+ if (
317+ ! normalized . trim ( ) ||
318+ isDriveRelativePath ( normalized , isWindows ) ||
319+ normalized === "." ||
320+ normalized === "~" ||
321+ normalized . endsWith ( "/" )
322+ ) {
313323 return undefined
314324 }
315325
@@ -328,8 +338,8 @@ function toMatcherPatternWithStrategy(
328338 return `${ negation } ${ strategy . toMatcherAbsolutePath ( normalized , workspaceVolume ) } `
329339 }
330340
331- if ( ! cwd ) {
332- // No workspace for a relative pattern to be relative to ; see the note above
341+ if ( ! cwd || isDriveRelativePath ( workspacePosix , isWindows ) ) {
342+ // An unknown or drive- relative workspace cannot anchor a relative pattern ; see the note above
333343 // on why this is not matched loosely instead.
334344 return undefined
335345 }
@@ -378,7 +388,7 @@ function toMatcherPath(
378388 // Not trimmed: whitespace can be part of a filename.
379389 const normalized = pathsepsToPosix ( filePath , isWindows )
380390
381- if ( ! normalized . trim ( ) || normalized === "." ) {
391+ if ( ! normalized . trim ( ) || isDriveRelativePath ( normalized , isWindows ) || normalized === "." ) {
382392 return undefined
383393 }
384394
@@ -390,8 +400,8 @@ function toMatcherPath(
390400 return toRootRelativePath ( strategy . toMatcherAbsolutePath ( normalized , workspaceVolume ) )
391401 }
392402
393- if ( ! cwd ) {
394- // A relative path cannot be placed on the filesystem without a root, and
403+ if ( ! cwd || isDriveRelativePath ( workspacePosix , isWindows ) ) {
404+ // A relative path cannot be placed without a known, non-drive-relative root, and
395405 // the only patterns that survive without one are absolute, which such a
396406 // path could never match anyway.
397407 return undefined
0 commit comments