opencode_offline/packages/opencode/src/util/filesystem.ts

94 lines
2.6 KiB
TypeScript
Raw Normal View History

import { realpathSync } from "fs"
2025-07-09 06:14:24 +08:00
import { dirname, join, relative } from "path"
export namespace Filesystem {
export const exists = (p: string) =>
Bun.file(p)
.stat()
.then(() => true)
.catch(() => false)
export const isDir = (p: string) =>
Bun.file(p)
.stat()
.then((s) => s.isDirectory())
.catch(() => false)
/**
* On Windows, normalize a path to its canonical casing using the filesystem.
* This is needed because Windows paths are case-insensitive but LSP servers
* may return paths with different casing than what we send them.
*/
export function normalizePath(p: string): string {
if (process.platform !== "win32") return p
try {
return realpathSync.native(p)
} catch {
return p
}
}
2025-07-09 06:14:24 +08:00
export function overlaps(a: string, b: string) {
const relA = relative(a, b)
const relB = relative(b, a)
return !relA || !relA.startsWith("..") || !relB || !relB.startsWith("..")
}
export function contains(parent: string, child: string) {
return !relative(parent, child).startsWith("..")
2025-07-09 06:14:24 +08:00
}
2025-06-04 03:57:38 +08:00
export async function findUp(target: string, start: string, stop?: string) {
2025-06-10 03:28:06 +08:00
let current = start
const result = []
while (true) {
2025-06-10 03:28:06 +08:00
const search = join(current, target)
if (await exists(search)) result.push(search)
2025-06-10 03:28:06 +08:00
if (stop === current) break
const parent = dirname(current)
if (parent === current) break
current = parent
}
2025-06-10 03:28:06 +08:00
return result
}
2025-07-03 11:27:04 +08:00
2025-07-10 21:37:28 +08:00
export async function* up(options: { targets: string[]; start: string; stop?: string }) {
const { targets, start, stop } = options
let current = start
while (true) {
for (const target of targets) {
const search = join(current, target)
if (await exists(search)) yield search
2025-07-10 21:37:28 +08:00
}
if (stop === current) break
const parent = dirname(current)
if (parent === current) break
current = parent
}
}
2025-07-03 11:27:04 +08:00
export async function globUp(pattern: string, start: string, stop?: string) {
let current = start
const result = []
while (true) {
try {
const glob = new Bun.Glob(pattern)
for await (const match of glob.scan({
cwd: current,
absolute: true,
2025-07-03 11:27:04 +08:00
onlyFiles: true,
2025-07-25 22:20:16 +08:00
followSymlinks: true,
2025-07-03 11:27:04 +08:00
dot: true,
})) {
result.push(match)
2025-07-03 11:27:04 +08:00
}
} catch {
// Skip invalid glob patterns
}
if (stop === current) break
const parent = dirname(current)
if (parent === current) break
current = parent
}
return result
}
}