Skip to content

flex-development/fst-util-from-fs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

148 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

fst-util-from-fs

github release npm npm downloads install size codecov module type: esm license conventional commits typescript vitest yarn

fst utility to create trees from file systems

Contents

What is this?

This package is a utility to create file system trees.

This utility uses file system adapters to recursively read a directory, and create a tree from its contents.

Install

This package is ESM only.

In Node.js (version 20+) with yarn:

yarn add @flex-development/fst-util-from-fs
See Git - Protocols | Yarn Β for details regarding installing from Git.

In Deno with esm.sh:

import { fromFileSystem } from 'https://esm.sh/@flex-development/fst-util-from-fs'

In browsers with esm.sh:

<script type="module">
  import { fromFileSystem } from 'https://esm.sh/@flex-development/fst-util-from-fs'
</script>

Use

import type {
  File,
  AnyParent as Parent,
  Root
} from '@flex-development/fst'
import {
  fromFileSystem,
  type Dirent,
  type FileSystem,
  type Stats
} from '@flex-development/fst-util-from-fs'
import pathe from '@flex-development/pathe'
import { inspect } from '@flex-development/unist-util-inspect'
import { ok } from 'devlop'
import fs from 'node:fs'
import { size } from 'unist-util-size'

declare module '@flex-development/fst' {
  interface File {
    /**
     * The size of the file.
     */
    size?: bigint | number | undefined
  }
}

declare module '@flex-development/fst-util-from-fs' {
  interface Stats {
    /**
     * The size of the entry.
     */
    size: bigint | number
  }
}

/**
 * A glob pattern matching directories to search
 * and files to include in the tree.
 *
 * @const {string} pattern
 */
const pattern: string = 'src/**/**'

/**
 * The file system tree.
 *
 * @const {Root} tree
 */
const tree: Root = await fromFileSystem({
  extensions: '.mts',
  filters: {
    /**
     * @this {void}
     *
     * @param {string} path
     *  The path to the directory, relative to `tree.path`
     * @param {number | null | undefined} depth
     *  The current search depth
     * @param {Dirent} dirent
     *  The dirent representing the file system entry
     * @param {Parent} parent
     *  The parent node
     * @param {Root} tree
     *  The file system tree
     * @return {boolean}
     *  `true` if node for `path` should be added, `false` otherwise
     */
    directory(
      this: void,
      path: string,
      depth: number | null | undefined,
      dirent: Dirent,
      parent: Parent,
      tree: Root
    ): boolean {
      return pathe.matchesGlob(path, pattern, {
        cwd: tree.path,
        dot: false,
        ignore: ['**/__mocks__/**', '**/__snapshots__/**', '**/__tests__/**'],
        noglobstar: false
      })
    },

    /**
     * @this {void}
     *
     * @param {string} path
     *  The path to the file, relative to `tree.path`
     * @param {number | null | undefined} depth
     *  The current search depth
     * @param {Dirent} dirent
     *  The dirent representing the file system entry
     * @param {Parent} parent
     *  The parent node
     * @param {Root} tree
     *  The file system tree
     * @return {boolean}
     *  `true` if node for `path` should be added, `false` otherwise
     */
    file(
      this: void,
      path: string,
      depth: number | null | undefined,
      dirent: Dirent,
      parent: Parent,
      tree: Root
    ): boolean {
      return pathe.matchesGlob(path, pattern, {
        cwd: tree.path,
        dot: true,
        ignore: ['**/.DS_Store'],
        noglobstar: false
      })
    }
  },
  fs: fs.promises,
  handles: {
    /**
     * @async
     *
     * @this {void}
     *
     * @param {File} node
     *  The node representing the file
     * @param {Dirent} dirent
     *  The dirent representing the file
     * @param {Parent} parent
     *  The parent of `node`
     * @param {Root} tree
     *  The current file system tree
     * @param {Parent[]} ancestors
     *  The ancestors of `node`, with the last node being `parent`
     * @param {FileSystem} fs
     *  The file system API
     * @return {Promise<undefined>}
     */
    async file(
      this: void,
      node: File,
      dirent: Dirent,
      parent: Parent,
      tree: Root,
      ancestors: Parent[],
      fs: FileSystem
    ): Promise<undefined> {
      /**
       * The list of relative ancestor paths.
       *
       * @const {string[]} paths
       */
      const paths: string[] = [...ancestors.slice(1), node].map(n => {
        ok(n.type !== 'root', 'did not expect tree')
        return n.name
      })

      /**
       * Info about the file.
       *
       * @const {Stats} stats
       */
      const stats: Stats = await fs.stat(pathe.join(tree.path, ...paths))

      return node.size = stats.size, void node
    }
  }
})

