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

@webfeet/reflect

v0.1.0

Published

Helper classes implementing HTML attribute reflection rules for use in custom elements

Readme

@webfeet/reflect

Implements the HTML attribute reflection rules

TODO

Currently, reflection is implemented for IDL attributes of type DOMString, DOMString?, USVString representing an URL, boolean, long, unsigned long, double, element, and frozen array of elements are implemented, no DOMTokenList (technically impossible, but could be approximated).

API

This package exports constants and functions whose name starts with reflect and whose value or the value they return, collectively known as reflectors, is an object with three functions and one constant:

  • fromAttribute takes a string or null as its single argument, coming directly from an HTML attribute, and implements the getter steps of the reflection rules, returning a value of the appropriate type
  • coerceValue takes any value as its single argument, and coerces it to the appropriate type following the WebIDL rules; it's generally directly one of the coerceTo functions exported by @webfeet/webidl
  • setAttribute takes three arguments: the HTML element, the attribute name, and the attribute's value (of the same type as returned by coerceValue), and implements the setter steps of the reflection rules
  • defaultValue is the default value one should initialize a property to behave as if an element had no HTML attribute (in other words, this is the value that will be returned by fromAttribute(null))

Those functions aren't methods of the returned object, they don't rely on their this, so they can be stored in variables and then directly called.

Enumerated attributes (and nullable enumerated attributes) take options as properties of an object passed to the exported functions:

  • keywords is the list of canonical keywords (the ones that can be returned from fromAttribute)
  • aliases is an object mapping a non-canonical keyword to a canonical keyword
  • missing represents the missing default value, that will be the defaultValue and will be returned by fromAttribute(null)
  • invalid represents the invalid default value, that will be returned by fromAttribute when the value doesn't match any known keyword (canonical or alias)

Only the keywords option is mandatory, all others are optional.

Numbers take options as properties of an object passed to the exported function. Most numbers only take a single, optional, option: defaultValue, the default value used when the attribute is absent or cannot be parsed to a number.

Clamped integers also take two mandatory options in addition to the optional defaultValue: min and max represent the bounds of the range the value is clamped to.

The reflectURL constant is slightly different from all the others, as it doesn't have a defaultValue and its fromAttribute takes two arguments: the HTML element, and then a string or null as the value coming from the HTML attribute. This is because the value of a USVString attribute representing a URL relies on the document's base URL, which can change at any time.

Finally, reflectElementReference and reflectElementReferences are factories of stateful reflectors, whose API is a bit different from the other stateless reflectors. The factory function itself takes the element as the first argument, and optionally an element type (e.g. HTMLDivElement) to filter the elements referenced by ID from the attribute. If not given, the second argument defaults to Element. The returned stateful reflector is an object with internal state and 4 functions:

  • get takes no argument, and implements the getter steps of the reflection rules, computing and returning the referenced element(s). For reflectElementReference, the returned value is either an element of the type passed to the constructor, or null. For the reflectElementReferences, the value is a frozen array of elements of the type passed to the constructor, or null.
  • fromAttribute takes a string or null as its single argument, coming directly from an HTML attribute, and updates the reflector's internal state, not returning anything.
  • coerceValue takes any value as its single argument, and coerces it to the appropriate type (same as get).
  • setAttribute takes two arguments: the attribute name, and the attribute's value (of the same type as returned by coerceValue), and implements the setter steps of the reflection rules.

A property backed by reflectElementReference should have its name suffixed with Element (relative to the HTML attribute name). A property backed by reflectElementReferences should have its name suffixed with Elements.

Usage

The (stateless) reflectors are meant to be used in two possible ways (except for reflectURL):

  • either applying the getter steps in the property getter, as specificied in HTML

    class MyElement extends HTMLElement {
      get prop() {
        return reflector.fromAttribute(this.getAttribute("prop"));
      }
      set prop(value) {
        value = reflector.coerceValue(value);
        reflector.setAttribute(this, "prop", value);
      }
    }
  • or applying the getter steps whenever the attribute changes, and caching the result (this approach is not compatible with reflectURL)

    class MyElement extends HTMLElement {
      #prop = reflector.defaultValue;
    
      static observedAttributes = ["prop"];
    
      attributeChangedCallback(name, oldValue, newValue) {
        // in this case, we know 'name' is necessarily 'prop' so we can skip any check
        this.#prop = reflector.fromAttribute(newValue);
      }
      get prop() {
        return this.#prop;
      }
      set prop(value) {
        value = reflector.coerceValue(value);
        reflector.setAttribute(this, "prop", value);
      }
    }

The reason the coerceValue and setAttribute are separated is to allow for custom steps to be added in between. coerceValue should always be the first thing called in the setter to coerce the input value, and setAttribute will most likely always be the last.

The stateful reflectors however require the use of attributeChangedCallback:

class MyElement extends HTMLElement {
  #prop = reflectElementReference(this);

  static observedAttributes = ["prop"];

  attributeChangedCallback(name, oldValue, newValue) {
    // in this case, we know 'name' is necessarily 'prop' so we can skip any check
    this.#prop.fromAttribute(newValue);
  }
  get propElement() {
    return this.#prop.get();
  }
  set propElement(value) {
    value = this.#prop.coerceValue(value);
    this.#prop.setAttribute("prop", value);
  }
}