Skip to content

Commit 1720532

Browse files
committed
Change findCommonParentDir to only allow absolute paths
1 parent d5ed25f commit 1720532

File tree

2 files changed

+72
-71
lines changed

2 files changed

+72
-71
lines changed

extensions/ql-vscode/src/common/files.ts

+31-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { pathExists, stat, readdir, opendir } from "fs-extra";
2-
import { isAbsolute, join, parse, relative, resolve, sep } from "path";
3-
import { homedir, tmpdir as osTmpdir } from "os";
2+
import { isAbsolute, join, relative, resolve, sep } from "path";
3+
import { tmpdir as osTmpdir, platform } from "os";
44

55
/**
66
* Recursively finds all .ql files in this set of Uris.
@@ -134,31 +134,53 @@ export function tmpdir(): string {
134134
}
135135

136136
/**
137-
* Finds the common parent directory of an arbitrary number of paths. The result
137+
* Finds the common parent directory of an arbitrary number of absolute paths. The result
138138
* will be an absolute path.
139139
* @param paths The array of paths.
140140
* @returns The common parent directory of the paths.
141141
*/
142142
export function findCommonParentDir(...paths: string[]): string {
143+
if (paths.length === 0) {
144+
throw new Error("At least one path must be provided");
145+
}
146+
if (paths.some((path) => !isAbsolute(path))) {
147+
throw new Error("All paths must be absolute");
148+
}
149+
143150
const normalizedPaths = paths.map((path) => normalizePath(path));
144151

145-
// Split each path into its components
146-
const pathParts = normalizedPaths.map((path) => path.split(sep));
152+
const pathParts = normalizedPaths.map((path) => getPathParts(path));
147153

148-
// Start with the root directory
149-
let commonDir = parse(homedir()).root;
154+
const commonParts = [];
150155

151156
// Iterate over the components of the first path and check if the same
152157
// component exists at the same position in all the other paths. If it does,
153158
// add the component to the common directory. If it doesn't, stop the
154159
// iteration and returns the common directory found so far.
155160
for (const [i, part] of pathParts[0].entries()) {
156161
if (pathParts.every((parts) => parts[i] === part)) {
157-
commonDir = join(commonDir, part);
162+
commonParts.push(part);
158163
} else {
159164
break;
160165
}
161166
}
162167

163-
return commonDir;
168+
const commonDir = join(...commonParts);
169+
return ensureAbsolutePath(commonDir);
170+
}
171+
172+
function getPathParts(path: string): string[] {
173+
// On Windows, keep the drive letter with the first part of the path
174+
if (platform() === "win32" && path.includes(":")) {
175+
const [driveLetter, ...restOfPath] = path.split(sep);
176+
restOfPath[0] = driveLetter + sep + restOfPath[0];
177+
return restOfPath;
178+
}
179+
180+
// On other platforms, just split the path normally
181+
return path.split(sep);
182+
}
183+
184+
function ensureAbsolutePath(path: string): string {
185+
return platform() === "win32" ? path : `${sep}${path}`;
164186
}

extensions/ql-vscode/test/unit-tests/common/files.test.ts

+41-62
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import type { DirResult } from "tmp";
1313
import { dirSync } from "tmp";
1414
import { ensureDirSync, symlinkSync, writeFileSync } from "fs-extra";
1515
import "../../matchers/toEqualPath";
16-
import { homedir } from "os";
1716

