feat(html): add @choojs/html — server renderer, raw(), morph
ESM ports with attribution: nanohtml 1.10.0 server tag (browserify/babel transform branches removed — pure runtime), raw-server, and nanomorph 5.4.3 consolidated into one module. browser.js is a Phase 2 placeholder that throws loudly. Covered by test/server.test.js on node:test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014NgfSjHE11oFpoSnLVKLXd
This commit is contained in:
co-authored by
Claude Fable 5
parent
3d6b474621
commit
90cca611c9
@@ -0,0 +1,56 @@
|
||||
// Server renderer behavior, matching nanohtml 1.x server semantics.
|
||||
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
|
||||
import html from '@choojs/html'
|
||||
import raw from '@choojs/html/raw'
|
||||
|
||||
test('renders a template to a string', () => {
|
||||
const res = html`<p>hello</p>`
|
||||
assert.strictEqual(res.toString(), '<p>hello</p>')
|
||||
})
|
||||
|
||||
test('escapes interpolated text', () => {
|
||||
const res = html`<p>${'<script>alert(1)</script>'}</p>`
|
||||
assert.strictEqual(res.toString(), '<p><script>alert(1)</script></p>')
|
||||
})
|
||||
|
||||
test('raw() bypasses escaping', () => {
|
||||
const res = html`<p>${raw('<em>hi</em>')}</p>`
|
||||
assert.strictEqual(res.toString(), '<p><em>hi</em></p>')
|
||||
})
|
||||
|
||||
test('nested templates are not double-encoded', () => {
|
||||
const inner = html`<em>&</em>`
|
||||
const res = html`<p>${inner}</p>`
|
||||
assert.strictEqual(res.toString(), '<p><em>&</em></p>')
|
||||
})
|
||||
|
||||
test('arrays of children are joined', () => {
|
||||
const items = ['a', 'b'].map((x) => html`<li>${x}</li>`)
|
||||
const res = html`<ul>${items}</ul>`
|
||||
assert.strictEqual(res.toString(), '<ul><li>a</li><li>b</li></ul>')
|
||||
})
|
||||
|
||||
test('unquoted attribute interpolation is quoted', () => {
|
||||
const res = html`<div class=${'a b'}></div>`
|
||||
assert.strictEqual(res.toString(), '<div class="a b"></div>')
|
||||
})
|
||||
|
||||
test('boolean attributes render when truthy and drop when falsy', () => {
|
||||
const on = html`<input disabled=${true} />`
|
||||
const off = html`<input disabled=${false} />`
|
||||
assert.strictEqual(on.toString(), '<input disabled="disabled" />')
|
||||
assert.strictEqual(off.toString(), '<input />'.replace(' ', ' '))
|
||||
})
|
||||
|
||||
test('event handler functions are stripped', () => {
|
||||
const res = html`<button onclick=${() => {}}>go</button>`
|
||||
assert.strictEqual(res.toString(), '<button onclick="">go</button>')
|
||||
})
|
||||
|
||||
test('spread-style object interpolation renders attributes', () => {
|
||||
const res = html`<div ${{ class: 'x', hidden: true }}></div>`
|
||||
assert.strictEqual(res.toString(), '<div class="x" hidden="hidden"></div>')
|
||||
})
|
||||
Reference in New Issue
Block a user