File System

File System

The File System API offers a way to organize and query file-system data in renoun. It is a powerful tool that allows you to define a schema for file exports and query those exports using a simple API.

To get started with the File System API, instantiate the Directory class to target a set of files and directories from the file system. You can then use the getEntry / getDirectory / getFile methods to query a specific descendant file or directory:

import { Directory } from 'renoun/file-system'

const posts = new Directory({
  path: 'posts',
  loaders: {
    mdx: (path) => import(`./posts/${path}.mdx`),
  },
})

Here we are creating a new Directory instance that targets the posts directory relative to the working directory. We are also specifying a loader for the mdx file extension that will be used to load the file contents using the bundler.

Querying file system entries

import { Directory } from 'renoun/file-system'

const posts = new Directory({
  path: 'posts',
  loaders: {
    mdx: (path) => import(`./posts/${path}.mdx`),
  },
})

export default async function Page({ slug }: { slug: string }) {
  const post = await posts.getFile(slug, 'mdx')
  const Content = await post.getExportValue('default')

  return <Content />
}

You can query the entries within the directory to help with generating navigations and index pages. For example, we can include only mdx file extensions to generate an index page of links to all posts using the getEntries method:

import { Directory, withSchema } from 'renoun/file-system'

interface PostType {
  frontmatter: {
    title: string
    date: Date
  }
}

const posts = new Directory({
  path: 'posts',
  include: '*.mdx',
  loaders: {
    mdx: withSchema<PostType>((path) => import(`./posts/${path}.mdx`)),
  },
})

export default async function Page() {
  const allPosts = await posts.getEntries()

  return (
    <>
      <h1>Blog</h1>
      <ul>
        {allPosts.map(async (post) => {
          const path = post.getPath()
          const frontmatter = await post.getExportValue('frontmatter')

          return (
            <li key={path}>
              <a href={path}>{frontmatter.title}</a>
            </li>
          )
        })}
      </ul>
    </>
  )
}

Type checking file exports

To improve type safety, you can utilize the withSchema helper to specify the schema for the file’s exports:

import { Directory, withSchema } from 'renoun/file-system'

interface PostType {
  frontmatter: {
    title: string
    date: Date
  }
}

const posts = new Directory({
  path: 'posts',
  loaders: {
    mdx: withSchema<PostType>((path) => import(`./posts/${path}.mdx`)),
  },
})

Now when we call JavaScript#getExportValue and JavaScriptExport#getRuntimeValue we will have stronger type checking and autocomplete.

Schema Validation

You can also apply schema validation using libraries that follow the Standard Schema Spect like Zod, Valibot, or Arktype to ensure file exports conform to a specific schema:

import { Directory, withSchema } from 'renoun/file-system'
import { z } from 'zod'

const posts = new Directory({
  path: 'posts',
  loaders: {
    mdx: withSchema(
      {
        frontmatter: z.object({
          title: z.string(),
          date: z.date(),
        }),
      },
      (path) => import(`./posts/${path}.mdx`)
    ),
  },
})

Alternatively, you can define a schema yourself using both TypeScript types and custom validation functions:

import { Directory, withSchema } from 'renoun/file-system'

interface PostType {
  frontmatter: {
    title: string
    date: Date
  }
}

const posts = new Directory({
  path: 'posts',
  loaders: {
    mdx: withSchema<PostType>(
      {
        frontmatter: (value) => {
          if (typeof value.title !== 'string') {
            throw new Error('Title is required')
          }

          if (!(value.date instanceof Date)) {
            throw new Error('Date is required')
          }

          return value
        },
      },
      (path) => import(`./posts/${path}.mdx`)
    ),
  },
})

The file system utilities are not limited to MDX files and can be used with any file type. By organizing content and source code into structured collections, you can easily generate static pages and manage complex routing and navigations. For a more in-depth look at the file system utilities, visit the docs site.

API Reference

withSchema