console.log(inspect(tree))
console.dir(size(tree))
console.dir(size(tree, node => node.type === 'directory'))
console.dir(size(tree, node => node.type === 'file'))

...yields

root[1]
β”‚ path: "/Users/lex/Projects/flex-development/fst-util-from-fs/"
└─0 directory<src>[6]
    β”œβ”€0 directory<interfaces>[16]
    β”‚   β”œβ”€0  file<dirent.mts> null
    β”‚   β”‚      size: 950
    β”‚   β”œβ”€1  file<file-system-entries.mts> null
    β”‚   β”‚      size: 559
    β”‚   β”œβ”€2  file<file-system.mts> null
    β”‚   β”‚      size: 643
    β”‚   β”œβ”€3  file<filters.mts> null
    β”‚   β”‚      size: 583
    β”‚   β”œβ”€4  file<handles.mts> null
    β”‚   β”‚      size: 640
    β”‚   β”œβ”€5  file<index.mts> null
    β”‚   β”‚      size: 1102
    β”‚   β”œβ”€6  file<is-directory.mts> null
    β”‚   β”‚      size: 345
    β”‚   β”œβ”€7  file<is-file.mts> null
    β”‚   β”‚      size: 315
    β”‚   β”œβ”€8  file<is-symbolic-link.mts> null
    β”‚   β”‚      size: 365
    β”‚   β”œβ”€9  file<options.mts> null
    β”‚   β”‚      size: 2052
    β”‚   β”œβ”€10 file<readdir-dirent-options.mts> null
    β”‚   β”‚      size: 744
    β”‚   β”œβ”€11 file<readdir-options.mts> null
    β”‚   β”‚      size: 594
    β”‚   β”œβ”€12 file<readdir.mts> null
    β”‚   β”‚      size: 798
    β”‚   β”œβ”€13 file<realpath.mts> null
    β”‚   β”‚      size: 743
    β”‚   β”œβ”€14 file<stat.mts> null
    β”‚   β”‚      size: 566
    β”‚   └─15 file<stats.mts> null
    β”‚          size: 704
    β”œβ”€1 directory<internal>[12]
    β”‚   β”œβ”€0  file<chain-or-call.mts> null
    β”‚   β”‚      size: 2656
    β”‚   β”œβ”€1  file<combine-paths.mts> null
    β”‚   β”‚      size: 1579
    β”‚   β”œβ”€2  file<constant.mts> null
    β”‚   β”‚      size: 413
    β”‚   β”œβ”€3  file<empty-array.mts> null
    β”‚   β”‚      size: 260
    β”‚   β”œβ”€4  file<empty-file-system-entries.mts> null
    β”‚   β”‚      size: 524
    β”‚   β”œβ”€5  file<fs.browser.mts> null
    β”‚   β”‚      size: 973
    β”‚   β”œβ”€6  file<fs.d.mts> null
    β”‚   β”‚      size: 241
    β”‚   β”œβ”€7  file<fs.node.mts> null
    β”‚   β”‚      size: 414
    β”‚   β”œβ”€8  file<identity.mts> null
    β”‚   β”‚      size: 351
    β”‚   β”œβ”€9  file<is-promise.mts> null
    β”‚   β”‚      size: 640
    β”‚   β”œβ”€10 file<visit-directory.mts> null
    β”‚   β”‚      size: 10232
    β”‚   └─11 file<with-trailing-slash.mts> null
    β”‚          size: 1357
    β”œβ”€2 directory<types>[10]
    β”‚   β”œβ”€0 file<awaitable.mts> null
    β”‚   β”‚     size: 269
    β”‚   β”œβ”€1 file<extensions.mts> null
    β”‚   β”‚     size: 323
    β”‚   β”œβ”€2 file<filter.mts> null
    β”‚   β”‚     size: 1006
    β”‚   β”œβ”€3 file<get-file-system-entries.mts> null
    β”‚   β”‚     size: 847
    β”‚   β”œβ”€4 file<handle.mts> null
    β”‚   β”‚     size: 1347
    β”‚   β”œβ”€5 file<index.mts> null
    β”‚   β”‚     size: 628
    β”‚   β”œβ”€6 file<list.mts> null
    β”‚   β”‚     size: 241
    β”‚   β”œβ”€7 file<sort.mts> null
    β”‚   β”‚     size: 455
    β”‚   β”œβ”€8 file<to-visit-key.mts> null
    β”‚   β”‚     size: 1083
    β”‚   └─9 file<visit-map.mts> null
    β”‚         size: 321
    β”œβ”€3 directory<utils>[2]
    β”‚   β”œβ”€0 file<get-file-system-entries.mts> null
    β”‚   β”‚     size: 5805
    β”‚   └─1 file<index.mts> null
    β”‚         size: 157
    β”œβ”€4 file<index.mts> null
    β”‚     size: 189
    └─5 file<util.mts> null
          size: 2055
