All files / toolbox / filesystem.ts

100.00% Branches 9/9
100.00% Lines 26/26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
 
x1
 
 
 
x1
x2
x2
 
 
x1
x39
x39
x39
x58
x58
x57
x57
x57
x39
 
x40
x40
x39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x1
x7
x7
x7
x35
x7
x98
x7
x7













































// Copyright (c) - 2025+ the lowlighter/esquie authors. AGPL-3.0-or-later
import type { Nullable } from "@libs/typing"
import { expandGlob } from "@std/fs"
export type { Nullable }

/** Gets the current working directory. */
export function cwd(): string {
  return Deno.cwd().replaceAll("\\", "/")
}

/** Gets the file type of a file path. */
export function filetype(path: string): Nullable<"file" | "directory"> {
  try {
    const lstat = Deno.lstatSync(path)
    if (lstat.isFile) {
      return "file"
    }
    if (lstat.isDirectory) {
      return "directory"
    }
  } catch {
    // Ignore errors
  }
  return null
}

/** Options for {@linkcode list()}. */
export type ListOptions = {
  /** Root directory to search in. Defaults to the current working directory. */
  root?: string
  /** Whether to return relative paths. */
  relative?: boolean
  /** Whether to include files in the results. */
  files?: boolean
  /** Whether to include directories in the results. */
  directories?: boolean
}

/** Lists all files matching the given glob pattern in the specified root directory. */
export async function list(glob: string, { root = cwd(), relative = true, files = false, directories = false }: ListOptions = {}): Promise<string[]> {
  if (!root.endsWith("/")) {
    root += "/"
  }
  const entries = await Array.fromAsync(expandGlob(glob, { root, canonicalize: true, includeDirs: directories }))
  return entries
    .filter(({ path }) => [files ? "file" : "", directories ? "directory" : ""].filter(Boolean).includes(filetype(path) as string))
    .map(({ path }) => path.replaceAll("\\", "/").replace(root, relative ? "" : root))
}