{ <Types extends ModuleExports>(): ModuleLoaderWithSchema<Types>; <Types extends ModuleExports>(runtime: ModuleRuntimeLoader<NoInfer<Types>>): ModuleLoaderWithSchema<Types, true>; <Types extends ModuleExports = never, Schema extends ModuleExports = ModuleExports<any>>(schema: IsNever<Types> extends true ? Schema : Partial<ModuleExportValidators<NoInfer<Types>>>, runtime: ModuleRuntimeLoader<IsNever<Types> extends true ? NoInfer<Schema> : NoInfer<Types>>): ModuleLoaderWithSchema<IsNever<Types> extends true ? Schema : ModuleExportValidators<NoInfer<Types>>>; }

Signatures

Returns
ModuleLoaderWithSchema<Types, false>
Parameters

runtime *

ModuleRuntimeLoader<NoInfer<Types>>

path *

string

file *

JavaScriptFile<Types, Path, Extension>
Returns
ModuleLoaderWithSchema<Types, true>
Parameters

schema *

ModuleExports<any> | Partial<ModuleExportValidators<NoInfer<Types>>>

runtime *

ModuleRuntimeLoader<IsNever<Types> extends true ? NoInfer<Schema> : NoInfer<Types>>

path *

string

file *

JavaScriptFile<any, string, string>
Returns
ModuleLoaderWithSchema<IsNever<Types> extends true ? Schema : ModuleExportValidators<NoInfer<Types>>, false>

isDirectory

<Types extends Record<string, any>>(entry: FileSystemEntry<Types> | undefined) => entry is Directory<Types>

Determines if a FileSystemEntry is a Directory.

Parameters

entry *

FileSystemEntry<Types> | undefined
Returns
boolean

isFile

<Types extends Record<string, any>, const Extension extends StringUnion<keyof Types> | StringUnion<keyof Types>[]>(entry: FileSystemEntry<Types> | undefined, extension?: Extension) => entry is Extension extends undefined ? File<Types> : Extension extends string ? FileWithExtension<Types, Extension> : FileWithExtension<Types, Extension[number]>

Determines if a FileSystemEntry is a File and optionally narrows the result based on the provided extensions.

Parameters

entry *

FileSystemEntry<Types> | undefined

extension

Extension | undefined
Returns
boolean

isJavaScriptFile

<Types extends Record<string, any>>(entry: FileSystemEntry<Types> | undefined) => entry is JavaScriptFile<Types>

Determines if a FileSystemEntry is a JavaScriptFile.

Parameters

entry *

FileSystemEntry<Types> | undefined
Returns
boolean

FileSystem

FileSystem

Constructors

(options: FileSystemOptions) => FileSystem

Methods

getProjectOptions

() => ProjectOptions

getAbsolutePath

(path: string) => string

getRelativePath

(path: string) => string

getRelativePathToWorkspace

(path: string) => string

getPath

(path: string, options?: { basePath?: string; }) => string

readDirectorySync

(path?: string, options?: { recursive?: boolean; }) => DirectoryEntry[]

readDirectory

(path?: string, options?: { recursive?: boolean; }) => Promise<DirectoryEntry[]>

readFileSync

(path: string) => string

readFile

(path: string) => Promise<string>

isFilePathExcludedFromTsConfig

(filePath: string, isDirectory?: boolean) => boolean

isFilePathGitIgnored

(filePath: string) => boolean

getFileExports

(filePath: string) => Promise<Array<FileExport>>

getFileExportMetadata

(name: string, filePath: string, position: number, kind: SyntaxKind) => Promise<{ name: string; environment: string; jsDocMetadata: { description?: string; tags?: { tagName: string; text?: string; }[]; } | undefined; location: DeclarationLocation; text: string; }>

resolveTypeAtLocation

(filePath: string, position: number, kind: SyntaxKind, filter?: SymbolFilter) => Promise<ResolvedType | undefined>