1817
describe("files", () => {
1918
const dataDir = join(__dirname, "../../data");
@@ -462,47 +461,39 @@ describe("walkDirectory", () => {
462461
});
463462

464463
describe("findCommonParentDir", () => {
465-
const dataDir = join(__dirname, "../../data");
466-
const rootDir = parse(homedir()).root;
467-
const sourceDir = join(dataDir, "../..");
464+
const rootDir = parse(process.cwd()).root;
468465

469-
it("should find the common parent dir for multiple relative paths with common parent", () => {
466+
it("should fail if not all paths are not absolute", async () => {
470467
const paths = [
471468
join("foo", "bar", "baz"),
472-
join("foo", "bar", "qux"),
473-
join("foo", "bar", "quux"),
469+
join("/foo", "bar", "qux"),
470+
join("/foo", "bar", "quux"),
474471
];
475472

476-
const commonDir = findCommonParentDir(...paths);
477-
478-
expect(commonDir).toEqualPath(join("foo", "bar"));
473+
expect(() => findCommonParentDir(...paths)).toThrow(
474+
"All paths must be absolute",
475+
);
479476
});
480477

481-
it("should return empty path if relative paths have no common parent", () => {
482-
const paths = [
483-
join("foo", "bar", "baz"),
484-
join("qux", "quux", "corge"),
485-
join("grault", "garply"),
486-
];
487-
488-
const commonDir = findCommonParentDir(...paths);
489-
490-
expect(commonDir).toEqualPath("");
478+
it("should fail if no path are provided", async () => {
479+
expect(() => findCommonParentDir()).toThrow(
480+
"At least one path must be provided",
481+
);
491482
});
492483

493-
it("should find the common parent dir for multiple absolute paths with common parent", () => {
484+
it("should find the common parent dir for multiple paths with common parent", () => {
494485
const paths = [
495-
join(dataDir, "foo", "bar", "baz"),
496-
join(dataDir, "foo", "bar", "qux"),
497-
join(dataDir, "foo", "bar", "quux"),
486+
join("/foo", "bar", "baz"),
487+
join("/foo", "bar", "qux"),
488+
join("/foo", "bar", "quux"),
498489
];
499490

500491
const commonDir = findCommonParentDir(...paths);
501492

502-
expect(commonDir).toEqualPath(join(dataDir, "foo", "bar"));
493+
expect(commonDir).toEqualPath(join("/foo", "bar"));
503494
});
504495

505-
it("should return the root if absolute paths have no common parent other than root", () => {
496+
it("should return empty path if paths have no common parent", () => {
506497
const paths = [
507498
join("/foo", "bar", "baz"),
508499
join("/qux", "quux", "corge"),
@@ -514,91 +505,79 @@ describe("findCommonParentDir", () => {
514505
expect(commonDir).toEqualPath(rootDir);
515506
});
516507

517-
it("should handle a mix of absolute and relative paths", async () => {
518-
const paths = [
519-
join(dataDir, "foo", "bar", "baz"),
520-
join("foo", "bar", "qux"),
521-
join(dataDir, "foo", "bar", "quux"),
522-
];
523-
524-
const commonDir = findCommonParentDir(...paths);
525-
526-
expect(commonDir).toEqualPath(sourceDir);
527-
});
528-
529508
it("should handle a mix of dirs and files", async () => {
530509
const paths = [
531-
join(dataDir, "foo", "bar", "baz"),
532-
join(dataDir, "foo", "bar", "qux.ql"),
533-
join(dataDir, "foo", "bar", "quux"),
510+
join("/foo", "bar", "baz"),
511+
join("/foo", "bar", "qux.ql"),
512+
join("/foo", "bar", "quux"),
534513
];
535514

536515
const commonDir = findCommonParentDir(...paths);
537516

538-
expect(commonDir).toEqualPath(join(dataDir, "foo", "bar"));
517+
expect(commonDir).toEqualPath(join("/foo", "bar"));
539518
});
540519

541520
it("should handle dirs that have the same name", async () => {
542521
const paths = [
543-
join("foo", "foo", "bar"),
544-
join("foo", "foo", "baz"),
545-
join("foo", "foo"),
522+
join("/foo", "foo", "bar"),
523+
join("/foo", "foo", "baz"),
524+
join("/foo", "foo"),
546525
];
547526

548527
const commonDir = findCommonParentDir(...paths);
549528

550-
expect(commonDir).toEqualPath(join("foo", "foo"));
529+
expect(commonDir).toEqualPath(join("/foo", "foo"));
551530
});
552531

553532
it("should handle dirs that have the same subdir structure but different base path", async () => {
554533
const paths = [
555-
join("foo", "bar"),
556-
join("bar", "foo", "bar"),
557-
join("foo", "foo", "bar"),
534+
join("/foo", "bar"),
535+
join("/bar", "foo", "bar"),
536+
join("/foo", "foo", "bar"),
558537
];
559538

560539
const commonDir = findCommonParentDir(...paths);
561540

562-
expect(commonDir).toEqualPath("");
541+
expect(commonDir).toEqualPath(rootDir);
563542
});
564543

565544
it("should handle a single path", async () => {
566-
const paths = [join("foo", "bar", "baz")];
545+
const paths = [join("/foo", "bar", "baz")];
567546

568547
const commonDir = findCommonParentDir(...paths);
569548

570-
expect(commonDir).toEqualPath(join("foo", "bar", "baz"));
549+
expect(commonDir).toEqualPath(join("/foo", "bar", "baz"));
571550
});
572551

573552
it("should return the same path if all paths are identical", () => {
574553
const paths = [
575-
join("foo", "bar", "baz"),
576-
join("foo", "bar", "baz"),
577-
join("foo", "bar", "baz"),
554+
join("/foo", "bar", "baz"),
555+
join("/foo", "bar", "baz"),
556+
join("/foo", "bar", "baz"),
578557
];
579558

580559
const commonDir = findCommonParentDir(...paths);
581560

582-
expect(commonDir).toEqualPath(join("foo", "bar", "baz"));
561+
expect(commonDir).toEqualPath(join("/foo", "bar", "baz"));
583562
});
584563

585564
it("should return the directory path if paths only differ by the file extension", () => {
586565
const paths = [
587-
join("foo", "bar", "baz.txt"),
588-
join("foo", "bar", "baz.jpg"),
589-
join("foo", "bar", "baz.pdf"),
566+
join("/foo", "bar", "baz.txt"),
567+
join("/foo", "bar", "baz.jpg"),
568+
join("/foo", "bar", "baz.pdf"),
590569
];
591570

592571
const commonDir = findCommonParentDir(...paths);
593572

594-
expect(commonDir).toEqualPath(join("foo", "bar"));
573+
expect(commonDir).toEqualPath(join("/foo", "bar"));
595574
});
596575

597576
it("should handle empty paths", () => {
598-
const paths = ["", "", ""];
577+
const paths = ["/", "/", "/"];
599578

600579
const commonDir = findCommonParentDir(...paths);
601580

602-
expect(commonDir).toEqualPath("");
581+
expect(commonDir).toEqualPath(rootDir);
603582
});
604583
});

0 commit comments

Comments
 (0)