# Pipeline

The Pipeline API is a recent addition, somewhat experimental and likely to change, though not that much, since I already use it heavily in a few codebases.

# Rationale

Technically you don't need a pipeline object to transform a LiveTree.files into a map compatible with DevServer and generateFiles. You can just do this:

const tree = new LiveTree('site', import.meta.url)

let files = tree.files.values().toArray()

// now do your site processing
files = files.filter(f => /* ... */)
files = files.map(f => /* ... */)
files.forEach(f => /* ... */)
await Promise.all(files.forEach(async f => /* ... */))

const fileMap = new Map(files.map(f => [f.path, f.content]))
server.files = fileMap
generateFiles(fileMap)

But that gets very inconvenient very quickly:

So the Pipeline class was created as a convenience.

# API

class Pipeline {

  static from(files: LiveTree['files']): Pipeline

  private constructor(files: MemFile[], filters: Filter[])

  // filter files for the next action
  with(regex: RegExp | string): Pipeline
  without(regex: RegExp | string): Pipeline

  // add/remove individual files
  add(path: string, content: string | Buffer | MemFile): void
  del(path: string): void

  // add/remove entire subtrees
  graft(prefix: string, files: Pipeline | LiveTree): void
  remove(): void

  // foreach conveniences
  do(fn: (file: MemFile) => void): void
  async doAsync(fn: (file: MemFile) => void | Promise<void>):
    Promise<void>

  // map conveniences
  all(): MemFile[]
  paths(): string[]

  // ill give you three guesses what this does
  copy(): Pipeline

  // return a map compatible with generateFiles and DevServer
  results(): Map<string, string|Buffer>

}

class MemFile {

  path: string
  content: Buffer

  constructor(path: string, content: string | Buffer)

  // return #text ??= content.toString('utf8')
  text: string

  // return text ?? content
  textOrContent() { return this.#text ?? this.content }

  copy(path = this.path): MemFile

}