@thingts/fs-path - v2.0.0
    Preparing search index...

    Class FsPath

    Represents an absolute filesystem path (i.e. a path starting at the root with a leading separator), and provides methods for path resolution, manipulation and queries as well as (async) filesystem operations.

    FsPath instances are normalized and immutable.

    FsPath extends AbsolutePath of @thingts/path, but with added methods for filesystem access.

    Having an FsPath instance does not imply that a file or directory exists at that path in the filesystem; use exists(), isFile(), isDirectory() or stat() to check for existence, and use write(), touch(), makeDirectory(), or makeTempDirectory() to create files or directories.

    Methods that create or modify files or directories return a Promise<this>, allowing for chaining.

    FsPath also supports creating temporary files or directories; see disposable()

    ⚠️ In the documentation below that is inherited from AbsolutePath, examples that refer to AbsolutePath apply equally to FsPath -- in particular, the path manipulation methods like join() return FsPath instances, not AbsolutePath instances.

    const p = new FsPath('/path/to/file.txt')
    await p.write('Hello, world!', { makeParents: true })
    const content = await p.read() // Reads the file content

    // With chaining
    const p2 = await new FsPath('/path/to/another.txt').write('data')

    // Multiple chaining is possible, though less ergonomic
    const p3 = await new FsPath('/path/to/yet-another.txt').write('data').then(p => p.setPermissions({ mode: 0o644 }))

    const p4 = await (await new FsPath('/path/to/yet-another.txt').write('data')).setPermissions({ mode: 0o644 })

    Hierarchy

    Index

    Constructors

    Accessors

    • get extension(): string

      Returns the extension of the filename including the leading dot, as a string. If the filename has no extension, returns an empty string.

      Note that if the filename starts with a dot (e.g. .gitignore), that dot is considered part of the stem. So .gitignore has extension '' and .gitignore.bak has extension .bak

      Returns string

    • get filename(): Filename

      The filename component (last path segment) as a Filename.

      Returns Filename

      new AbsolutePath('/a/b/c.txt').filename // Filename('c.txt')
      new RelativePath('a/b/c.txt').filename // Filename('c.txt')
    • get parent(): this

      The parent directory of this path.

      Returns this

      A new instance pointing to the parent.

      new AbsolutePath('/a/b/c.txt').parent // AbsolutePath('/a/b')
      new RelativePath('a/b/c.txt').parent // RelativePath('a/b')
    • get segments(): string[]

      The segments of this path as an array of strings.

      Returns string[]

      new AbsolutePath('/a/b/c.txt').segments // ['a', 'b', 'c.txt']
      new RelativePath('a/b/c.txt').segments // ['a', 'b', 'c.txt']
    • get stem(): string

      Returns the stem of the filename, i.e. the part before the extension. If the filename has no extension, returns the entire filename

      Note that if the filename starts with a dot (e.g. .gitignore), that dot is considered part of the stem. So .gitignore and .gitignore.bak both have stem .gitignore

      Returns string

    Methods

    • Checks the accessibility of the path with the specified mode(s).

      Note that a successful access check does not guarantee that a subsequent operation will succeed, since the filesystem state may change in between.

      Parameters

      • modes: FsFileModeSpec

        The access mode(s) to check. Can be a FsFileMode string, an array of FsFileMode strings, or an FsPermissionFlags object specifying read/write/execute booleans. An empty spec ([] or {}) checks for existence.

      • Optionalopts: { throw?: boolean }
        • Optionalthrow?: boolean

          If true, rethrows the underlying filesystem error if the path is not accessible with the specified mode(s). (Default: false)

      Returns Promise<boolean>

      A Promise resolving to true if the path is accessible with the specified mode(s), or false if it is not (and opts.throw is false).

      const p = new FsPath('/path/to/file')
      const canRead = await p.access('read')
      const canReadWrite = await p.access(['read', 'write'])
      const canExecute = await p.access({ execute: true })
    • Copies this file or directory to a new location.

      By default:

      • Files are copied and will overwrite an existing file at the destination.
      • Directories require recursive: true; otherwise an error is thrown.
      • When copying directories recursively:
        • If the destination does not exist, it is created.
        • If the destination exists, contents are merged.
        • Existing files are overwritten unless overwrite: false is specified.

      Parameters

      • to: AbsolutePath

        The target path to copy to.

      • Optionalopts: {
            intoDir?: boolean;
            makeParents?: boolean;
            overwrite?: boolean;
            recursive?: boolean;
        }
        • OptionalintoDir?: boolean

          If true, treat to as a directory and copy this path into it (i.e. to.join(this.filename)). (Default: false)

        • OptionalmakeParents?: boolean

          If true, create parent directories of the target path as needed. (Default: false)

        • Optionaloverwrite?: boolean

          If false, do not overwrite existing files; instead throw an error if a destination path already exists. (Default: true)

        • Optionalrecursive?: boolean

          If true, allow copying directories recursively. Required when the source is a directory. (Default: false)

      Returns Promise<FsPath>

      A Promise resolving to a new FsPath for the path of the copy (either to or to.join(this.filename) based on opts.intoDir)

      const src = new FsPath('/path/to/file.txt')
      const dest = new FsPath('/new/path/to/file.txt')

      await src.copyTo(dest) // overwrites dest if it exists

      const dir = new FsPath('/new/path/')
      await src.copyTo(dir, { intoDir: true }) // copies to /new/path/file.txt

      const srcDir = new FsPath('/path/to/dir')
      await srcDir.copyTo('/backup/dir', { recursive: true }) // copies entire directory tree

      await src.copyTo(dest, { overwrite: false }) // throws if dest exists
    • Test whether this path is a descendant of the given ancestor path.

      Parameters

      • ancestor: string | AbsolutePath

        An AbsolutePath or string to check against.

      • Optionalopts: { includeSelf?: boolean }
        • OptionalincludeSelf?: boolean

          If true, return true when the paths are identical.

      Returns boolean

      True if this path descends from the ancestor, otherwise false.

      const p1 = new AbsolutePath('/project/demo')
      const p2 = new AbsolutePath('/project/demo/src/index.ts')
      console.log(p2.descendsFrom(p1)) // true
      console.log(p1.descendsFrom(p1)) // false
      console.log(p1.descendsFrom(p1, { includeSelf: true })) // true
    • Create an FsPath instance marked as as disposable, meaning the file or directory will be automatically deleted when no longer needed.

      Disposable paths will be disposed (i.e. the file or directory will be deleted) in one of three circumstances, whichever comes first:

      • Via the using declaration -- the path is disposed as soon as the declared variable goes out of scope. This is the most immediate and reliable.

      • Disposal when the FsPath object is garbage collected. As always, there's no knowing when or if this will happen.

      • Disposal on process exit.

      ⚠️ Disposal is best-effort; it may fail due to permissions or be skipped if the process exits abnormally. Failures during disposal are silently ignored.

      Returns this

      A new instance of this path, marked as disposable.

      {
      using p = new FsPath('/path/to/tempfile.txt').disposable()
      await p.write('data') // Creates the file
      ...
      } // The file is automatically removed when p goes out of scope

      const p2 = new FsPath('/path/to/tempfile2.txt').disposable()
      await p2.write('data')
      // The file will be removed eventually, on gc or process exit
    • Returns true if this path equals the other path or path string.

      Parameters

      Returns boolean

    • Finds files and directories matching a glob pattern within this directory.

      Uses tinyglobby under the hood.

      Parameters

      • pattern: string | Filename

        The glob pattern to match

      • Optionalopts: FsReadDirectoryOptions

        Options for reading directories, used by readDirectory() and glob().

        • OptionalallowMissing?: boolean

          If true, return an empty array if the directory does not exist, rather than throwing an error. (Default: false)

        • OptionalincludeDotfiles?: boolean

          If true, include dotfiles in the result. (Default: true)

        • OptionalonlyDirs?: boolean

          If true, only include directories in the result. (Default: false)

        • OptionalonlyFiles?: boolean

          If true, only include files in the result. (Default: false)

      Returns Promise<FsPath[]>

      An array of FsPath objects representing the matching entries.

      const dir = new FsPath('/path/to/dir')
      const paths = await dir.glob('**/*.js')
      for (const path of paths) {
      console.log(path.filename.toString())
      }
    • Join additional path segments to this path.

      Accepts relative path objects and Filename segment arguments for type safety, but you can also directly pass strings for convenience. All args are stringified and interpreted as segments to be joined, regardless of whether they start with a path separator.

      Any null, undefined, or empty segments are ignored.

      Parameters

      Returns this

      A new instance with the segments appended and resulting path normalized.

      const a1 = new AbsolutePath('/project/demo')
      const a2 = a1.join('demo1/src', 'index.js') // → AbsolutePath('/project/demo/demo1/src/index.js')

      const r1 = new RelativePath('demo')
      const r2 = r1.join('demo1/src', 'index.js') // → RelativePath('demo/demo1/src/index.js')
    • Create a directory at this path

      By default, if the directory already exists, this succeeds as a no-op (that is, the operation is idempotent).

      Parameters

      • Optionalopts: { makeParents?: boolean; throwIfExists?: boolean }
        • OptionalmakeParents?: boolean

          If true, create parent directories as needed.

        • OptionalthrowIfExists?: boolean

          If true, throw an error if the final directory already exists. (Default: false)

      Returns Promise<FsPath>

      A Promise resolving to this instance, for chaining.

      const p = new FsPath('/path/to/dir')
      await p.makeDirectory({ makeParents: true }) // Ensures the directory and any needed parent directories exist
      await p.makeDirectory({ throwIfExists: true }) // Throws if the directory already exists
    • Move (rename) this file or directory to a new location.

      Parameters

      • to: AbsolutePath

        The target path to move to.

      • Optionalopts: { intoDir?: boolean; makeParents?: boolean; overwrite?: boolean }
        • OptionalintoDir?: boolean

          If true, treat to as a directory and move the file into it. (Default: false)

        • OptionalmakeParents?: boolean

          If true, create parent directories of the target path as needed. (Default: false)

        • Optionaloverwrite?: boolean

          If false, do not overwrite an existing file; instead throw an error if the final destination path already exists. (Default: true)

      Returns Promise<FsPath>

      A Promise resolving to a new FsPath for the final path (either to or to.join(this.filename) based on opts.intoDir)

      const p1 = new FsPath('/path/to/file.txt')
      const p2 = new FsPath('/new/path/to/file.txt')
      await p1.moveTo(p2, { makeParents: true }) // Renames (moves) the file, creating parent directories as needed

      const dir = new FsPath('/new/path/')
      const p3 = await p2.moveTo(dir, { intoDir: true, makeParents: true }) // Moves the file into the directory
      console.log(p3.toString()) // /new/path/file.txt
    • Read the contents of the file as a string.

      Parameters

      • Optionalopts: { encoding?: BufferEncoding }
        • Optionalencoding?: BufferEncoding

          The file encoding. (Default: 'utf8')

      Returns Promise<string>

      The file contents as a string.

      const p = new FsPath('/path/to/file.txt')
      const content = await p.read()
    • Reads the contents of the directory.

      Parameters

      • Optionalopts: FsReadDirectoryOptions

        Options for reading directories, used by readDirectory() and glob().

        • OptionalallowMissing?: boolean

          If true, return an empty array if the directory does not exist, rather than throwing an error. (Default: false)

        • OptionalincludeDotfiles?: boolean

          If true, include dotfiles in the result. (Default: true)

        • OptionalonlyDirs?: boolean

          If true, only include directories in the result. (Default: false)

        • OptionalonlyFiles?: boolean

          If true, only include files in the result. (Default: false)

      Returns Promise<FsPath[]>

      An array of FsPath objects representing the entries in the directory.

      const dir = new FsPath('/path/to/dir')
      const paths = await dir.readDirectory()
      for (const path of paths) {
      console.log(path.filename.toString())
      }
    • Creates a readable stream for the file.

      Parameters

      • Optionalopts: { end?: number; start?: number }
        • Optionalend?: number

          The ending byte position (inclusive).

        • Optionalstart?: number

          The starting byte position (inclusive).

      Returns Promise<ReadStream>

      A readable stream for the file.

      const p = new FsPath('/path/to/file.txt')
      const stream = await p.readStream()
      stream.pipe(process.stdout) // Print file contents to stdout
    • Resolves this path to its canonical filesystem path.

      This resolves symbolic links and returns the absolute canonical path reported by the filesystem.

      The path must exist; otherwise the underlying filesystem error is thrown.

      Returns Promise<FsPath>

      A Promise resolving to a new FsPath.

    • Compute the relative path from the given base path to this path.

      Parameters

      Returns RelativePath

      A RelativePath that goes from base to this.

      const p1 = new AbsolutePath('/project/demo')
      const p2 = new AbsolutePath('/project/demo/src/index.ts')
      const rel = p2.relativeTo(p1) // 'src/index.ts' (RelativePath)
      p1.join(rel).equals(p2) // true
    • Removes the file or directory.

      Parameters

      • Optionalopts: { throwIfMissing?: boolean }
        • OptionalthrowIfMissing?: boolean

          If true, throw an error if the path does not exist. (Default: false)

      Returns Promise<FsPath>

      A Promise resolving to this instance, for chaining. (The path object remains valid even though the file or directory has been removed.)

      const p = new FsPath('/path/to/file.txt')
      await p.remove() // Removes the file if it exists, does nothing if it does not exist
      await p.remove({ throwIfMissing: true }) // Removes the file, throws an error if it does not exist
    • Parameters

      • newExt: string

      Returns this

    • Replace the filename (last segment).

      Parameters

      Returns this

      A new instance with the filename replaced.

      new AbsolutePath('/a/b/c.txt').replaceFilename('d.json') // → AbsolutePath('/a/b/d.json')
      new RelativePath('a/b/c.txt').replaceFilename('d.json') // → RelativePath('a/b/d.json')
    • Replace the entire directory path through the parent, while keeping the current filename.

      Parameters

      • newParent: string | FsPath

        Parent directory as string or another path

      Returns this

      A new path rooted at newParent with the same filename.

      new AbsolutePath('/old/file.txt').replaceParent('/new/dir') // '/new/dir/file.txt' (AbsolutePath)
      new RelativePath('old/file.txt').replaceParent('new/dir') // 'new/dir/file.txt' (RelativePath)
    • Parameters

      • newStem: string

      Returns this

    • Cancels disposal for this instance.

      If this instance was created via disposable(), so that file or directory would be automatically removed, calling retain() prevents that removal.

      Returns this

      A new instance of this path, unmarked as disposable.

      ⚠️ The old instance remains valid and can still be used normally. Technically the old instance continues to to be marked as disposable, but its disposal will now be a no-op.

      {
      using p = new FsPath('/path/to/tempfile.txt').disposable()
      await p.write('data') // Creates the file
      ...
      p.retain() // The file will *not* be removed when p goes out of scope
      }

      const p2 = new FsPath('/path/to/tempfile2.txt').disposable()
      await p2.write('data')
      p2.retain() // the file will *not* be automatically removed
    • Sets the permission mode of the path.

      Parameters

      • spec: FsPermissionSpec

        The permission specification. Can be the numeric mode, or an object specifying user/group/others permissions; each category can be specified with a FsFileMode string, an array of FsFileMode strings, or an FsPermissionFlags object specifying read/write/execute booleans.

      • Optionalopts: { operation?: "clear" | "overlay" | "assign" }
        • Optionaloperation?: "clear" | "overlay" | "assign"

          The operation to perform: assign the permissions exactly (default), overlay the permissions on top of the existing ones, or clear the specified permissions from the existing ones.

      Returns Promise<FsPath>

      A Promise resolving to this instance, for chaining.

      const p = new FsPath('/path/to/file')

      // Set to rw-r--r--
      await p.setPermissions({ mode: 0o644 })

      // Add write permission for all => rw-rw-rw-
      await p.setPermissions({ all: ['read', 'write'] }, { operation: 'overlay' })

      // Remove write permission for others => rw-rw-r--
      await p.setPermissions({ others: { write: true } }, { operation: 'clear' })
    • Returns string

      Returns the string representation of this path.

    • Updates the file's access and modification times, or create an empty file if it does not exist.

      Parameters

      • Optionalopts: { makeParents?: boolean }
        • OptionalmakeParents?: boolean

          If true, create parent directories as needed. (Default: false)

      Returns Promise<FsPath>

      A Promise resolving to this instance, for chaining.

      const p = await new FsPath('/path/to/file.txt')
      await p.touch({ makeParents: true }) // Creates the file if it does not exist, creating parent directories as needed
    • Transform the filename via a callback.

      Parameters

      Returns this

      A new instance with the transformed filename.

      p.transformFilename(f => String(f).toUpperCase())
      
    • Writes content to the file, replacing or appending to the existing content.

      Parameters

      • content: string | Uint8Array<ArrayBufferLike>

        The content to write (string or Uint8Array).

      • Optionalopts: { append?: boolean; encoding?: BufferEncoding; makeParents?: boolean }
        • Optionalappend?: boolean

          If true, append to the file instead of replacing it. (Default: false)

        • Optionalencoding?: BufferEncoding

          The file encoding. (Default: 'utf8' if content is string)

        • OptionalmakeParents?: boolean

          If true, create parent directories as needed. (Default: false)

      Returns Promise<FsPath>

      A Promise resolving to this instance, for chaining.

      const p = new FsPath('/path/to/file.txt')
      await p.write('Hello', { makeParents: true }) // Creates the file with content, creating parent directories as needed
      await p.write(', world!', { append: true }) // Appends to the file
    • Creates a new temporary directory

      By default, the directory is created under the system temporary directory (see os.tmpdir)

      The resulting directory name will begin with the given prefix (default: 'temp-') followed by a unique suffix, as per fs.mkdtemp.

      The returned FsPath is marked as disposable, meaning it will be automatically removed when no longer needed (e.g. when used with a using declaration, on garbage collection, or on process exit).

      Parameters

      • Optionalopts: { makeParents?: boolean; parent?: AbsolutePath; prefix?: string }
        • OptionalmakeParents?: boolean

          If true, create the parent directory if it does not exist. (Default: false)

        • Optionalparent?: AbsolutePath

          The parent directory in which to create the temporary directory. Defaults to the system temporary directory.

        • Optionalprefix?: string

          Prefix for the generated directory name. (Default: 'temp-'`)

      Returns Promise<FsPath>

      A Promise resolving to a new FsPath representing the temporary directory, marked as disposable.

      // Default: create under system temp directory
      const dir = await FsPath.makeTempDirectory()

      // With custom prefix
      const dir2 = await FsPath.makeTempDirectory({ prefix: 'build-' })

      // Under a custom parent directory
      const dir3 = await FsPath.makeTempDirectory({
      parent: '/projects/demo/.tmp',
      prefix: 'build-',
      makeParents: true
      })