JSFiddle - React, Tailwind, and code Playground

by evan

JavaScript

function htmlEscape(str: string): string {
  return str
    .replace(/&/g, '&') // first!
    .replace(/>/g, '>')
    .replace(/</g, '&lt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
    .replace(/`/g, '&#96;');
}

export class Description {
  #content: string;
  private constructor(content: string) {
    this.#content = content;
  }

  get() {
    return this.#content;
  }

  append(more: Description) {
    return new Description(this.get() + more.get());
  }

  concat(others: Description[]) {
    const additional = others.map((desc) => desc.get()).join();
    return new Description(this.get() + additional);
  }

  static template(templateParts: readonly string[], data: any[]) {
    let result = data
      .map((subst, i) => {
        return `${templateParts[i]}${htmlEscape(String(subst))}`;
      })
      .join('');
    // Take care of last literal section
    // (Never fails, because an empty template string
    // produces one literal section, an empty string)
    result += templateParts[templateParts.length - 1];
    return new Description(result);
  }
}

function html(templateParts: TemplateStringsArray, ...data: any[]): Description {
  return Description.template(templateParts.raw, data);
}