npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

jostraca

v0.28.0

Published

Jostraca Jostraca.

Readme

jostraca

A code and project generator that uses React-style components to define files, folders, and content declaratively.

npm version license

Overview

Jostraca lets you compose a file tree using components — Project, Folder, File, Content, Fragment, Copy, and more. You describe the output structure in a define phase, then Jostraca builds the actual files in a build phase. Templates, slots, injections, 3-way merges, and custom components give you fine-grained control over generated output.

Install

npm install jostraca

Peer dependencies:

npm install jsonic memfs

Quick Start

import { Jostraca, Project, Folder, File, Content } from 'jostraca'

const jostraca = Jostraca()

await jostraca.generate({ folder: './out' }, () => {
  Project({ folder: 'my-app' }, () => {

    Folder({ name: 'src' }, () => {
      File({ name: 'index.js' }, () => {
        Content('console.log("hello world")\n')
      })
    })

    File({ name: 'package.json' }, () => {
      Content('{ "name": "my-app" }\n')
    })
  })
})

This generates:

out/
  my-app/
    src/
      index.js       -> console.log("hello world")
    package.json     -> { "name": "my-app" }

Template Substitution

Use $$path$$ syntax to insert values from the model:

const jostraca = Jostraca({
  model: { app: { name: 'Acme', version: '1.0.0' } }
})

await jostraca.generate({ folder: './out' }, () => {
  Project({}, () => {
    File({ name: 'config.txt' }, () => {
      Content('App: $$app.name$$ v$$app.version$$\n')
    })
  })
})
// config.txt -> App: Acme v1.0.0

Fragments and Slots

Read external template files with Fragment, and replace marked regions with Slot:

// template.html contains:
// <html>
// <!-- <[SLOT:head]> -->
// <body>
// <!-- <[SLOT:body]> -->
// </body>
// </html>

File({ name: 'index.html' }, () => {
  Fragment({ from: '/templates/template.html' }, () => {
    Slot({ name: 'head' }, () => {
      Content('<title>My Page</title>')
    })
    Slot({ name: 'body' }, () => {
      Content('<h1>Hello</h1>')
    })
  })
})

Unnamed <[SLOT]> markers receive all non-Slot children of the Fragment.

Copy

Copy files and directories, applying template substitution to text files:

const jostraca = Jostraca({
  model: { title: 'My App' }
})

await jostraca.generate({ folder: './out' }, () => {
  Project({ folder: 'app' }, () => {
    Folder({ name: 'static' }, () => {
      Copy({ from: '/templates/assets' })
      Copy({ from: '/templates/readme.txt', to: 'README.txt' })
    })
  })
})

Inject

Update existing files by replacing content between markers:

// existing foo.txt contains:
// HEADER
// #--START--#
// old content
// #--END--#
// FOOTER

Project({}, () => {
  Inject({ name: 'foo.txt' }, () => {
    Content('new content')
  })
})
// Result: HEADER\n#--START--#\nnew content\n#--END--#\nFOOTER

Custom Components

Use cmp() to create reusable components:

import { cmp, Content, each } from 'jostraca'

const FunctionDef = cmp(function FunctionDef(props: any) {
  Content(`function ${props.name}(`)
  Content(props.params.join(', '))
  Content(') {\n')
  each(props.ctx$.model.body, (line) => Content(`  ${line}\n`))
  Content('}\n')
})

// Usage inside a File:
File({ name: 'utils.js' }, () => {
  FunctionDef({ name: 'greet', params: ['name'] })
})

Existing File Handling

Control how generated files interact with files that already exist:

await jostraca.generate({
  folder: './out',
  existing: {
    txt: {
      write: true,      // Overwrite existing files (default)
      preserve: true,    // Keep .old. backup of overwritten files
      present: false,    // Write to .new. instead of overwriting
      diff: false,       // Annotated 2-way diff
      merge: false,      // 3-way merge with conflict markers
    },
    bin: {
      write: true,
      preserve: false,
      present: false,
    }
  }
}, root)

Protected Files

Add # JOSTRACA_PROTECT to any generated file to prevent it from being overwritten on subsequent generations. This lets users safely edit generated files.

In-Memory Generation

Use mem and vol for testing or virtual file systems:

const jostraca = Jostraca({
  mem: true,
  vol: {
    '/templates/header.txt': 'HEADER\n'
  }
})

const result = await jostraca.generate({ folder: '/' }, root)

const files = result.vol().toJSON()
// { '/output.txt': '...' }

Result Object

generate() returns a JostracaResult:

{
  when: number,          // Timestamp of generation
  files: {
    written: string[],     // Files written to disk
    preserved: string[],   // Backup copies created
    presented: string[],   // .new. files created
    diffed: string[],      // Diff files created
    merged: string[],      // Merged files created
    conflicted: string[],  // Files with merge conflicts
    unchanged: string[],   // Files unchanged
  },
  audit: () => Audit[],   // Audit trail of operations
  vol?: () => any,         // Virtual volume (mem mode)
  fs?: () => FST,          // File system (mem mode)
}

Utility Functions

Jostraca exports several utility functions for working with names, templates, and data:

import {
  each,           // Iterate arrays/objects with marking and sorting
  get,            // Simple dot-path property access
  getx,           // Advanced path access with operators
  camelify,       // 'foo_bar' -> 'FooBar'
  snakify,        // 'FooBar' -> 'foo_bar'
  kebabify,       // 'FooBar' -> 'foo-bar'
  names,          // Generate all case variants of a name
  template,       // Process template strings with model data
  indent,         // Indent text content
  cmp,            // Create custom components
  deep,           // Deep merge objects
} from 'jostraca'

See REFERENCE.md for full details on every component, option, and utility function.

License

MIT. Copyright (c) Richard Rodger.

Go Port

A Go template utility port is available under go/, using github.com/rjrodger/shape/go from release go/v0.1.0.