47 # total number of nodes
5  # total number of directory nodes
42 # total number of file nodes

API

This package exports the identifiers listed below.

There is no default export.

fromFileSystem<T>([options])

Create a file system tree.

πŸ‘‰ Note: Returns a promise if one of the following methods returns a promise: fs.realpath, options.getFileSystemEntries, options.handles.directory, options.handlers.file.

Type Parameters

Parameters

  • options (Options | null | undefined, optional) β€” options for tree creation

Returns

(T) The file system tree

Utilities

This package exports utilities from @flex-development/fst-util-from-fs/utils.

The utilities library exports the identifiers listed below.

There is no default export.

getFileSystemEntries<T>(parent[, fs])

Get a record of accessible file system entries.

Entries are relative to parent.

πŸ‘‰ Note: Returns a promise if fs.readdir returns a promise, or any symbolic links are encountered and fs.stat returns a promise.

Type Parameters

Parameters

  • parent (URL | string | null | undefined) β€” the entry id of the parent directory
  • fs (FileSystem | null | undefined) β€” the file system api

Returns

(T) The file system entries record

Types

This package is fully typed with TypeScript.

Awaitable<T>

Create a union of T and T as a promise-like object (type).

type Awaitable<T> = PromiseLike<T> | T

Type Parameters

  • T (any) β€” the value

Dirent

Information about a file system entry (interface).

This interface can be augmented to register custom methods and properties.

declare module '@flex-development/fst-util-from-fs' {
  interface Dirent {
    parentPath: string
  }
}

Properties

  • isDirectory (IsDirectory) β€” check if the entry is a directory
  • isFile (IsFile) β€” check if the entry is a file
  • isSymbolicLink (IsSymbolicLink) β€” check if the entry is a symbolic link
  • name (string) β€” the path to the entry, relative to its parent directory

Extensions

Union of options used to filter files by extension (type).

type Extensions = List<string> | string

FileSystemEntries

Information about directories and files (interface).

Properties

FileSystem

The file system API (interface).

Properties

  • readdir (Readdir) β€” read the entire contents of a directory
  • realpath (Realpath) β€” compute a canonical pathname by resolving ., .., and symbolic links
  • stat (Stat) β€” get information about a file system entry

Filter

Determine if a node for path should be added to the tree (type).

type Filter = (
  this: void,
  path: string,
  depth: number | null | undefined,
  dirent: Dirent,
  parent: AnyParent,
  tree: Root
) => boolean

Parameters

  • path (string) β€” the path to the file system entry, relative to its parent directory
  • depth (number | null | undefined) β€” the current search depth
  • dirent (Dirent) β€” the dirent representing the file system entry
  • parent (AnyParent) β€” the current parent node
  • tree (Root) β€” the file system tree

Returns

(boolean) true if node for path should be added, false otherwise

Filters

The filters used to determine if a node should be added to a tree (interface).

Properties

  • directory (Filter | null | undefined, optional) β€” determine if a Directory node should be added to the tree
  • file (Filter | null | undefined, optional) β€” determine if a File node should be added to the tree

GetFileSystemEntries<[T]>

Get a file system entries record (type).

type GetFileSystemEntries<
  T extends Awaitable<FileSystemEntries> = Awaitable<FileSystemEntries>
> = (
  this: void,
  parent: URL | string,
  fs: FileSystem
) => T

Type Parameters

Parameters

  • parent (URL | string) β€” the entry id of the parent directory
  • fs (FileSystem) β€” the file system api

Returns

(T) The file system entries record

Handle<[T][, Result]>

Handle a node that has been added to the tree (type).

type Handle<
  T extends Child = Child,
  Result extends Awaitable<null | undefined | void> = Awaitable<null | undefined | void>
> = (
  this: void,
  node: T,
  dirent: Dirent,
  parent: AnyParent,
  tree: Root,
  ancestors: AnyParent[],
  fs: FileSystem
) => Result

Type Parameters

Parameters

  • node (T) β€” the node representing the file system entry
  • dirent (Dirent) β€” the dirent representing the file system entry
  • parent (AnyParent) β€” the parent of node
  • tree (Root) β€” the current file system tree
  • ancestors (AnyParent[]) β€” the ancestors of node, with the last node being parent
  • fs (FileSystem) β€” the file system api

Returns

(Result) Nothing

Handles

The callbacks to fire after a node is added to a tree (interface).

Properties

IsDirectory

Check if a file system entry is a directory (interface).

Signatures

(): boolean

Returns

(boolean) true if entry is directory, false otherwise

IsFile

Check if a file system entry is a file (interface).

Signatures

(): boolean

Returns

(boolean) true if entry is file, false otherwise

