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

carehtml

v0.1.5

Published

Templates with automatic registration of Custom Elements.

Downloads

23

Readme

carehtml (Custom Auto Registered Elements HTML)

Templates with automatic registration of Custom Elements.

const template = html`<${CustomElement}></${CustomElement}>`;

Inspired by JSX in general and htm in particular.

Motivation

There are 2 main reasons Web Components need something like this:

  1. Lack of scoping when registering Custom Elements which creates issues in tests and makes it impossible to have 2 different components with the same name.
  2. Inability to have 2 different versions of the same Custom Element when refactoring from an old to a new version, especially when having nested node modules.

Usage with Lit

You need to wrap the Lit html tag:

import { LitElement, html as litHtml } from 'lit';
import takeCareOf from 'carehtml';

const html = takeCareOf(litHtml);

class MySearchBar extends LitElement {
  render() {
    return html`
      <${MyInput} name="query"></${MyInput}>
      <${MyButton}>
        <${MyIcon} icon="search"></${MyIcon}>
        Search
      </${MyButton}>
    `;
  }
}

Wrapping is extra work which might seem unnecessary in the user code, but that allows to decouple carehtml from lit, primarily in terms of npm dependencies. This allows to use carehtml with any version of lit and develop carehtml with its independent release cycle.

Usage with Other Templating Libraries Based on Tagged Templates

In fact it can work with any other templating library which relies on tagged templates. For example with htm allowing to mix Custom Element classes with Preact components (and any other components supported by htm including React ones).

This is an example taken from the htm docs with one change: instead of simple <button> there is a Custom Element based Button from new Material Web Components.

import htm from 'htm';
import { h, Component, render } from 'preact';
import { Button } from '@material/mwc-button';
import takeCareOf from 'carehtml';

const html = takeCareOf(htm.bind(h));

class App extends Component {
  addTodo() {
    const { todos = [] } = this.state;
    this.setState({ todos: todos.concat(`Item ${todos.length}`) });
  }
  render({ page }, { todos = [] }) {
    return html`
      <div class="app">
        <${Header} name="ToDo's (${page})" />
        <ul>
          ${todos.map((todo) => html`<li>${todo}</li>`)}
        </ul>
        <${Button} onClick=${this.addTodo.bind(this)}>Add Todo</${Button}>
        <${Footer}>footer content here<//>
      </div>
    `;
  }
}

render(html`<${App} page="All" />`, document.body);

Usage in Tests

import { html as litHtml, render } from 'lit';
import takeCareOf from 'carehtml';

const html = takeCareOf(litHtml);

describe('MyMixin', () => {
  it('does something', () => {
    class MyElement extends MyMixin(HTMLElement) {
      // define extra behavior
    }

    // create fixture
    // (html`` in this context returns TemplateResult as if it was Lit itself)
    const element = fixture(html`<${MyElement}></${MyElement}>`);

    // test mixin/element behavior
  });
});

function fixture(litTemplate) {
  // please use smth like this in real life
  // https://open-wc.org/recommendations/testing-helpers.html#test-a-custom-element-with-properties
  const wrapper = document.createElement('div');
  render(litTemplate, wrapper);
  document.body.appendChild(wrapper);
  return wrapper.children[0];
}

Benchmarks

Runtime performance is not the key requirement for carehtml since the end goal is to compile the code and have static and still unique tag names in the production code. But some numbers might be interesting to show the impact of such solution on local development and the potential runtime usage in production for projects that want to stay compilation-free.

There are 2 things which carehtml can slow down and which can be measured: creating a template and rendering a template. Both can be measured together as well.

The original idea was that the benchmarks need to compare the most minimalistic template possible, e.g. <my-element></my-element> where MyElement does not render any internal template, otherwise the benchmarks will measure the DOM update caused by the internal template instead of the carehtml overhead. It turned out to be quite difficult to see the carehtml impact in such benchmarks, because it's insignificant as compared to even rendering such a minimalistic <my-element></my-element> template. You can play around with this by using yarn bench:create-and-render:chrome script and alike and modifying the benchmarks/index.html to your needs, e.g. by removing the constructors of the measured elements.

The only thing that makes sense to measure in this situation is the rerendering. The idea is to check if it does not rerender unnecessarily second time when the classes stay the same meaning that the actual template is also the same. That's what makes Lit so fast after all and carehml should not break this essential optimisation. In such benchmarks the <my-element></my-element> should have an internal template which will take most of the time of each render, so that the rerendering (if it happens) is close to being 2 times slower due to that internal template being rendered again. The end setup has MyElement with a shadow root with 100000 divs containing some text. The script yarn bench:create-and-render-twice can be used to measure that. The goal is to have the same numbers when using Lit html directly or wrapped with carehtml.

These are the results for Chrome which clearly show no overhead on rerendering when wrapping with carehtml:

| Benchmark | Avg time | vs direct | vs wrapped | vs wrapped with classes | | -------------------- | ----------------: | ---------------------------------------: | ---------------------------------------: | ---------------------------------------: | | direct | 52.30ms - 53.12ms | - | unsure-2% - +1%-0.99ms - +0.44ms | unsure-2% - +0%-1.08ms - +0.04ms | | wrapped | 52.40ms - 53.57ms | unsure-1% - +2%-0.44ms - +0.99ms | - | unsure-2% - +1%-0.94ms - +0.46ms | | wrapped with classes | 52.85ms - 53.61ms | unsure-0% - +2%-0.04ms - +1.08ms | unsure-1% - +2%-0.46ms - +0.94ms | - |

Measurements in other browsers are similar.

Special Thanks

BrowserStack

For their awesome cross-browser testing automation solution!