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 🙏

© 2025 – Pkg Stats / Ryan Hefner

rip-lang

v1.5.7

Published

A lightweight scripting language that compiles to modern JavaScript

Readme


What is Rip?

A clean-room CoffeeScript-inspired compiler that produces modern JavaScript (ES2022). Built from scratch with an elegant S-expression architecture.

Key differentiators:

  • 🎯 Zero dependencies - Completely standalone (includes its own parser generator)
  • 🚀 Self-hosting - Rip compiles itself (bun run parser works!)
  • ~50% smaller than CoffeeScript (9,839 LOC vs 17,760 LOC)
  • 🎨 Modern output - ES2022 with classes, modules, optional chaining
  • Production-ready - 968/968 tests passing (100%)

Status

Version 1.5.7 - PRODUCTION READY 🚀

Quality metrics:

  • 968/968 tests passing (100% coverage)
  • Self-hosting - Rip compiles itself, including its own parser generator
  • Zero dependencies - Completely standalone, no npm packages required
  • 9,839 LOC - Lean, maintainable codebase (~50% smaller than CoffeeScript)

Complete feature set:

  • 110+ node types - All language constructs fully implemented
  • Interactive REPL - Terminal, browser, and console modes
  • Live playground - Full-featured browser environment with code editor
  • 43KB bundle - Brotli-compressed, includes compiler + REPL

Architecture strengths:

  • Dispatch table - O(1) lookup for all 110 operations
  • S-expression IR - Simple, clean intermediate representation
  • Fast compilation - ~200ms parser regeneration (self-hosting)
  • Modern output - Clean ES2022 code generation

Quick Example

# Async with dammit operator! (call and await)
fetchUser = (id) => fetch! "/api/user/${id}"

# Ruby-style regex with =~ operator
def parseEmail(input)
  return unless input =~ /^(\w+)@([\w.]+)$/
  { user: _[1], domain: _[2] }  # _ captures match groups

# Otherwise operator (!?) - undefined-only coalescing
timeout = config.timeout !? 5000  # null/0/false are valid!

result = parseEmail "[email protected]"

Compiles to clean ES2022:

let _;

const fetchUser = async (id) => await fetch(`/api/user/${id}`);
function parseEmail(input) {
  if (!(_ = toSearchable(input).match(/^(\w+)@([\w.]+)$/))) return;
  return {user: _[1], domain: _[2]};
};
const timeout = (config.timeout !== undefined ? config.timeout : 5000);
const result = parseEmail("[email protected]");

Improvements Over CoffeeScript

Rip includes all of CoffeeScript's beloved features plus modern enhancements:

Unique to Rip

