Motivation
While implementing crank-mdx, I needed a convenient way for the transpiler to generate element creation calls without having to explicitly import or define every HTML tag.
For example, the transpiler currently generates:
import { tags } from "crank-mdx/tags";
and Markdown such as:
# Heading 1
Paragraph
List:
- item 1
- item 2
can be transformed into:
export default () => {
const { h1, p, ul, li } = tags;
const __items__ = [];
__items__.push(h1({}, "Heading 1"));
__items__.push(p({}, "Paragraph"));
__items__.push(p({}, "List:"));
__items__.push(
ul(
{},
li({}, p({}, "item 1")),
li({}, p({}, "item 2"))
)
);
return __items__;
};
To support this, I implemented a small tags proxy:
Why add it to Crank core?
The implementation is intentionally very small and does not introduce another rendering abstraction. It simply provides a dynamic interface to the existing createElement function.
Instead of maintaining a separate implementation in projects such as crank-mdx, Crank could expose this utility directly:
import { tags } from "@b9g/crank";
const { div, h1, p } = tags;
h1({}, "Hello");
p({}, "This is Crank");
This also makes the API useful outside of MDX. Potential use cases include:
- Markdown/MDX transpilers
- HTML-to-Crank transformations
- template compilers
- code generators
- hyperscript-style APIs
- applications that prefer tag functions over JSX
- integrations with other markup languages
It complements createElement
I don't see tags as an alternative to createElement. Rather, it is a small convenience layer on top of it.
Conceptually:
tags.div(props, children...)
│
▼
createElement("div", props, children...)
│
▼
Crank Element
This keeps the actual element creation logic in one place.
Another benefit for transpilers
A transpiler can generate:
const { h1, p, ul, li } = tags;
instead of having to generate or maintain a static mapping such as:
import {
h1,
p,
ul,
li
} from "...";
The proxy also means that arbitrary/custom element names can be handled without requiring the core package to maintain a list of every possible tag.
For example:
Minimal API addition
The public API could simply be:
export { createElement } from "./...";
export { tags } from "./...";
There is no new component model, state system, renderer, or runtime dependency involved.
The implementation is essentially a cached dynamic wrapper around the existing createElement API.
crank-mdx
With tags available from Crank itself, crank-mdx would no longer need to ship its own implementation:
import { tags } from "@b9g/crank";
instead of:
import { tags } from "crank-mdx/tags";
This would make the generated output more directly tied to Crank's public API and allow other Crank-related transpilers and tools to reuse the same primitive.
Summary
I propose adding tags as a small utility to Crank core.
It provides a dynamic tag-function interface over createElement, requires very little code, does not change Crank's existing architecture, and is useful for transpilers and hyperscript-style usage beyond crank-mdx.
The main motivation came from crank-mdx, but the utility itself is independent of MDX and could be useful as a general part of Crank's element-creation API.
Motivation
While implementing
crank-mdx, I needed a convenient way for the transpiler to generate element creation calls without having to explicitly import or define every HTML tag.For example, the transpiler currently generates:
and Markdown such as:
can be transformed into:
To support this, I implemented a small
tagsproxy:Why add it to Crank core?
The implementation is intentionally very small and does not introduce another rendering abstraction. It simply provides a dynamic interface to the existing
createElementfunction.Instead of maintaining a separate implementation in projects such as
crank-mdx, Crank could expose this utility directly:This also makes the API useful outside of MDX. Potential use cases include:
It complements
createElementI don't see
tagsas an alternative tocreateElement. Rather, it is a small convenience layer on top of it.Conceptually:
This keeps the actual element creation logic in one place.
Another benefit for transpilers
A transpiler can generate:
instead of having to generate or maintain a static mapping such as:
The proxy also means that arbitrary/custom element names can be handled without requiring the core package to maintain a list of every possible tag.
For example:
Minimal API addition
The public API could simply be:
There is no new component model, state system, renderer, or runtime dependency involved.
The implementation is essentially a cached dynamic wrapper around the existing
createElementAPI.crank-mdxWith
tagsavailable from Crank itself,crank-mdxwould no longer need to ship its own implementation:instead of:
This would make the generated output more directly tied to Crank's public API and allow other Crank-related transpilers and tools to reuse the same primitive.
Summary
I propose adding
tagsas a small utility to Crank core.It provides a dynamic tag-function interface over
createElement, requires very little code, does not change Crank's existing architecture, and is useful for transpilers and hyperscript-style usage beyondcrank-mdx.The main motivation came from
crank-mdx, but the utility itself is independent of MDX and could be useful as a general part of Crank's element-creation API.