112 lines · 3.5 KB
/**
 * Tiny, dependency-free highlighter for the static shell and YAML snippets on marketing and docs
 * pages. It only needs to look right for hand-written examples, so each language is a short list of
 * regex rules tried in order at every position.
 */

export type SnippetLanguage = "bash" | "yaml";

export type TokenKind = "comment" | "string" | "keyword" | "property" | "entity" | "sign" | "plain";

export type Token = { kind: TokenKind; text: string };

type Rule = { kind: TokenKind; pattern: RegExp };

const EXPRESSION = { kind: "entity", pattern: /\$\{\{.*?\}\}/y } as const;

const BASH_RULES: ReadonlyArray<Rule> = [
  { kind: "comment", pattern: /#.*/y },
  { kind: "string", pattern: /"(?:\\.|[^"\\])*"|'[^']*'/y },
  { kind: "entity", pattern: /\$\{?[A-Za-z_][\w]*\}?/y },
  { kind: "property", pattern: /--?[A-Za-z][\w-]*/y },
  { kind: "sign", pattern: /&&|\|\||[|;>]/y },
];

const YAML_RULES: ReadonlyArray<Rule> = [
  { kind: "comment", pattern: /#.*/y },
  EXPRESSION,
  { kind: "string", pattern: /"(?:\\.|[^"\\])*"|'[^']*'/y },
  { kind: "sign", pattern: /- |[[\]{},]/y },
  { kind: "keyword", pattern: /\b(?:true|false|null|\d+(?:\.\d+)?)\b/y },
];

const YAML_KEY = /^(\s*(?:- )?)([\w.-]+)(:)(?=\s|$)/;
const BASH_COMMAND_START = /(^|&&|\|\||[|;])(\s*)$/;

function scan(line: string, rules: ReadonlyArray<Rule>, start: number, tokens: Token[]): void {
  let plain = "";
  let index = start;
  const flushPlain = () => {
    if (plain) tokens.push({ kind: "plain", text: plain });