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

elvish

v1.1.6

Published

<img src='logo.png' />

Downloads

33

Readme

A functional, reactive and some what type safe javascript library to build UIs

Elvish is heavily inspired from elm-architecture, it is an effort to implement the type safe and functional architecture that elm provides in a non typed language like javascript.

Getting Started

The logic of every Elvish program will break up into three cleanly separated parts:

  • Model — the state of your application
  • Update — a way to update your state
  • View — a way to view your state as HTML

Counter Example

Here is a Code Sandbox

import run from 'elvish';
import { onClick, Text, div, button, span } from "elvish";
import { Union, Record } from 'elvish'

const Model = Record({
  counter: Number
})

// init :: Model
const Init = Model.create(0)

// Msg = Increment | Decrement
const Msg = Union({
  Increment: [],
  Decrement: []
});

// update :: Model -> Msg -> Model
const Update = model => Msg.case({
  Increment: () => ({ ...model, counter: model.counter + 1 }),
  Decrement: () => ({ ...model, counter: model.counter - 1 }),
  _: () => model
})

// view :: Model -> Html Msg
const View = ({ counter }) =>
  div([], [
    button([ onClick(Msg.Increment) ], ["+"]),
    span([], [ Text(counter) ]),
    button([ onClick(Msg.Decrement) ], ["-"])
  ])

// Run
const Root = document.getElementById("root");

run({
  View,
  Update,
  Init,
  Root
});

Side Effect example

Here is a Code Sandbox

import run from "elvish";
import { div, button, h3, onClick, Text } from 'elvish'
import { Union, Record, Effect } from 'elvish'

const Model = Record({
  userId: Number,
  title: String,
  fetched: Boolean,
  url: String
})

// init :: Model
const Init = Model.create(0)('')(false)('https://jsonplaceholder.typicode.com/todos/1')

// Msg :: FetchData | FetchedData | FetchError
const Msg = Union({
  FetchData: [],
  FetchedData: [Number, String],
  FetchError: [String]
});

// getJson :: String -> Effect err { number, string }
const getJson = url => Effect((reject, resolve) =>
  fetch(url)
    .then(x => !x.ok ? reject('Error') : x.json())
    .then(resolve)
)

// fetchUserData :: String -> Effect FetchError FetchedData
const fetchUserData = url =>
  Effect.of(url)
    .chain(getJson)
    .bimap(err => Msg.FetchError(err), ({ userId, title }) => Msg.FetchedData(userId, title))

// update :: Model -> Msg -> Model | (Model, Effect)
const Update = model => Msg.case({
  FetchData: () => [model, fetchUserData(model.url)],
  FetchedData: (userId, title) => ({ ...model, userId, title, fetched: true }),
  FetchError: err => ({ ...model, title: err, fetched: true }),
  _: _ => model
})

// view :: Model -> Html Msg
const View = ({ userId, title, fetched }) =>
  div([],
    [
      button([onClick(Msg.FetchData)], [Text('Fetch Data')]),
      fetched && div([], [
        h3([], [Text(userId)]),
        h3([], [Text(title)])
      ])
    ]
  );


// Run
const Root = document.getElementById("root");

run({
  View,
  Update,
  Init,
  Root
});

Performance

Elvish uses Inferno as its Vdom which makes it insanely fast, even faster than React and Elm itself.

Here is the jS-benchmark

Types

Record

  • Example
  const Model = Record({
    counter: Number,
    text: String
  }) // Returns a type constructor Model(counter, text)

  const model = Model.create(0, 'hello world') // create new Model

  console.log(model.counter) // 0
  console.log(model.text) // hello world
  • Type safe
  const Model = Record({
    counter: Number
  })

  const model = Model.create('0') // wrong type passed to record

Union Type

Union types are an important part of an elvish application.

  • Example
  const Bool = Union({
    True: [],
    False: []
  })
  • Adding instance methods to union types.
  const T = () => true

  const Maybe = Union({
    Just: [T],
    Nothing: []
  })
  
  Maybe.map = function(fn) {
    return Maybe.case({
      Nothing: () => Maybe.Nothing,
      Just: (v) => Maybe.Just(fn(v))
    }, this);
  };

  const just = Maybe.Just(1)
  const nothing = Maybe.Nothing
  nothing.map(add(1)); // => Nothing
  just.map(add(1)); // => Just(2)
  • Pattern Matching
  const Action = Union({
    ShowNumber: [Number],
    HideNumber: []
  })

  const initState = {
    number: 0,
    showNumber: false
  }

  const reducer = (initState, action) =>
    Action.case({
      ShowNumber: number => ({ showNumber: true, number }),
      HideNumber: _ => ({ showNumber: false, number: 0 })
      _: () => initState,
    }, action)

Effect type

Effect types are used to run side effects in an elvish application. Effects is a structure for time-dependent values.

Basically the idea is to abstract the side effects from your pure code and running them separately, so your code stays pure even when you are doing side effects.

You can think of an elvish application as pure and functional code running in an imperative shell.

  • Example
  const executeEffect = number =>
    Effect.of(number)
      .map(x => x + 10)

Only way to run Effects in an elvish application is to return a tuple from the update function containing [ Model, Effect ].

  ExecuteEffect: () => [model, executeEffect(model.number)],

Instances


  // Constructing
  Effect.of(value)
  //  b -> Effect(a, b)
  // Constructs a new Effect containing the given successful value.

  Effect.rejected(value)
  // a -> Effect(a, b)
  // Constructs a new Effect containing the given failure value.

  Effect.empty()
  // () -> Effect(a, b)
  // Constructs a Effect that will never resolve.

  // Transforming
  Effect.map(transformation)
  // Effect(a, b) => (b -> c) -> Effect(a, c)
  // Transforms the successful value of the Effect using a regular unary function.

  Effect.chain(transformation)
  // Effect(a, b) => (b -> Effect(a, c)) -> Effect(a, c)
  // Transforms the succesful value of the Effect using a function over monads.

  Effect.orElse(transformation)
  // Effect(a, b) => (a -> Effect(c, b)) -> Effect(c, b)
  // Transforms the failure value of the Effect into a new Effect.

  Effect.fold(onRejection, onSucecss)
  // Effect(a, b) => (a -> c), (b -> c) -> Effect(d, c)
  // Applies a function to each side of the Effect.

  Effect.cata(pattern)
  // Effect(a, b) => { Rejected: a -> c, Resolved: b -> c } → Effect(d, c)
  // Applies a function to each side of the Effect.

  Effect.bimap(onRejection, onSuccess)
  // Effect(a, b) => (a -> c), (b -> d) -> Effect(c, d)
  // Maps both sides of the Effect.