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

@atjson/renderer-hir

v0.26.2

Published

Generalized tools for generating documents in a hierarchical fashion

Downloads

4,062

Readme

🌳 @atjson/renderer-hir

This renderer is designed to render out to hierarchical formats like HTML, markdown, and the DOM.

Renderers use generators to return the content around annotations so each annotation has contextual awareness of what's going on. Render functions are keyed on their annotation type (or the classified type). In addition, there are 3 special functions that can be overridden:

  • *renderAnnotation, which will be called for every annotation (even unknown annotations)
  • *root, which is the root node of the document being rendered. Overriding this allows for output to be fixed accordingly (for example, joining the yielded contents when the string output is expected to be a string)
  • text, which will be called for all text nodes.

🖍 Writing a Renderer

Let's write a renderer for Slack! Slack has a simple message format that looks somewhat like markdown (but isn't). We'll use their documentation found here to write this renderer.

First, install the package:

npm install --save @atjson/renderer-hir

And import it to the top of the file:

import Renderer from "@atjson/renderer-hir";

export default class SlackRenderer extends Renderer {}

We have a renderer that will return the text of the document we pass in! (yay!)

Let's make the renderer return a string instead of an array of strings:

import Renderer from "@atjson/renderer-hir";

export default class SlackRenderer extends Renderer {
  *root() {
    let text = yield;
    return text.join("");
  }
}

Let's start writing tests to verify what we're writing works. For this example we'll be using 🃏Jest as our testing framework.

We're going to use the OffsetSource as our source document and set of annotations, since it's provided with out-of-the-box support for the basic syntax that Slack supports.

import OffsetSource from "@atjson/offset-annotations";
import SlackRenderer from "../src";

describe("SlackRenderer", () => {
  test("it returns text", () => {
    let doc = new OffsetSource({
      content: "Hello!",
      annotations: [],
    });

    expect(SlackRenderer.render(doc)).toBe("Hello!");
  });
});

Let's add some test cases for this, from their docs:

import OffsetSource, { Bold, Italic, Strikethrough } from '@atjson/offset-annotations';
import SlackRenderer from '../src';

describe('SlackRenderer', () => {
  test('it returns text', () => { ... });

  test('bold', () => {
    let doc = new OffsetSource({
      content: 'To bold, surround your text with asterisks: your text',
      annotations: [new Bold({ start: 44, end: 53 })]
    });

    expect(SlackRenderer.render(doc)).toBe('To bold, surround your text with asterisks: *your text*');
  });

  test('italic', () => {
    let doc = new OffsetSource({
      content: 'To italicize, surround your text with underscores: your text',
      annotations: [new Italic({ start: 51, end: 60 })]
    });

    expect(SlackRenderer.render(doc)).toBe('To italicize, surround your text with underscores: _your text_');
  });

  test('strikethrough', () => {
    let doc = new OffsetSource({
      content: 'To strikethrough, surround your text with tildes: your text',
      annotations: [new Italic({ start: 50, end: 59 })]
    });

    expect(SlackRenderer.render(doc)).toBe('To strikethrough, surround your text with tildes: ~your text~');
  });
});

Running tests should result in 3 failing tests.

Now that we have some failing test cases, let's go back to our renderer and implement bold markup:

import Renderer from "@atjson/renderer-hir";

export default class SlackRenderer extends Renderer {
  *Bold() {
    let text = yield;
    return `*${text.join("")}*`;
  }

  *root() {
    let text = yield;
    return text.join("");
  }
}

We use Bold here as a convention to match the class name of the annotation. Alternatively, we can use the key bold to generate the markup (it's up to you how you want to write it 😉).

Running tests now should result in 2 failing tests. 🎉

Now that we've got the hang of the first one, let's bang out the other 2 failing tests:

import Renderer from "@atjson/renderer-hir";

export default class SlackRenderer extends Renderer {
  *Bold() {
    let text = yield;
    return `*${text.join("")}*`;
  }

  *Italic() {
    let text = yield;
    return `_${text.join("")}_`;
  }

  *Strikethrough() {
    let text = yield;
    return `~${text.join("")}~`;
  }

  *root() {
    let text = yield;
    return text.join("");
  }
}