markz

Element names

An element's @name is the element it writes (Design): an HTML element on the allowlist for its kind, or a custom element. The lists leave out what Markdown already writes (em, a, pre, …) and span, which [text]{.x} writes, so each element has one way in, and anything active (script, iframe, form controls, media), so a name can never run code. Inline and block are separate, so a block element never lands inside a paragraph.

A custom-element name is lowercase ASCII letters, digits and -, starts with a letter and has a -, less the few names HTML reserves. It fits either kind.

export const INLINE: ReadonlySet<string> = new Set(
  "abbr b i u s small cite q dfn time data var samp kbd mark sub sup ins bdi bdo ruby rt rp".split(
    " ",
  ),
);
export const BLOCK: ReadonlySet<string> = new Set(
  "div section article aside header footer nav main address hgroup search details summary figure figcaption dl dt dd".split(
    " ",
  ),
);
const RESERVED = new Set(
  "annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(
    " ",
  ),
);

export function custom(name: string): boolean {
  return /^[a-z][a-z\d]*-[a-z\d-]*$/.test(name) && !RESERVED.has(name);
}

/** Whether `name` is an element of this kind: inline for a span, block for a leaf or container. */
export function element(name: string, inline: boolean): boolean {
  return (inline ? INLINE : BLOCK).has(name) || custom(name);
}

What a name that isn't an element should be instead, in the writer's own words: a class of that name, or a custom element made from it. A name a hyphen would fix gets my- in front, so {@note} suggests {@div .note} or {@my-note}. Returns the warning's message and instead.

export function notElement(name: string, inline: boolean): [message: string, instead: string] {
  const word = name.toLowerCase();
  const tag = custom(word) ? word : `my-${word}`;
  return [
    `\`${name}\` is not an element name`,
    inline
      ? `a span with a class (\`[text]{.${name}}\`), or a custom element (\`[text]{@${tag}}\`)`
      : `a \`div\` with a class (\`{@div .${name}}\`), or a custom element (\`{@${tag}}\`)`,
  ];
}