http-nu 0.15.0

The surprisingly performant, Nushell-scriptable, cross.stream-powered, Datastar-ready HTTP server that fits in your back pocket.
Documentation
# HTML DSL for nushell

# Escape HTML special characters
def escape-html []: string -> string {
  $in
  | str replace -a '&' '&'
  | str replace -a '"' '"'
  | str replace -a "'" '''
  | str replace -a '<' '&lt;'
  | str replace -a '>' '&gt;'
}

# Normalize content to string, joining lists
def to-children []: any -> string {
  let input = $in
  let type = $input | describe -d | get type
  match $type {
    'string' => ($input | escape-html)
    'list' => ($input | each { to-children } | str join)
    'closure' => (do $input | to-children)
    'record' => (if '__html' in $input { $input.__html } else { "" })
    _ => ""
  }
}

# {class: "foo"} -> ' class="foo"'
# style can be a record: {style: {color: red, padding: 10px}} -> ' style="color: red; padding: 10px;"'
# boolean: {disabled: true} -> ' disabled', {disabled: false} -> ''
export def attrs-to-string []: record -> string {
  $in
  | transpose key value
  | each {|attr|
    if $attr.value == false { return "" }
    if $attr.value == true { return $attr.key }
    let value = if $attr.key == "style" and ($attr.value | describe -d | get type) == "record" {
      $attr.value | transpose k v | each {|p|
        let v = if ($p.v | describe -d | get type) == "list" { $p.v | str join ", " } else { $p.v }
        $"($p.k): ($v);"
      } | str join " "
    } else if $attr.key == "class" and ($attr.value | describe -d | get type) == "list" {
      $attr.value | str join " "
    } else {
      $attr.value
    }
    $'($attr.key)="($value | escape-html)"'
  }
  | where $it != ""
  | str join ' '
  | if ($in | is-empty) { "" } else { $" ($in)" }
}

# Render a tag with optional attributes and children
export def render-tag [tag: string ...args: any]: nothing -> record {
  # Normalize to [attrs, ...content] - prepend {} if first arg isn't a record
  let args = if ($args | is-not-empty) and ($args | first | describe -d | get type) == 'record' and '__html' not-in ($args | first) { $args } else { $args | prepend {} }
  let attrs = $args | first
  let content = $args | skip 1
  let children = $content | each { to-children } | str join
  let attrs_str = $attrs | attrs-to-string
  {__html: $"<($tag)($attrs_str)>($children)</($tag)>"}
}

# Render a void tag (no children allowed per HTML spec)
export def render-void-tag [tag: string attrs?: record]: nothing -> record {
  let attrs_str = ($attrs | default {}) | attrs-to-string
  {__html: $"<($tag)($attrs_str)>"}
}

# Jinja2 control flow
export def _for [binding: record ...body: any]: nothing -> record {
  let var = $binding | columns | first
  let collection = $binding | values | first
  let children = $body | each { to-children } | str join
  {__html: $"{% for ($var) in ($collection) %}($children){% endfor %}"}
}

export def _if [cond: string ...body: any]: nothing -> record {
  let children = $body | each { to-children } | str join
  {__html: $"{% if ($cond) %}($children){% endif %}"}
}

# Jinja2 variable expression (not escaped)
export def _var [expr: string]: nothing -> record {
  {__html: $"{{ ($expr) }}"}
}

# Document metadata
export def HTML [...args: any]: nothing -> record {
  let inner = render-tag html ...$args
  {__html: $"<!DOCTYPE html>($inner.__html)"}
}
export def HEAD [...args: any]: nothing -> record { render-tag head ...$args }
export def TITLE [...args: any]: nothing -> record { render-tag title ...$args }
export def BASE [attrs?: record]: nothing -> record { render-void-tag base $attrs }
export def LINK [attrs?: record]: nothing -> record { render-void-tag link $attrs }
export def META [attrs?: record]: nothing -> record { render-void-tag meta $attrs }
export def STYLE [...args: any]: nothing -> record { render-tag style ...$args }

# Sections
export def BODY [...args: any]: nothing -> record { render-tag body ...$args }
export def ARTICLE [...args: any]: nothing -> record { render-tag article ...$args }
export def SECTION [...args: any]: nothing -> record { render-tag section ...$args }
export def NAV [...args: any]: nothing -> record { render-tag nav ...$args }
export def ASIDE [...args: any]: nothing -> record { render-tag aside ...$args }
export def HEADER [...args: any]: nothing -> record { render-tag header ...$args }
export def FOOTER [...args: any]: nothing -> record { render-tag footer ...$args }
export def MAIN [...args: any]: nothing -> record { render-tag main ...$args }

# Grouping content
export def DIV [...args: any]: nothing -> record { render-tag div ...$args }
export def P [...args: any]: nothing -> record { render-tag p ...$args }
export def HR [attrs?: record]: nothing -> record { render-void-tag hr $attrs }
export def PRE [...args: any]: nothing -> record { render-tag pre ...$args }
export def BLOCKQUOTE [...args: any]: nothing -> record { render-tag blockquote ...$args }
export def OL [...args: any]: nothing -> record { render-tag ol ...$args }
export def UL [...args: any]: nothing -> record { render-tag ul ...$args }
export def LI [...args: any]: nothing -> record { render-tag li ...$args }
export def DL [...args: any]: nothing -> record { render-tag dl ...$args }
export def DT [...args: any]: nothing -> record { render-tag dt ...$args }
export def DD [...args: any]: nothing -> record { render-tag dd ...$args }
export def FIGURE [...args: any]: nothing -> record { render-tag figure ...$args }
export def FIGCAPTION [...args: any]: nothing -> record { render-tag figcaption ...$args }

# Text content
export def A [...args: any]: nothing -> record { render-tag a ...$args }
export def EM [...args: any]: nothing -> record { render-tag em ...$args }
export def STRONG [...args: any]: nothing -> record { render-tag strong ...$args }
export def SMALL [...args: any]: nothing -> record { render-tag small ...$args }
export def CODE [...args: any]: nothing -> record { render-tag code ...$args }
export def SPAN [...args: any]: nothing -> record { render-tag span ...$args }
export def BR [attrs?: record]: nothing -> record { render-void-tag br $attrs }
export def WBR [attrs?: record]: nothing -> record { render-void-tag wbr $attrs }

# Embedded content
export def IMG [attrs?: record]: nothing -> record { render-void-tag img $attrs }
export def IFRAME [...args: any]: nothing -> record { render-tag iframe ...$args }
export def EMBED [attrs?: record]: nothing -> record { render-void-tag embed $attrs }
export def OBJECT [...args: any]: nothing -> record { render-tag object ...$args }
export def VIDEO [...args: any]: nothing -> record { render-tag video ...$args }
export def AUDIO [...args: any]: nothing -> record { render-tag audio ...$args }
export def SOURCE [attrs?: record]: nothing -> record { render-void-tag source $attrs }
export def TRACK [attrs?: record]: nothing -> record { render-void-tag track $attrs }
export def CANVAS [...args: any]: nothing -> record { render-tag canvas ...$args }

# SVG container
export def SVG [...args: any]: nothing -> record { render-tag svg ...$args }

# SVG container/structural elements
export def G [...args: any]: nothing -> record { render-tag g ...$args }
export def DEFS [...args: any]: nothing -> record { render-tag defs ...$args }
export def SYMBOL [...args: any]: nothing -> record { render-tag symbol ...$args }
export def USE [...args: any]: nothing -> record { render-tag use ...$args }

# SVG basic shapes
export def CIRCLE [...args: any]: nothing -> record { render-tag circle ...$args }
export def ELLIPSE [...args: any]: nothing -> record { render-tag ellipse ...$args }
export def LINE [...args: any]: nothing -> record { render-tag line ...$args }
export def PATH [...args: any]: nothing -> record { render-tag path ...$args }
export def POLYGON [...args: any]: nothing -> record { render-tag polygon ...$args }
export def POLYLINE [...args: any]: nothing -> record { render-tag polyline ...$args }
export def RECT [...args: any]: nothing -> record { render-tag rect ...$args }

# SVG text
export def TEXT [...args: any]: nothing -> record { render-tag text ...$args }
export def TEXTPATH [...args: any]: nothing -> record { render-tag textPath ...$args }
export def TSPAN [...args: any]: nothing -> record { render-tag tspan ...$args }

# SVG gradients and patterns
export def LINEARGRADIENT [...args: any]: nothing -> record { render-tag linearGradient ...$args }
export def RADIALGRADIENT [...args: any]: nothing -> record { render-tag radialGradient ...$args }
export def STOP [...args: any]: nothing -> record { render-tag stop ...$args }
export def PATTERN [...args: any]: nothing -> record { render-tag pattern ...$args }

# SVG clipping and masking
export def CLIPPATH [...args: any]: nothing -> record { render-tag clipPath ...$args }
export def MASK [...args: any]: nothing -> record { render-tag mask ...$args }

# SVG other
export def FOREIGNOBJECT [...args: any]: nothing -> record { render-tag foreignObject ...$args }
export def IMAGE [...args: any]: nothing -> record { render-tag image ...$args }
export def MARKER [...args: any]: nothing -> record { render-tag marker ...$args }
export def DESC [...args: any]: nothing -> record { render-tag desc ...$args }
export def SVGTITLE [...args: any]: nothing -> record { render-tag title ...$args }

# MathML
export def MATH [...args: any]: nothing -> record { render-tag math ...$args }

# Tables
export def TABLE [...args: any]: nothing -> record { render-tag table ...$args }
export def CAPTION [...args: any]: nothing -> record { render-tag caption ...$args }
export def THEAD [...args: any]: nothing -> record { render-tag thead ...$args }
export def TBODY [...args: any]: nothing -> record { render-tag tbody ...$args }
export def TFOOT [...args: any]: nothing -> record { render-tag tfoot ...$args }
export def TR [...args: any]: nothing -> record { render-tag tr ...$args }
export def TH [...args: any]: nothing -> record { render-tag th ...$args }
export def TD [...args: any]: nothing -> record { render-tag td ...$args }
export def COL [attrs?: record]: nothing -> record { render-void-tag col $attrs }
export def COLGROUP [...args: any]: nothing -> record { render-tag colgroup ...$args }

# Forms
export def FORM [...args: any]: nothing -> record { render-tag form ...$args }
export def LABEL [...args: any]: nothing -> record { render-tag label ...$args }
export def INPUT [attrs?: record]: nothing -> record { render-void-tag input $attrs }
export def BUTTON [...args: any]: nothing -> record { render-tag button ...$args }
export def SELECT [...args: any]: nothing -> record { render-tag select ...$args }
export def OPTION [...args: any]: nothing -> record { render-tag option ...$args }
export def TEXTAREA [...args: any]: nothing -> record { render-tag textarea ...$args }
export def FIELDSET [...args: any]: nothing -> record { render-tag fieldset ...$args }
export def LEGEND [...args: any]: nothing -> record { render-tag legend ...$args }

# Headings
export def H1 [...args: any]: nothing -> record { render-tag h1 ...$args }
export def H2 [...args: any]: nothing -> record { render-tag h2 ...$args }
export def H3 [...args: any]: nothing -> record { render-tag h3 ...$args }
export def H4 [...args: any]: nothing -> record { render-tag h4 ...$args }
export def H5 [...args: any]: nothing -> record { render-tag h5 ...$args }
export def H6 [...args: any]: nothing -> record { render-tag h6 ...$args }

# Scripting
export def SCRIPT [...args: any]: nothing -> record { render-tag script ...$args }
export def NOSCRIPT [...args: any]: nothing -> record { render-tag noscript ...$args }
export def TEMPLATE [...args: any]: nothing -> record { render-tag template ...$args }
export def SLOT [...args: any]: nothing -> record { render-tag slot ...$args }

# Additional inline text
export def ABBR [...args: any]: nothing -> record { render-tag abbr ...$args }
export def B [...args: any]: nothing -> record { render-tag b ...$args }
export def I [...args: any]: nothing -> record { render-tag i ...$args }
export def U [...args: any]: nothing -> record { render-tag u ...$args }
export def S [...args: any]: nothing -> record { render-tag s ...$args }
export def MARK [...args: any]: nothing -> record { render-tag mark ...$args }
export def Q [...args: any]: nothing -> record { render-tag q ...$args }
export def CITE [...args: any]: nothing -> record { render-tag cite ...$args }
export def DFN [...args: any]: nothing -> record { render-tag dfn ...$args }
export def KBD [...args: any]: nothing -> record { render-tag kbd ...$args }
export def SAMP [...args: any]: nothing -> record { render-tag samp ...$args }
export def VAR [...args: any]: nothing -> record { render-tag var ...$args }
export def SUB [...args: any]: nothing -> record { render-tag sub ...$args }
export def SUP [...args: any]: nothing -> record { render-tag sup ...$args }
export def TIME [...args: any]: nothing -> record { render-tag time ...$args }
export def DATA [...args: any]: nothing -> record { render-tag data ...$args }

# Edits
export def DEL [...args: any]: nothing -> record { render-tag del ...$args }
export def INS [...args: any]: nothing -> record { render-tag ins ...$args }

# Interactive
export def DETAILS [...args: any]: nothing -> record { render-tag details ...$args }
export def SUMMARY [...args: any]: nothing -> record { render-tag summary ...$args }
export def DIALOG [...args: any]: nothing -> record { render-tag dialog ...$args }

# Additional content
export def ADDRESS [...args: any]: nothing -> record { render-tag address ...$args }
export def HGROUP [...args: any]: nothing -> record { render-tag hgroup ...$args }
export def SEARCH [...args: any]: nothing -> record { render-tag search ...$args }
export def MENU [...args: any]: nothing -> record { render-tag menu ...$args }

# Image maps
export def AREA [attrs?: record]: nothing -> record { render-void-tag area $attrs }
export def MAP [...args: any]: nothing -> record { render-tag map ...$args }
export def PICTURE [...args: any]: nothing -> record { render-tag picture ...$args }

# Form additions
export def METER [...args: any]: nothing -> record { render-tag meter ...$args }
export def PROGRESS [...args: any]: nothing -> record { render-tag progress ...$args }
export def OUTPUT [...args: any]: nothing -> record { render-tag output ...$args }
export def DATALIST [...args: any]: nothing -> record { render-tag datalist ...$args }
export def OPTGROUP [...args: any]: nothing -> record { render-tag optgroup ...$args }

# Ruby annotations
export def RUBY [...args: any]: nothing -> record { render-tag ruby ...$args }
export def RT [...args: any]: nothing -> record { render-tag rt ...$args }
export def RP [...args: any]: nothing -> record { render-tag rp ...$args }

# Bidirectional text
export def BDI [...args: any]: nothing -> record { render-tag bdi ...$args }
export def BDO [...args: any]: nothing -> record { render-tag bdo ...$args }

# Web components
export def ICONIFY [name: string attrs?: record]: nothing -> record {
  render-tag iconify-icon ({icon: $name} | merge ($attrs | default {}))
}
export def SCRIPT-ICONIFY []: nothing -> record {
  SCRIPT {src: "https://cdn.jsdelivr.net/npm/iconify-icon@2/dist/iconify-icon.min.js"}
}