diff --git a/packages/pi-fff/src/aux-finders.ts b/packages/pi-fff/src/aux-finders.ts index 8bec5b75b..6b1fd4713 100644 --- a/packages/pi-fff/src/aux-finders.ts +++ b/packages/pi-fff/src/aux-finders.ts @@ -128,6 +128,14 @@ export function resolveAuxRoot( return null; } +export function isOutsideWorkspaceRelativePath(relativePath: string): boolean { + return ( + path.isAbsolute(relativePath) || + relativePath === ".." || + relativePath.startsWith(`..${path.sep}`) + ); +} + // Decide whether a `path` parameter should route to the workspace finder or // to an aux finder. Accepts absolute paths, `~`-prefixed paths, and relative // paths escaping the workspace (`../other-project`); everything is resolved @@ -147,7 +155,7 @@ export function routePathConstraint( candidate = path.resolve(cwd, candidate); } const rel = path.relative(cwd, candidate); - if (rel !== ".." && !rel.startsWith(`..${path.sep}`)) return null; + if (!isOutsideWorkspaceRelativePath(rel)) return null; return resolveAuxRoot(candidate); } diff --git a/packages/pi-fff/test/aux-finders.test.ts b/packages/pi-fff/test/aux-finders.test.ts index 0b829bf92..7237aa030 100644 --- a/packages/pi-fff/test/aux-finders.test.ts +++ b/packages/pi-fff/test/aux-finders.test.ts @@ -3,6 +3,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { + isOutsideWorkspaceRelativePath, resolveAuxRoot, rootCovers, routePathConstraint, @@ -28,6 +29,14 @@ describe("routePathConstraint", () => { expect(route).toEqual({ root: "/tmp", suffix: "" }); }); + if (process.platform === "win32") { + test("treats a cross-volume relative result as outside the workspace", () => { + const rel = path.win32.relative("D:\\workspace", "C:\\target"); + expect(rel).toBe("C:\\target"); + expect(isOutsideWorkspaceRelativePath(rel)).toBe(true); + }); + } + test("splits glob suffix from existing dir prefix", () => { const route = routePathConstraint("/tmp/**/*.ts", cwd); expect(route).toEqual({ root: "/tmp", suffix: "**/*.ts" });