MemoryFileSystem

MemoryFileSystem

A file system that stores files in memory.

Constructors

() => MemoryFileSystem

Methods

getProjectOptions

() => ProjectOptions

transpileFile

(path: string) => Promise<string>

getAbsolutePath

(path: string) => string

getFiles

() => Map<string, string>

readDirectorySync

(path?: string) => DirectoryEntry[]

readDirectory

(path?: string) => Promise<DirectoryEntry[]>

readFileSync

(path: string) => string

readFile

(path: string) => Promise<string>

isFilePathGitIgnored

(filePath: string) => boolean

NodeFileSystem

NodeFileSystem

Constructors

(options: FileSystemOptions) => NodeFileSystem

Methods

getProjectOptions

() => { tsConfigFilePath: string; }

getAbsolutePath

(path: string) => string

readDirectorySync

(path?: string) => DirectoryEntry[]

readDirectory

(path?: string) => Promise<DirectoryEntry[]>

readFileSync

(path: string) => string

readFile

(path: string) => Promise<string>

isFilePathGitIgnored

(filePath: string) => boolean

Repository

Repository

Constructors

(repository: string | RepositoryConfig) => Repository

Methods

getIssueUrl

(options: GetIssueUrlOptions) => string

Constructs a new issue URL for the repository.

getFileUrl

(options: GetFileUrlOptions) => string

Constructs a URL for a file in the repository.

getDirectoryUrl

(options: GetDirectoryUrlOptions) => string

Constructs a URL for a directory in the repository.

InferModuleExports

InferModuleExports<Exports>

Utility type that infers the schema output from validator functions or a Standard Schema.

InferModuleExports<Exports>

InferModuleLoadersTypes

InferModuleLoadersTypes<Loaders>

Infer extension types for all loaders in a module.

InferModuleLoadersTypes<Loaders>

FileNotFoundError

FileNotFoundError

Error for when a file is not found.

Constructors

(path: string | Array<string>, extension?: any) => FileNotFoundError

FileSystemEntry

FileSystemEntry<Types>

A directory or file entry.

Directory

Directory<Types, LoaderTypes, Loaders, Include>

A directory containing files and subdirectories in the file system.

File

File<Types, Path, Extension>

A file in the file system.

FileOptions

FileOptions<Types, Path>

Options for a file in the file system.

Properties

path *

Path

pathCasing

SlugCasings | undefined

depth

number | undefined

directory

Directory<Types, WithDefaultTypes<Types>, ModuleLoaders, EntryInclude<FileSystemEntry<Types>, Types>> | undefined

File

File<Types, Path, Extension>

A file in the file system.

Constructors

<undefined, undefined, undefined>(options: FileOptions<Types, Path>) => File<Types, Path, Extension>

Methods

getName

() => string

The intrinsic name of the file.

getBaseName

() => string

The file name without the extension.

getTitle

() => string

The file name formatted as a title.

getOrder

() => string | undefined

The order of the file if defined.

getModifier

() => string | undefined

The modifier of the file if defined.

getExtension

() => Extension

The extension of the file if defined.

getDepth

() => number

Get the depth of the file starting from the root directory.

getSlug

() => string

Get the slug of the file.

getPath

(options?: { includeBasePath?: boolean; includeDuplicateSegments?: boolean; }) => string

Get the path of the file excluding the file extension and order prefix. The configured pathCasing option will be used format the path segments.

getPathSegments

(options?: { includeBasePath?: boolean; includeDuplicateSegments?: boolean; }) => Array<string>

Get the path segments of the file.

getRelativePath

() => string

Get the file path relative to the root directory.

getRelativePathToWorkspace

() => string

Get the file path relative to the workspace root.

getAbsolutePath

() => string

Get the absolute file system path.

getBlameUrl

(options?: Pick<GetFileUrlOptions, "ref">) => string

Get the URL to the file git blame for the configured git repository.

getEditUrl

