import { expect, describe, test, beforeEach } from 'bun:test' const buffer: string[] = [] const ribbitGlobals = { ribbit: async (cb: Function) => { await cb() return buffer.join("\n") }, tag: async (tagFn: Function, atDefaults = {}) => { return (atNamed = {}, ...args: any[]) => tagFn(Object.assign({}, atDefaults, atNamed), ...args) }, head: (atNamed: {}, ...args: any[]) => tag('head', atNamed, ...args), title: (atNamed: {}, ...args: any[]) => tag('title', atNamed, ...args), meta: (atNamed: {}, ...args: any[]) => tag('meta', atNamed, ...args), p: (atNamed: {}, ...args: any[]) => tag('p', atNamed, ...args), h1: (atNamed: {}, ...args: any[]) => tag('h1', atNamed, ...args), h2: (atNamed: {}, ...args: any[]) => tag('h2', atNamed, ...args), b: (atNamed: {}, ...args: any[]) => tag('b', atNamed, ...args), ul: (atNamed: {}, ...args: any[]) => tag('ul', atNamed, ...args), li: (atNamed: {}, ...args: any[]) => tag('li', atNamed, ...args), nospace: () => NOSPACE_TOKEN, echo: (...args: any[]) => console.log(...args) } function raw(fn: Function) { (fn as any).raw = true } const tagBlock = async (tagName: string, props = {}, fn: Function) => { const attrs = Object.entries(props).map(([key, value]) => `${key}="${value}"`) const space = attrs.length ? ' ' : '' buffer.push(`<${tagName}${space}${attrs.join(' ')}>`) await fn() buffer.push(`${tagName}>`) } const tagCall = (tagName: string, atNamed = {}, ...args: any[]) => { const attrs = Object.entries(atNamed).map(([key, value]) => `${key}="${value}"`) const space = attrs.length ? ' ' : '' const children = args .reverse() .map(a => a === TAG_TOKEN ? buffer.pop() : a) .reverse().join(' ') .replaceAll(` ${NOSPACE_TOKEN} `, '') if (SELF_CLOSING.includes(tagName)) buffer.push(`<${tagName}${space}${attrs.join(' ')} />`) else buffer.push(`<${tagName}${space}${attrs.join(' ')}>${children}${tagName}>`) } const tag = async (tagName: string, atNamed = {}, ...args: any[]) => { if (typeof args[0] === 'function') { await tagBlock(tagName, atNamed, args[0]) } else { tagCall(tagName, atNamed, ...args) return TAG_TOKEN } } const NOSPACE_TOKEN = '!!ribbit-nospace!!' const TAG_TOKEN = '!!ribbit-tag!!' const SELF_CLOSING = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"] describe('ribbit', () => { beforeEach(() => buffer.length = 0) test('head tag', () => { expect(` ribbit: head: title What up meta charset=UTF-8 meta name=viewport content='width=device-width, initial-scale=1, viewport-fit=cover' end end `).toEvaluateTo(`
Double the fun.
`, ribbitGlobals) }) })