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

@reflex/dominion

v0.0.1

Published

Library for describing & applying DOM changes

Downloads

24

Readme

DOMinion

travis package downloads styled with prettier

Library for describing & applying DOM changes with a primary emphasys of moving DOM UI work off the UI thread. Library provides Virtual DOM abstraction that application could use to in the web worker to describe the UI state. Versions of Virtual DOM can be diffed to produce a changelist encoded in efficient format like UintArray8 that is transferable to a UI thread where changlist can be then applied to an actual DOM tree. Library is designed to support use case without Virtual DOM abstraction (like Ember) by allowing direct authoring of DOM changelists that again can be encoded into UintArray8 in the web worker and then applied back on the main thread.

Current State

At the moment library reached milestone where it can be put out there for feedback. Test coverage is shameful, so expect bugs. In fact main reasons to publish it this early is to get help with that.

There is one big piece missing to make this library useful - Event Handling. Which is quite a challenge both from design and implementation standpoint & one of the major humps to moving UI off the main thread. Plan is to utilize decoder.flow for describing "event decoders" that declaratively can describe what data to extract from DOM (event) when it occurs that can be encoded in the same efficent format as changelist and passed over to the worker (where appliaction will run) to update state & UI as necessary. And yes sometimes you'd need to make certain decisions in the UI thread like cancelling events and decoders could do that too by decoding to value like {message:ArrayBuffer, preventDefault?:boolean, stopPropagation?:boolean}. There still might be some cases where this is not enough and those likely will get a solutions optimized case by case.

Usage

Import

Rest of the the document & provided code examples assumes that library is installed (with yarn or npm) and imported as follows:

import * as DOMinion from "dominion"

Virtual DOM

Virtual DOM API assumes some host DOM element to which changes will be applied. Library provides DOMinion.createHost function to create virtual presentation of that host element.

const v1 = DOMinion.createHost()

It is possibel to describe changes to the host attributes and children but it is not possible to change a host element itself (say from div to span).

const v2 = DOMinion.createHost(
  [],
  [
    DOMinion.createElement(
      "div",
      [
        DOMinion.setAttribute("id", "main"),
        DOMinion.property("autofocus", true),
        DOMinion.style({
          backgroundColor: "red",
          color: "white"
        })
      ],
      [
        DOMinion.createTextNode("hi there"),
        DOMinion.createComment("this is some comment"),
        DOMinion.createElementNS("http://www.w3.org/2000/svg", "circle", [
          DOMinion.setAttribute("cx", "40"),
          DOMinion.setAttribute("cy", "50"),
          DOMinion.setAttribute("r", "26")
        ])
      ]
    )
  ]
)

Changelist can be calculated by running a diff between version of the tree and encoded into efficient representation. Please note that diff-ing and encoding happens in a single iteration and avoids additional allocations to what encoder uses. Mostly we'll focuse on FlatBuffer encoder that uses Google's flatbuffer library to effiently encode changelist into Uint8Array.

const changeList1 = DOMinion.diff(v1, v2)
const buffer1 = DOMinion.FlatBuffer.encode(changeList1)
if (!buffer1.isError) {
    self.postMessage({type:'changeList', changeList:buffer1} [buffer1])
}

Similarily changelists can be decoded and applied to the host DOM element in single iteration and avoiding additional alloactions.

const host = DOMinion.mount(document.body)
worker.onmessage = (event) => {
  const {changeList} = event.data
  DOMinion.patch(host, FlatBuffer.decode(changeList))
}

ChangeList without Virtual DOM

As mentioned you don't have to use Virtual DOM abstraction to encode / apply changes you could use lower-lever API to encode changes directly:

import { flatbuffers } from "flatbuffers"
const ChangeList = DOMinion.FlatBuffer.Encoder

const changeList = ChangeList.encoder()

ChangeList.selectChildren(changeList)
ChangeList.insertElement(changeList, 'div')
ChangeList.removeNextSibling(changeList, 1)
ChangeList.setAttribute(changeList, 'id', 'main')
ChangeList.selectChildren(changeList)
ChangeList.insertElement(changeList, 'p')
// ...

const buffer = ChangeList.toUint8Array(changeList)
self.postMessage({type:'changeList', changeList:buffer} [buffer])

On the UI thread things would be same as in previously.

Direct encoding

It is certainly possibly to avoid FlatBuffer.decode(FlatBuffer.encode(changeList)) and do directly DOMinion.patch(host, changeList) which would diff and patch in the same cycle but then emphasis is to move move things off UI thread and if you do you'd need to transfer changeList efficiently across threads and that's what plugable encode / decode allows you to do. You could in fact implement custom encoder that would even stream changeList as it's being written but that's not necesserily good idea either is application of changes will no longer be atomic.

Any more crazy ideas ?

In fact Yes! Representing DOM via Uint8Array has some interesting implications, for instance other languages (think WASM) could represent and change DOM on a separate thread and use just the patch from this library to change actual DOM. In fact flatbuffer library is going to do the half of the work there in supporting other languages. Finally @wycats has made whatwg proposal to add applicaton of changelists part of DOM API and if that happens patch will be natively supported.

Install

npm install dominion