(options?: Pick<GetFileUrlOptions, "ref" | "line">) => string

Get the edit URL to the file source for the configured git repository.

getHistoryUrl

(options?: Pick<GetFileUrlOptions, "ref">) => string

Get the URL to the file history for the configured git repository.

getRawUrl

(options?: Pick<GetFileUrlOptions, "ref">) => string

Get the URL to the raw file contents for the configured git repository.

getSourceUrl

(options?: Pick<GetFileUrlOptions, "ref" | "line">) => string

Get the URL to the file source for the configured git repository.

getEditorUri

() => string

Get the URI to the file source code for the configured editor.

getFirstCommitDate

() => Promise<Date | undefined>

Get the first local git commit date of the file.

getLastCommitDate

() => Promise<Date | undefined>

Get the last local git commit date of the file.

getAuthors

() => Promise<Array<GitAuthor>>

Get the local git authors of the file.

getParent

() => Directory<Types, WithDefaultTypes<Types>, ModuleLoaders, EntryInclude<FileSystemEntry<WithDefaultTypes<Types>>, WithDefaultTypes<Types>>>

Get the directory containing this file.

getSiblings

<GroupTypes extends Record<string, any> = Types>(options?: { entryGroup?: EntryGroup<GroupTypes, FileSystemEntry<any>[]>; includeDuplicateSegments?: boolean; }) => Promise<[FileSystemEntry<Types> | undefined, FileSystemEntry<Types> | undefined]>

Get the previous and next sibling entries (files or directories) of the parent directory. If the file is an index or readme file, the siblings will be retrieved from the parent directory.

getText

() => Promise<string>

Get the source text of the file.

FileExportNotFoundError

FileExportNotFoundError

Error for when a file export is not found.

Constructors

(path: string, name: string) => FileExportNotFoundError

JavaScriptFileExport

JavaScriptFileExport<Value>

A JavaScript file export.

Constructors

<Value>(name: string, file: JavaScriptFile<any, string, string>, loader?: ModuleLoader<any> | undefined) => JavaScriptFileExport<Value>

Methods

init

<Value>(name: string, file: JavaScriptFile<any>, loader?: ModuleLoader<any>) => Promise<JavaScriptFileExport<Value>>

getStaticMetadata

() => Promise<{ name: string; environment: string; jsDocMetadata: { description?: string; tags?: { tagName: string; text?: string; }[]; } | undefined; location: DeclarationLocation; text: string; } | undefined>

getSlug

() => string

Get the slug of the file export.

getName

() => string

Get the name of the export. Default exports will use the file name or declaration name if available.

getTitle

() => string

The export name formatted as a title.

getDescription

() => string | undefined

Get the JS Doc description of the export.

getTags

() => Array<{ tagName: string; text?: string; }> | undefined

Get the JS Doc tags of the export.

getEnvironment

() => string | undefined

Get the environment of the export.

getText

() => string | undefined

Get the source text of the export.

getPosition

() => DeclarationPosition | undefined

Get the start and end position of the export in the file system.

getEditUrl

(options?: Pick<GetFileUrlOptions, "ref">) => string

Get the edit URL to the file export source for the configured git repository.

getSourceUrl

(options?: Pick<GetFileUrlOptions, "ref">) => string

Get the URL to the file export source for the configured git repository.

getEditorUri

() => string

Get the URI to the file export source code for the configured editor.

getType

(filter?: SymbolFilter) => Promise<ResolvedType | undefined>

Get the resolved type of the export.

getRuntimeValue

() => Promise<Value>

Get the runtime value of the export. An error will be thrown if the export is not found or the configured schema validation for this file extension fails.

JavaScriptFileOptions

JavaScriptFileOptions<Types, Path>

Options for a JavaScript file in the file system.

Properties

loader

ModuleLoader<Types> | undefined

path *

Path

pathCasing

SlugCasings | undefined

depth

number | undefined

directory

