Skip to content

Commit

Permalink
add EntryGroup utility (#220)
Browse files Browse the repository at this point in the history
  • Loading branch information
souporserious authored Nov 22, 2024
1 parent 27d917b commit 2e7f458
Show file tree
Hide file tree
Showing 3 changed files with 477 additions and 18 deletions.
54 changes: 54 additions & 0 deletions .changeset/modern-tools-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
'renoun': minor
---

Adds an `EntryGroup` utility to `renoun/file-system` that provides an interface for querying and navigating a group of entries:

```ts
import { Directory, EntryGroup } from 'renoun/file-system'

interface FrontMatter {
title: string
description?: string
date: string
tags?: string[]
}

interface MDXType {
frontmatter: FrontMatter
}

const posts = new Directory<{ mdx: MDXType }>({
path: 'posts',
})
const docs = new Directory<{ mdx: MDXType }>({
path: 'docs',
})
const group = new EntryGroup({
entries: [posts, docs],
})
const entries = await group.getEntries()
```

This also adds `getHasEntry` and `getHasFile` methods to `Directory` which can be used to check if an entry or file exists in an `EntryGroup`:

```ts
type MDXTypes = { metadata: { title: string } }
type TSXTypes = { title: string }

const directoryA = new Directory<{ mdx: MDXTypes }>({
fileSystem: new VirtualFileSystem({ 'Button.mdx': '' }),
})
const directoryB = new Directory<{ tsx: TSXTypes }>({
path: 'fixtures/components',
})
const group = new EntryGroup({
entries: [directoryA, directoryB],
})
const entry = await group.getEntryOrThrow('Button')
const hasFile = await directoryA.getHasFile(entry)

if (hasFile(entry, 'mdx')) {
entry // JavaScriptFile<MDXTypes>
}
```
100 changes: 97 additions & 3 deletions packages/renoun/src/file-system/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import { z } from 'zod'
import { NodeFileSystem } from './NodeFileSystem'
import { VirtualFileSystem } from './VirtualFileSystem'
import {
isFile,
isFileWithExtension,
type FileSystemEntry,
File,
Directory,
JavaScriptFile,
JavaScriptFileExport,
type FileSystemEntry,
EntryGroup,
isFile,
isFileWithExtension,
} from './index'

describe('file system', () => {
Expand Down Expand Up @@ -237,6 +238,17 @@ describe('file system', () => {
expect(nestedDirectory).toBeInstanceOf(Directory)
})

test('duplicate directory', async () => {
const fixtures = new Directory<{ ts: { title: string } }>({
path: 'fixtures',
})
const duplicate = fixtures.duplicate()

expect(duplicate).toBeInstanceOf(Directory)
expect(duplicate).not.toBe(fixtures)
expect(duplicate.getRelativePath()).toBe(fixtures.getRelativePath())
})

test('file', async () => {
const rootDirectory = new Directory()
const file = await rootDirectory.getFile('tsconfig', 'json')
Expand Down Expand Up @@ -680,4 +692,86 @@ describe('file system', () => {
expectTypeOf(file).toMatchTypeOf<File<FileTypes>>()
}
})

test('entry group', async () => {
const memoryFileSystem = new VirtualFileSystem({
'posts/building-a-button-component.mdx': '# Building a Button Component',
'posts/meta.js': 'export default { "title": "Posts" }',
})
type FrontMatter = { frontmatter: { title: string } }
const posts = new Directory<{ mdx: FrontMatter }>({
path: 'posts',
fileSystem: memoryFileSystem,
})
const docs = new Directory<{ mdx: FrontMatter }>({
path: 'fixtures/docs',
})
const group = new EntryGroup({
entries: [posts, docs],
})
const entries = await group.getEntries()

expect(entries).toHaveLength(2)
expect(entries[1].getName()).toBe('docs')

const entry = await group.getEntry('posts/building-a-button-component')

expect(entry).toBeInstanceOf(File)
expect(entry?.getName()).toBe('building-a-button-component')

const directory = await group.getDirectory('docs')

expect(directory).toBeInstanceOf(Directory)

const jsFile = await group.getFileOrThrow('posts/meta', 'js')

expect(jsFile).toBeInstanceOf(JavaScriptFile)
expectTypeOf(jsFile).toMatchTypeOf<JavaScriptFile<any>>()

const mdxFile = await group.getFileOrThrow(
'posts/building-a-button-component',
'mdx'
)

expect(mdxFile).toBeInstanceOf(JavaScriptFile)
expectTypeOf(mdxFile).toMatchTypeOf<JavaScriptFile<FrontMatter>>()

const file = await group.getFileOrThrow('meta', 'js')
const [previousEntry, nextEntry] = await file.getSiblings()

expect(previousEntry?.getName()).toBe('building-a-button-component')
expect(nextEntry?.getName()).toBe('docs')
})

test('has entry', async () => {
type MDXTypes = { metadata: { title: string } }
type TSXTypes = { title: string }

const directoryA = new Directory<{ mdx: MDXTypes }>({
fileSystem: new VirtualFileSystem({ 'Button.mdx': '' }),
})
const directoryB = new Directory<{ tsx: TSXTypes }>({
path: 'fixtures/components',
})
const group = new EntryGroup({
entries: [directoryA, directoryB],
})
const file = await group.getFileOrThrow('Button', 'mdx')

expectTypeOf(file).toMatchTypeOf<JavaScriptFile<MDXTypes>>()

const entry = await group.getEntryOrThrow('Button')
const hasEntry = await directoryA.getHasEntry(entry)

expect(hasEntry(entry)).toBe(true)
expectTypeOf(entry).toMatchTypeOf<FileSystemEntry<{ mdx: MDXTypes }>>()

const hasFile = await directoryA.getHasFile(entry)

expect(hasFile(entry, 'mdx')).toBe(true)

if (hasFile(entry, 'mdx')) {
expectTypeOf(entry).toMatchTypeOf<JavaScriptFile<MDXTypes>>()
}
})
})
Loading

0 comments on commit 2e7f458

Please sign in to comment.