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 🙏

© 2024 – Pkg Stats / Ryan Hefner

rimmel

v1.1.1

Published

Reactive template engine for webapps

Downloads

55

Readme

Rimmel.js

A Functional-Reactive templating library for the Rx.Observable Universe.

In RML, Promises and Observables are fist-class citizens, so whenever they emit, their output is rendered.

No need for JSX, React, Babel or Webpack. 100% pure JavaScript/TypeScript that just works out of the box.

Hello World!

The modern Hello World for reactive frameworks is a component with a button that counts how many times you click it.

To best illustrate reactivity in general we'll be using RxJS.

// This click-counter view-model is just a simple
// in-out RxJS stream
const counter = new BehaviorSubject(0).pipe(
	// Emits 0, 1, 2, 3, ...
	scan(a => a+1)
)

// The template simply references the observable stream in an
// intuitive way,so it's clear what goes in and what comes out.
document.body.innerHTML = rml`
  <button type="button" onclick="${counter}"> Click me </button>
  You clicked the button <span>${counter}</span> times.
`;

The onclick handler of the <button> element is wired into counter, which is a Subject, an in-out RxJS transform stream. Every time you click the button it emits a MouseEvent which is pumped into counter. Since it's a transform stream, what it does is take the input, transform it and re-emit the result. The result is then wired back to the DOM through a Sink.

Why Rimmel?

  • Functional-Reactive: you can treat nearly everything in the DOM as observable streams, which means a lot less code to write
  • Fast: Rimmel doesn't use any virtual DOM, so renders are around fast-enough for most use cases and updates can run at "vanilla+" speed in certain others
  • Super simple to start: no toolchain required, just click and change on Codepen
  • Powerful: when using Observable streams you can manage even the most complex state you can think of with grace, elegance and simplicity
  • Scalable: Scalability is an architectural feature. Bad architectures don't scale. Good ones do, and Rimmel will not be your bottleneck.

Playground: Stackblitz

Get Started

import { rml } from 'rimmel';

Sources and Sinks

There are two key concepts here: sources and sinks.

Sources are things that generate data, which you can optionally process/transform along the way. What remains goes somewhere. That somewhere is usually referred to as a sink.

Sources typically include any DOM events such as onclick or onmousemove, fetch() calls, just like promises in general, async functions and, most remarkably, Observables.

Sinks are most often the places where you want to display any information in your UI. Your main document, any state updates, notifications, console logs, etc.

With RML/Rimmel you can treat most DOM elements as sources, sinks, or both.

Stream Processing

Sources normally emit raw data, not meant to display in a UI (e.g.: a ScrollEvent or a MouseEvent), so what we do is to process and format them. This can include mapping, reducing, etc. RxJS comes with a comprehensive set of utility functions to transform data.

Supported Sources

Rimmel supports the following as observable sources:

  • Event listeners from DOM elements. Essentially any attribute beginning with "on" can be bound to an observable.
  • Anything else that can map to an Observable or a Promise. Websockets, Timers, fetch() calls, etc.
  • Static values will be simply treated as non-reactive values and no data-binding will be created.

Supported Sinks

Rimmel supports two types of sinks: render-time and dynamic sinks. Render-time sinks are the simplest and most intuitive ones: those you define in a template from which the data binding can be easily inferred. These include:

  • Attributes for any HTML element
  • Style attributes
  • Datasets (data- attributes)
  • innerHTML/innerText/textContent

Dynamic sinks enable apps to inject pretty much anything into them and the content will determine at runtime the correct bindings.

const mixin = () => {
  const onmouseover = () => console.log('mouseover')

  const onclick = new Subject()

  // emit 'clickable' first,
  // then 'clicked' afterwards
  const classes = onclick.pipe(
    mapTo('clicked-class'),
    startWith('clickable'),
  )

  return {
    onclick,
    onmouseover,
    'data-new-attribute': 'some value',
    class: classes,
  }
}

const component = () => {
  return rml`
    <div ...${mixin()}></div>
  `
}

When the above component is rendered on the page, the mixin will inject everything else into it, including the onclick and onmouseover event handlers, a statically defined data-new-attribute and a merge-in observable stream to set classes... dynamically! Essentially, whenever the classes stream emits, it will be able to set/change/toggle class names in the component. More details in the upcoming RDOM (Reactive DOM) documentation.

Building and testing

bun install
bun run build
bun test

Supported Environments

Our focus is modern EcmaScript code. SSR is in the making.

Current State

Rimmel is created and maintained by Hello Menu, is being used in production on an advanced and complex webapp and is now an independent spin-off project of its own.

It's not to be considered a "mature" web framework yet and it certainly has some minor bugs and gotchas, but it's ready for early adopters and FRP enthusiasts to create webapps with logic of any complexity. It becomes especially powerful if paired with RxJS or possibly with other Observable libraries.

Roadmap

  • Full ObservableArray support for lists, grids and complex repeatable data structures
  • Observable completion handlers
  • Observable error sinks
  • Performance benchmarks
  • Bug fixes
  • SSR
  • Support for Rx Schedulers for high-throughput highly-responsive real-time apps (trading front-ends, ad-tech, gaming, ...)
  • Support text node and HTML comment sinks
  • Tree-shakeable separation of sources and sinks
  • Support for the EventEmitter type as source and sink
  • Separate memory-optimised and speed-optimised sinks.
  • Compiled Templates
  • Plugin support
  • RML Security

Web Standards alignment

There are discussions going on around making HTML and/or the DOM natively support Observables at WHATWG DOM/544 and WICG Observable.

Rimmel.js intends to align with these efforts as they develop, mature and hopefully standardise.