Directory<Types, WithDefaultTypes<Types>, ModuleLoaders, EntryInclude<FileSystemEntry<Types>, Types>> | undefined

JavaScriptFile

JavaScriptFile<Types, Path, Extension>

A JavaScript file in the file system.

Constructors

<undefined, undefined, undefined>(JavaScriptFileOptions<Types, Path>) => JavaScriptFile<Types, Path, Extension>

Methods

parseExportValue

(name: string, value: any) => any

Parse and validate an export value using the configured schema if available.

getExports

() => Promise<Array<JavaScriptFileExport<Types[Extract<keyof Types, string>]>>>

Get all exports from the JavaScript file.

getExport

<ExportName extends Extract<keyof Types, string>>(name: ExportName) => Promise<JavaScriptFileExport<Types[ExportName]>>

Get a JavaScript file export by name.

getExportLocation

(name: string) => Promise<FileExport | undefined>

Get the start position of an export in the JavaScript file.

getExportValue

<ExportName extends Extract<keyof Types, string>>(name: ExportName) => Promise<Types[ExportName]>

Get the runtime value of an export in the JavaScript file.

hasExport

(name: string) => Promise<boolean>

Check if an export exists in the JavaScript file.

EntryInclude

EntryInclude<Entry, Types>

string

(entry: FileSystemEntry<Types>) => entry is Entry

entry *

FileSystemEntry<Types>

(entry: FileSystemEntry<Types>) => Promise<boolean> | boolean

entry *

FileSystemEntry<Types>

IncludedEntry

IncludedEntry<Types, DirectoryFilter>
Directory<any, any, ModuleLoaders, EntryInclude<FileSystemEntry<any>, any>>File<any, string, string>

Directory

Directory<Types, LoaderTypes, Loaders, Include>

A directory containing files and subdirectories in the file system.

File

File<Types, Path, Extension>

A file in the file system.

Directory

Directory<Types, LoaderTypes, Loaders, Include>

A directory containing files and subdirectories in the file system.

Constructors

<undefined, undefined, undefined, undefined>(options?: DirectoryOptions<Types, LoaderTypes, Loaders, Include> | undefined) => Directory<Types, LoaderTypes, Loaders, Include>

Methods

getFileSystem

() => FileSystem

Get the file system for this directory.

getRepository

() => Repository

Get the Repository for this directory.

getDepth

() => number

Get the depth of the directory starting from the root directory.

getFile

{ <const Path extends string, Extension extends ExtractFileExtension<Path> = ExtractFileExtension<Path>>(path: Path): Promise<Extension extends string ? IsJavaScriptLikeExtension<Extension> extends true ? JavaScriptFile<LoaderTypes[Extension], string, Extension> : File<LoaderTypes, Path, Extension> : File<LoaderTypes>>; <ExtensionType extends keyof LoaderTypes | string, const Extension extends ExtensionType | Extension[]>(path: string | string[], extension?: Extension | Extension[]): Promise<Extension extends string ? IsJavaScriptLikeExtension<Extension> extends true ? JavaScriptFile<LoaderTypes[Extension], string, Extension> : File<LoaderTypes, Extension> : File<LoaderTypes>>; }

getDirectory

(path: string | string[]) => Promise<Directory<LoaderTypes>>

Get a directory at the specified path.

getEntry

(path: string | string[]) => Promise<FileSystemEntry<LoaderTypes>>

Get a file or directory at the specified path. Files will be prioritized over directories.

getEntries

(options?: { recursive?: boolean; includeIndexAndReadme?: boolean; includeDuplicates?: boolean; includeGitIgnoredFiles?: boolean; includeTsConfigIgnoredFiles?: boolean; }) => Promise<Include extends string ? FileWithExtension<LoaderTypes, ExtractFileExtension<Include>>[] : Include extends EntryInclude<infer FilteredEntry, LoaderTypes> ? FilteredEntry[] : FileSystemEntry<LoaderTypes>[]>

