From 7756306e1d247b24a58711fb6838e01f2c06dbd3 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 1 Nov 2025 22:40:25 -0700 Subject: [PATCH] insanity --- src/compiler/tests/function-blocks.test.ts | 81 --------------- src/compiler/tests/ribbit.test.ts | 113 +++++++++++++++++++++ 2 files changed, 113 insertions(+), 81 deletions(-) create mode 100644 src/compiler/tests/ribbit.test.ts diff --git a/src/compiler/tests/function-blocks.test.ts b/src/compiler/tests/function-blocks.test.ts index 056affe..41bf65d 100644 --- a/src/compiler/tests/function-blocks.test.ts +++ b/src/compiler/tests/function-blocks.test.ts @@ -53,84 +53,3 @@ signals.trap 'EXIT': end`).toEvaluateTo(['EXIT', true]) }) }) - -describe('ribbit', () => { - test('head tag', () => { - expect(` - head: - title What up - meta charSet=UTF-8 - meta name=viewport content='width=device-width, initial-scale=1, viewport-fit=cover' - end`).toEvaluateTo(`head`, { - head: () => 'head' - }) - }) - - test('li', () => { - expect(` - list: - li border-bottom='1px solid black' one - li two - li three - end`).toEvaluateTo(`list`, { - list: () => 'list' - }) - }) - - test('inline expressions', () => { - const buffer: string[] = [] - - 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(``) - } - - 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 === null ? buffer.pop() : a) - .reverse().join(' ') - .replaceAll(' !!ribbit-nospace!! ', '') - - buffer.push(`<${tagName}${space}${attrs.join(' ')}>${children}`) - } - - const tag = async (tagName: string, atNamed: {}, ...args: any[]) => { - if (typeof args[0] === 'function') - await tagBlock(tagName, atNamed, args[0]) - else - tagCall(tagName, atNamed, ...args) - } - - expect(` - ribbit: - p class=container: - h1 class=bright style='font-family: helvetica' Heya - h2 man that is (b wild) (nospace) ! - p Double the fun. - end - end`).toEvaluateTo( - `

-

Heya

-

man that is wild!

-

Double the fun.

-

`, { - ribbit: async (cb: Function) => { - await cb() - return buffer.join("\n") - }, - 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), - nospace: () => '!!ribbit-nospace!!', - join: (...args: string[]) => args.join(''), - }) - }) -}) \ No newline at end of file diff --git a/src/compiler/tests/ribbit.test.ts b/src/compiler/tests/ribbit.test.ts new file mode 100644 index 0000000..0fe397a --- /dev/null +++ b/src/compiler/tests/ribbit.test.ts @@ -0,0 +1,113 @@ +import { expect, describe, test, beforeEach } from 'bun:test' +import { type Value } from 'reefvm' + +const buffer: string[] = [] + +const ribbitGlobals = { + ribbit: async (cb: Function) => { + await cb() + return buffer.join("\n") + }, + tag: (tagName: string, atDefaults = {}) => { + return (atNamed = {}, ...args: any[]) => tag(tagName, 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(``) +} + +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 === null ? 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}`) +} + +const tag = async (tagName: string, atNamed = {}, ...args: any[]) => { + if (typeof args[0] === 'function') + await tagBlock(tagName, atNamed, args[0]) + else + tagCall(tagName, atNamed, ...args) +} + +const NOSPACE_TOKEN = '!!ribbit-nospace!!' +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(` +What up + + +`, ribbitGlobals) + }) + + test('custom tags', () => { + expect(` +list = tag 'ul' class=list +ribbit: + list: + li border-bottom='1px solid black' one + li two + li three + end +end`).toEvaluateTo(``, ribbitGlobals) + }) + + test('inline expressions', () => { + expect(` + ribbit: + p class=container: + h1 class=bright style='font-family: helvetica' Heya + h2 man that is (b wild) (nospace) ! + p Double the fun. + end + end`).toEvaluateTo( + `

+

Heya

+

man that is wild!

+

Double the fun.

+

`, ribbitGlobals) + }) +}) \ No newline at end of file