| Feature | Syntax | Benefit | |---------|--------|---------| | Dammit operator | fetchData!await fetchData() | Call and await in one | | Otherwise operator | val !? default | Undefined-only coalescing (null/false/0 are valid) | | Void functions | def process! | Suppress implicit returns | | Traditional ternary | x > 0 ? 'pos' : 'neg' | JavaScript-style (plus CoffeeScript's if/then/else) | | Ruby-style regex | str =~ /pattern/, _[1] | Match with capture, inline extraction | | Heregex | ///pattern # comment/// | Extended regex (CoffeeScript deprecated it) | | Smart heredoc margins | Closing ''' column = left margin | Visual alignment - position delimiter to set baseline! | | 10 optional operators | obj?.prop + arr?[0] | Both ES6 and CoffeeScript styles work! | | DATA marker | __DATA__\nconfig... | Ruby-style inline data sections |

Modern JavaScript Output

| Feature | CoffeeScript | Rip | |---------|-------------|-----| | Classes | ES5 prototypes | ES6 native classes | | Modules | CommonJS | ES6 import/export | | Nullish operators | x ? y | x ?? y and ??= (ES2020) | | Optional chaining | Transpiled soak | Native ?. (ES2020) | | Spread syntax | Postfix args... | Prefix ...args (ES6) |

Smarter Compilation

| Feature | CoffeeScript | Rip | |---------|-------------|-----| | Comprehensions | Always IIFE | Context-aware (plain loop when result unused) | | Async/generators | Manual syntax | Auto-detected | | Switch statements | switch (false) pattern | Clean if/else chains |

Compatibility Features

Both syntaxes work! Rip auto-converts CoffeeScript style:

  • args......args (prefix/postfix spread)
  • x ? yx ?? y (legacy existential, unless ternary)
  • Seamless migration from CoffeeScript! 🎉

Why Choose Rip?

For Users

  • Elegant syntax - CoffeeScript's readability without the quirks
  • Modern output - ES2022 with native classes, modules, optional chaining
  • Zero complexity - No build tools, no dependency hell
  • Unique features - Dammit operator, otherwise operator, Ruby regex
  • Browser ready - 43KB bundle with REPL included

For Developers

  • Simple architecture - S-expressions beat complex AST classes
  • Easy to extend - Add a case, run tests, done!
  • Well-tested - 968/968 tests (100% coverage)
  • Well-documented - Complete guides (AGENT.md is gold!)
  • Self-hosting - Rip compiles itself (including parser generator)

Philosophy

Simplicity scales.

Keep the IR simple (s-expressions), keep the pipeline clear (lex → parse → generate), keep the code minimal (pattern matching). Test everything.


Installation

Option 1: Install Globally (Recommended)

# Install Bun if needed
curl -fsSL https://bun.sh/install | bash

# Install Rip
bun add -g rip-lang

# Start using it!
rip                    # Interactive REPL
rip yourfile.rip       # Compile a file
bun yourfile.rip       # Execute directly

Option 2: Clone from Source

git clone https://github.com/shreeve/rip-lang.git
cd rip-lang

# Link globally
bun link

# Add to global Bun config
echo 'preload = ["rip-lang/loader"]' >> ~/.bunfig.toml

# Run .rip files from anywhere
bun your-script.rip

Quick Start

# Run code
./bin/rip                                  # Interactive REPL
./bin/rip examples/fibonacci.rip           # Execute file
bun examples/fibonacci.rip                 # Direct execution (with loader)

# Compile
./bin/rip -c examples/fibonacci.rip        # Output JavaScript
./bin/rip -o output.js input.rip           # Save to file

# Debug / Inspect
./bin/rip -s input.rip                     # Show s-expressions (parser output)
./bin/rip -t input.rip                     # Show tokens (lexer output)
./bin/rip -s -c input.rip                  # Show both
echo 'x = 42' | ./bin/rip -s               # Pipe from stdin

# Build
bun run parser                             # Rebuild parser (self-hosting!)
bun run browser                            # Build browser bundle
bun run test                               # Run all 968 tests

Key Features

Elegant Syntax

# Functions (three styles)
def greet(name)              # Named, hoisted
  "Hello, ${name}!"

calculate = (a, b) ->        # Thin arrow (unbound this)
  a + b

handler = (event) =>         # Fat arrow (bound this)
  @process event

# Comprehensions (context-aware!)
squares = (x * x for x in [1..10])     # IIFE (result used)

processItem x for x in items           # Plain loop (result unused)

Modern JavaScript Features

# Destructuring
{name, age} = person
[first, ...rest] = array

# Optional chaining (dual syntax)
user?.profile?.name          # ES6 native
arr?[0]                      # CoffeeScript soak
fn?(arg)                     # Soak call

# Nullish coalescing
port = config.port ?? 8080

# Async/await auto-detection
def fetchData
  data = await fetch "/api/data"
  data.json()
# → async function fetchData() { ... }

Unique Features

# Dammit operator! - Call and await
result = fetchData!         # → await fetchData()
user = getUser!(id)         # → await getUser(id)

# Otherwise operator (!?) - Undefined-only coalescing
timeout = config.timeout !? 5000   # null/0/false are valid!
name = user.name !? 'Guest'        # Only defaults on undefined

# Void functions - No implicit returns
def process!                # Always returns undefined
  doWork()
  # No return value

# Ruby-style regex
email =~ /(.+)@(.+)/              # Match with _ capture
username = _[1]                   # Extract first group
domain = email[/@(.+)/, 1]        # Inline extraction (group 1)
suffix = name[/,\s*([js]r|i{1,3})\b/, 1]  # Complex pattern extraction

# Heregex - Extended regex with comments
pattern = ///
  ^ \d+      # starts with digits
  \s*        # optional whitespace
  [a-z]+     # followed by letters
  $
///i

# Heredocs - Smart visual indentation control
# When closing ''' or """ is preceded only by whitespace,
# its column position becomes the "left margin" for all content.
# This makes it easy to visually align content!

code = '''
  if (x) {
    return y;
  }
  '''                        # Closing at column 2 → strips 2 spaces from all lines
# Output: "if (x) {\n  return y;\n}"

code = '''
  if (x) {
    return y;
  }
'''                          # Closing at column 0 → preserves all indentation
# Output: "  if (x) {\n    return y;\n  }"

# Perfect for code generation - align the closing delimiter where you want!
template = """
    function ${name}() {
      console.log("${message}");
    }
    """                      # Closing at column 4 → baseline is column 4
# Output: "function ${name}() {\n  console.log(\"${message}\");\n}"

# Traditional ternary (plus CoffeeScript's if/then/else)
result = x > 0 ? 'positive' : 'negative'

# __DATA__ marker (Ruby-inspired)
config = parseConfig(DATA)

__DATA__
host=localhost
port=8080

Why S-Expressions?

Traditional compilers use complex AST classes. Rip uses simple arrays:

Rip Source → Lexer → Parser → S-Expressions → Codegen → JavaScript
            (3,145)  (340)    ["=", "x", 42]  (5,246)    (ES2022)

Traditional AST approach:

class BinaryOp {
  constructor(op, left, right) { ... }
  compile() { /* 50+ lines */ }
}

Rip's S-expression approach:

case '+': {
  const [left, right] = rest;
  return `(${this.generate(left)} + ${this.generate(right)})`;
}

Result: ~50% smaller compiler

| Component | CoffeeScript | Rip | Savings | |-----------|--------------|-----|---------| | Lexer+Rewriter | 3,558 LOC | 3,145 LOC | -11% | | Parser Generator | 2,285 LOC (Jison) | 928 LOC (Solar, built-in) | -59% | | Compiler | 10,346 LOC (AST) | 5,246 LOC (S-expr) | -49% | | Total | 17,760 LOC | 9,839 LOC | -45% |


Zero Dependencies

Rip is completely standalone - no runtime or build dependencies:

{
  "dependencies": {}    // ← Empty!
}

What's included:

  • ✅ Full compiler (lexer, parser, codegen)
  • ✅ Parser generator (solar.rip - SLR(1))
  • ✅ Triple REPL (terminal, browser, console)
  • ✅ Browser bundle (43KB compressed)
  • ✅ Test framework

Self-hosting verification:

bun run parser    # Rebuilds parser from scratch (solar.rip + grammar.rip)
# Complete bootstrap loop - ZERO external tools! ✅

Browser Support

Run Rip in the browser! Try it live: https://shreeve.github.io/rip-lang/

# Build browser bundle
bun run browser

# Start dev server
bun run serve       # → http://localhost:3000

Features:

  • 43KB bundle (brotli-compressed)
  • Interactive REPL console
  • Live compiler with syntax highlighting
  • Inline <script type="text/rip"> support
<script src="https://cdn.example.com/rip.browser.min.js"></script>
<script type="text/rip">
  def greet(name)
    console.log "Hello, ${name}!"
  greet "World"
</script>

See docs/BROWSER.md for details.


Runtime Compatibility

Primary targets:

  • 🎯 Bun - First-class support with automatic .rip loader
  • 🌐 Browsers - 43KB bundle with REPL

Also supported:

  • Deno - ES2022 output works natively
  • Node.js 12+ - Full compatibility

ES2022 features used:

  • ES2015: classes, let/const, arrow functions, template literals, destructuring
  • ES2018: async iteration (for await...of)
  • ES2020: optional chaining (?.), nullish coalescing (??)
  • ES2022: static class fields, top-level await

Documentation

For users:

Technical references:

For contributors:


Development

Running Tests

# All tests
bun run test

# Specific file
bun test/runner.js test/rip/functions.rip

# Clear cache
bun --no-cache test/runner.js test/rip

Build Commands

bun run parser    # Rebuild parser from grammar (self-hosting!)
bun run browser   # Build 43KB browser bundle
bun run serve     # Start dev server (REPL at localhost:3000)

Project Structure

rip/
├── src/
│   ├── lexer.js         # CoffeeScript lexer (adapted)
│   ├── parser.js        # Solar parser (generated)
│   ├── codegen.js       # Code generator (S-expression dispatch)
│   ├── compiler.js      # Pipeline orchestration
│   └── grammar/
│       ├── grammar.rip  # Grammar specification
│       └── solar.rip    # Parser generator
├── docs/                # Complete documentation
├── test/rip/            # 23 test files, 968 tests
├── AGENT.md             # Complete developer guide
└── README.md            # This file

Contributing

  1. Read AGENT.md - Complete guide for developers and AI agents
  2. Check CONTRIBUTING.md - Workflow with examples
  3. Write tests first (test-driven development)
  4. Run bun run test before committing
  5. Follow existing patterns in docs/CODEGEN.md

Quick workflow:

# 1. Check what parser emits
echo 'your code' | ./bin/rip -s

# 2. Implement in src/codegen.js (check dispatch table)
# 3. Write tests in test/rip/

# 4. Run tests
bun test/runner.js test/rip/your-test.rip

# 5. Verify all tests pass
bun run test

Comparison to CoffeeScript

| Feature | CoffeeScript | Rip | |---------|-------------|------| | Implementation | 17,760 LOC | 9,839 LOC (~50% smaller) | | Dependencies | Multiple | ZERO | | Parser Generator | External (Jison) | Built-in (solar.rip) | | Self-Hosting | No | Yes | | Output | ES5 (var, prototypes) | ES2022 (let, classes) | | Modules | CommonJS | ES6 | | Maintenance | Complex AST | Simple S-expressions |

Real-world output comparison:

Compiling a 400-line CoffeeScript file with classes, nested switches, and loops:

| Metric | CoffeeScript | Rip | |--------|--------------|-----| | Lines of code | 608 lines | 304 lines (50% smaller) | | Syntax | ES5 (var, prototypes) | ES2022 (let, classes) | | Readability | Verbose with intermediate vars | Clean and direct |

Both are functionally equivalent - Rip just generates cleaner code!


License

MIT


Credits

Inspired by:

  • CoffeeScript - Syntax and lexer foundation
  • Lisp/Scheme - S-expression approach
  • Solar - Lightning-fast parser generator (80ms vs Jison's 12.5s!)
  • Ruby - Regex operators, DATA marker

Built by: Developers who believe simplicity scales

Powered by: Bun - The fast all-in-one JavaScript runtime


Start simple. Build incrementally. Ship elegantly.