Retrieves all entries (files and directories) within the current directory that are not excluded by Git ignore rules or the closest tsconfig file. Additionally, index and readme files are excluded by default.

getParent

() => Directory<any, any, any, EntryInclude<FileSystemEntry<any>, any>>

Get the directory containing this directory.

getSiblings

<GroupTypes extends Record<string, any> = LoaderTypes>(options?: { entryGroup?: EntryGroup<GroupTypes, FileSystemEntry<any>[]>; }) => Promise<[FileSystemEntry<LoaderTypes> | undefined, FileSystemEntry<LoaderTypes> | undefined]>

Get the previous and next sibling entries (files or directories) of the parent directory.

getSlug

() => string

Get the slug of the directory.

getName

() => string

Get the base name of the directory.

getBaseName

() => string

Get the base name of the directory.

getTitle

() => string

The directory name formatted as a title.

getPath

(options?: { includeBasePath?: boolean; }) => string

Get a URL-friendly path to the directory.

getPathSegments

(options?: { includeBasePath?: boolean; }) => Array<string>

Get the path segments to the directory.

getBasePath

() => string | undefined

Get the configured base path of the directory.

getRelativePath

() => string

Get the relative path of the directory.

getRelativePathToWorkspace

() => string

Get the relative path of the directory to the workspace.

getAbsolutePath

() => string

Get the absolute path of the directory.

getHistoryUrl

(options?: Pick<GetFileUrlOptions, "ref">) => string

Get the URL to the directory history for the configured git repository.

getSourceUrl

(options?: Pick<GetFileUrlOptions, "ref">) => string

Get the URL to the directory source for the configured git repository.

getEditorUri

() => string

Get the URI to the directory source code for the configured editor.

getFirstCommitDate

() => Promise<Date | undefined>

Get the first local git commit date of the directory.

getLastCommitDate

() => Promise<Date | undefined>

Get the last local git commit date of the directory.

getAuthors

() => Promise<Array<GitAuthor>>

Get the local git authors of the directory.

hasEntry

(entry: FileSystemEntry<any> | undefined) => entry is FileSystemEntry<LoaderTypes>

Checks if this directory contains the provided entry.

hasFile

<ExtensionType extends keyof LoaderTypes | string, const Extension extends ExtensionType | Extension[]>(entry: FileSystemEntry<any> | undefined, extension?: Extension | Extension[]) => entry is FileWithExtension<LoaderTypes, Extension>

Checks if this directory contains the provided file.

EntryGroupOptions

EntryGroupOptions<Entries>

Options for an EntryGroup.

Properties

entries *

Entries

EntryGroup

EntryGroup<Types, Entries, Loaders>

A group of file system entries.

Constructors

<Types, Entries extends Array<FileSystemEntry<any>>, Loaders extends ModuleLoaders>(options: EntryGroupOptions<Entries>) => EntryGroup<Types, Entries, Loaders>

Methods

getEntries

(options?: { recursive?: boolean; includeIndexAndReadme?: boolean; }) => Promise<Entries>

Get all entries in the group.

getEntry

(path: string | string[]) => Promise<FileSystemEntry<Types>>

Get an entry in the group by its path.

getFile

<const Extension extends string | undefined = undefined>(path: string | string[], extension?: Extension | Extension[]) => Promise<Extension extends string ? IsJavaScriptLikeExtension<Extension> extends true ? JavaScriptFile<Types[Extension]> : File<Types> : File<Types>>

Get a file at the specified path and optional extension(s).

getDirectory

(path: string | string[]) => Promise<Directory<Types>>

Get a directory at the specified path.

FileWithExtension

FileWithExtension<Types, Extension>

Determines the type of a FileSystemEntry based on its extension.

File

File<Types, Path, Extension>

A file in the file system.

JavaScriptFile

JavaScriptFile<Types, Path, Extension>

A JavaScript file in the file system.