Breezewind - Breezing fast templatingπŸ”—

Breezewind is a templating engine (JSON to HTML/XML) designed with the following considerations in mind:

InstallationπŸ”—

Breezewind is available through npm or deno.land. It's coupled to Gustwind releases at deno.land as I don't know yet how to publish both from a monorepo.

UsageπŸ”—

The following example illustrates how to use the API to render a link:

import breeze from "breezewind";

breeze({
  component: {
    type: "a",
    attributes: {
      href: "https://gustwind.js.org/",
      title: "Gustwind",
    },
    children: "Link to Gustwind",
  },
  components: {},
  context: {},
  extensions: [],
  globalUtilities: {},
});

ComponentsπŸ”—

Components allow React-style composition. In other words, they let you extract functionality that's shared and reuse it. Consider the following example:

import breeze from "breezewind";

breeze({
  component: {
    type: "Link",
    props: {
      href: "https://gustwind.js.org/",
      title: "Gustwind",
      children: "Link to Gustwind",
    },
  },
  components: {
    Link: {
      type: "a",
      attributes: {
        href: { utility: "get", parameters: ["props", "href"]},
        title: { utility: "get", parameters: ["props", "title"]},
      },
      children: { utility: "get", parameters: ["props", "children"]},
    }
  },
  context: {},
  extensions: [],
  globalUtilities: {},
});

Note that you can apply utilities within props as well to dig data from components props or global context.

UtilitiesπŸ”—

To work around the limitation of not having access to eval based techniques, Gustwind relies on a simple programming model based on function invocations. In the example below, { utility: "get", parameters: ["props", "href"]} was an example of this.

On top of this, it's possible to apply functions within parameters recursively. For example, you could concatenate to a url like this:

const concatenatedAttribute = {
  utility: "concat",
  parameters: [
    {
      utility: "get",
      parameters: ["props", "data.slug"]
    },
    "/"
  ]
};

The following utilities are provided out of the box:

To implement your own, follow this signature: (...args: unknown[]) => unknown | Promise<unknown>.

Note that Breezewind context is available through this.context at utilities should you need to access it. For example, the get default utility leverages to allow access to the context through the utility.

To pass custom utilities to Breezewind, do the following:

import breeze from "breezewind";

breeze({
  component: {
    type: "Link",
    // Since we want to apply a utility, we have to use bindToProps.
    // This way the system knows what you want to preserve as an object.
    bindToProps: {
      children: { utility: "hello", parameters: ["hello"] },
    },
    props: {
      href: "https://gustwind.js.org/",
      title: "Gustwind",
    },
  },
  components: { ... },
  context: {},
  extensions: [],
  globalUtilities: {
    hello: (input: string) => input + ' ' + 'world!',
  },
  // Component utilities are scoped to a component or its children
  componentUtilities: {
    Button: {
      demo: (input: string) => input + ' ' + 'demo!',
    }
  }
});

To detect when rendering has started and ended (useful for instrumentation), you can use _onRenderStart(context: Context) and _onRenderEnd(context: Context). Each triggers once during the rendering process.

ContextπŸ”—

To allow injecting data from outside to templates, Breezewind implements a global context. Use it like this:

import breeze from "breezewind";

breeze({
  component: {
    type: "Link",
    props: {
      href: "https://gustwind.js.org/",
      title: "Gustwind",
      children: { utility: "hello", parameters: [{
        // Note the access here!
        utility: "get", parameters: ["context", "hello"]
      }] },
    },
  },
  components: { ...},
  context: {
    hello: "hello",
  },
  extensions: [],
  globalUtilities: {
    hello: (input: string) => input + ' ' + 'world!',
  },
});

You have access to the context anywhere and it's comparable to the concept in React and other templating engines.

ExtensionsπŸ”—

Currently four official extensions are supported:

To activate extensions, do the following:

import { tw } from "twind";
import breeze from "breezewind";
import extensions from "breezewind/extensions";

breeze({
  component: {
    visibleIf: { utility: "get", parameters: ["context", "url"] },
    type: "a",
    class: "underline",
    classList: {
      "font-bold": [
        "gustwind.js.org",
        { utility: "get", parameters: ["context", "url"] }
      ]
    },
    attributes: {
      href: "https://gustwind.js.org/",
      title: "Gustwind",
    },
    children: "Link to Gustwind",
  },
  components: { ... },
  context: { url: "gustwind.js.org" },
  extensions: [
    extensions.visibleIf,
    extensions.classShortcut(tw),
    extensions.foreach,
  ],
  globalUtilities: {},
});

Rendering a doctypeπŸ”—

To render a doctype, set closingCharacter to "" to avoid the default behavior that generates full tags:

import breeze from "breezewind";

breeze({
  component: {
    type: "!DOCTYPE",
    attributes: {
      html: "",
    },
    closingCharacter: "",
  },
  ...
});

TypingπŸ”—

To access types, use the following kind of syntax:

import breeze, { type Component } from "breezewind";

...

PlaygroundπŸ”—

Use the playground below to experiment with the syntax:

Editor

Publishing to npmπŸ”—

  1. deno task build:breezewind-for-npm <VERSION> where VERSION is 0.1.0 for example
  2. cd breezewind/npm
  3. npm publish. You may need to pass --otp here as well (preferred for security)

LicenseπŸ”—

MIT.