import { fromFileUrl } from "./path.ts";
import { resolve, SEP as SEPARATOR } from "./path.ts";
import { copySync } from "fs/copy";
import { existsSync } from "fs/exists";
import { originalRealPathSync } from "./original-real-path.ts";
export { ensureDir, ensureDirSync } from "fs/ensure-dir";
export { existsSync } from "fs/exists";
export { walk, walkSync } from "fs/walk";
export { expandGlob, expandGlobSync } from "fs/expand-glob";
export type { ExpandGlobOptions } from "fs/expand-glob";
export { EOL, format, LF } from "fs/eol";
export { copy, copySync } from "fs/copy";
export type { CopyOptions } from "fs/copy";
export { moveSync } from "fs/move";
export { emptyDirSync } from "fs/empty-dir";
export type { WalkEntry } from "fs/walk";
export type PathType = "file" | "dir" | "symlink";
export function getFileInfoType(fileInfo: Deno.FileInfo): PathType | undefined {
return fileInfo.isFile
? "file"
: fileInfo.isDirectory
? "dir"
: fileInfo.isSymlink
? "symlink"
: undefined;
}
export function isSubdir(
path1: string | URL,
path2: string | URL,
sep = SEPARATOR,
): boolean {
path1 = toPathString(path1);
path2 = toPathString(path2);
path1 = resolve(path1);
path2 = resolve(path2);
if (path1 === path2) {
return false;
}
const path1Array = path1.split(sep);
const path2Array = path2.split(sep);
return path1Array.every((current, i) => path2Array[i] === current);
}
export function toPathString(
pathUrl: string | URL,
): string {
return pathUrl instanceof URL ? fromFileUrl(pathUrl) : pathUrl;
}
export function safeMoveSync(
src: string,
dest: string,
): void {
try {
Deno.renameSync(src, dest);
} catch (err: any) {
if (err.code !== "EXDEV") {
throw err;
}
copySync(src, dest, { overwrite: true });
safeRemoveSync(src, { recursive: true });
}
}
export function safeRemoveSync(
file: string,
options: Deno.RemoveOptions = {},
) {
try {
Deno.removeSync(file, options);
} catch (e) {
if (existsSync(file)) {
throw e;
}
}
}
export class UnsafeRemovalError extends Error {
constructor(msg: string) {
super(msg);
}
}
export function safeRemoveDirSync(
path: string,
boundary: string,
) {
let resolvedPath = path;
let resolvedBoundary = boundary;
try {
resolvedPath = originalRealPathSync(path);
resolvedBoundary = originalRealPathSync(boundary);
} catch {
}
if (resolvedPath === resolvedBoundary || !isSubdir(resolvedBoundary, resolvedPath)) {
throw new UnsafeRemovalError(
`Refusing to remove directory ${path} that isn't a subdirectory of ${boundary}`,
);
}
return safeRemoveSync(path, { recursive: true });
}
export function safeModeFromFile(path: string): number | undefined {
if (Deno.build.os !== "windows") {
const stat = Deno.statSync(path);
if (stat.mode !== null) {
return stat.mode;
}
}
}