IsSymbolicLink

Check if a file system entry is a symbolic link (interface).

Signatures

(): boolean

Returns

(boolean) true if entry is symbolic link, false otherwise

List<[T]>

A list (type).

type List<T = unknown> = ReadonlySet<T> | readonly T[]

Type Parameters

  • T (any, optional) β€” the list item type

Options

Options for creating a file system tree (interface).

Properties

  • depth (number | null | undefined, optional) β€” the maximum search depth (inclusive)

    πŸ‘‰ note: a search depth less than 0 will produce an empty tree

  • extensions (Extensions | null | undefined, optional) β€” the file extensions to filter matched files by

    πŸ‘‰ note: this is alternative way to exclude files from the tree

  • filters (Filters | null | undefined, optional) β€” the filters used to determine if nodes should be added to the tree
  • fs (FileSystem | null | undefined, optional) β€” the file system adapter
  • getFileSystemEntries (GetFileSystemEntries | null | undefined, optional) β€” get a file system entries record
  • handles (Handles | null | undefined, optional) β€” the callbacks to fire after a node is added to the tree
  • root (URL | string | null | undefined, optional) β€” the module id of the root directory
  • sort (Sort | null | undefined, optional) β€” the child node sorter.
    by default, nodes are sorted by type and name
  • visitKey (ToVisitKey | null | undefined, optional) β€” generate a key for the visited directory map
    • default: path => path
  • visited (VisitMap | null | undefined, optional) β€” the map indicating which directories have already been searched
    • default: new Map()

ReaddirDirentOptions

Options for reading the contents of a directory (interface).

Extends

Properties

  • withFileTypes (true) β€” whether the result should be a content object list instead of just strings.
    if true, the result will be a list of Direct objects, which provide methods like isDirectory() and isFile() to get more information about a file system entry without additional fs.stat() calls

ReaddirOptions

Options for reading the contents of a directory (interface).

Extends

Properties

  • withFileTypes? (boolean | null | undefined) β€” whether the result should be a content object list instead of just strings.
    if true, the result will be a list of Direct objects, which provide methods like isDirectory() and isFile() to get more information about a file system entry without additional fs.stat() calls

Readdir

Read the entire contents of a directory (interface).

Signatures

<T extends Awaitable<readonly Dirent[]>>(id: URL | string, options: ReaddirDirentOptions): T

Type Parameters

Parameters

Returns

(T) The directory contents

Realpath

Compute a canonical pathname by resolving ., .., and symbolic links (interface).

πŸ‘‰ Note: A canonical pathname is not necessarily unique. Hard links and bind mounts can expose an entity through many pathnames.

Signatures

<T extends Awaitable<string>>(id: URL | string): T

Type Parameters

Parameters

  • id (URL | string) β€” the entry id

Returns

(T) The canonical pathname

Sort

Decide how two nodes should be sorted (type).

type Sort = (this: void, a: Child, b: Child) => number

Parameters

  • a (Child) β€” the current child node
  • b (Child) β€” the next child node

Returns

(number) The comparison result

Stat

Get information about a file system entry (interface).

Signatures

<T extends Awaitable<Stats>>(id: URL | string): T

Type Parameters

Parameters

  • id (URL | string) β€” the entry id

Returns

(T) The entry info

Stats

Information about a file system entry (interface).

This interface can be augmented to register custom methods and properties.

declare module '@flex-development/fst-util-from-fs' {
  interface Stats {
    size: bigint | number
  }
}

Properties

  • isDirectory (IsDirectory) β€” check if the entry is a directory
  • isFile (IsFile) β€” check if the entry is a file

ToVisitKey<[K]>

Get a visit map key for a pathname (type).

type ToVisitKey<K extends WeakKey | string | null = WeakKey | string | null> = (
  this: void,
  path: string,
  dir: string | null,
  parent: AnyParent,
  tree: Root,
  options: Options
) => K

Type Parameters

  • K (WeakKey | string | null, optional) β€” the map key

Parameters

  • path (string) β€” the canonical pathname (realpath) of the directory to visit
  • dir (string | null) β€” the path to the directory to visit, relative to tree.path
  • parent (AnyParent) β€” the current parent node
  • tree (Root) β€” the current file system tree
  • options (Options) β€” options for tree creation

Returns

(K) The visited map key

VisitMap

Map indicating which directories have already been searched (type).

type VisitMap = Map<string | null, boolean> | WeakMap<WeakKey, boolean>

Syntax tree

The syntax tree is fst.

Contribute

See CONTRIBUTING.md.

This project has a code of conduct. By interacting with this repository, organization, or community you agree to abide by its terms.

About

fst utility to create trees from a file system

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Sponsor this project

 

Packages

 
 
 

Contributors 3

  •  
  •  
  •