ajo 0.1.30 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LLMs.md +349 -0
- package/context.js +18 -0
- package/dist/html.cjs +1 -1
- package/dist/html.js +47 -45
- package/dist/index.cjs +1 -1
- package/dist/index.js +87 -70
- package/dist/jsx.cjs +1 -0
- package/dist/jsx.js +12 -0
- package/html.js +137 -0
- package/index.js +309 -0
- package/jsx.js +21 -0
- package/license +15 -15
- package/package.json +74 -54
- package/readme.md +447 -418
- package/types.ts +135 -90
package/LLMs.md
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# Ajo LLM Instructions
|
|
2
|
+
|
|
3
|
+
Ajo is a micro UI library using JSX and generators. **No React imports**: JSX compiles to Ajo via build config.
|
|
4
|
+
|
|
5
|
+
## Stateless Component
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import type { Stateless, WithChildren } from 'ajo'
|
|
9
|
+
import clsx from 'clsx' // optional, for conditional classes
|
|
10
|
+
|
|
11
|
+
type Args = WithChildren<{ title: string; active?: boolean }>
|
|
12
|
+
|
|
13
|
+
const Card: Stateless<Args> = ({ title, active, children }) => (
|
|
14
|
+
<div class={clsx('card', { active })}>
|
|
15
|
+
<h3>{title}</h3>
|
|
16
|
+
{children}
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
// Example usage - everything goes to args:
|
|
21
|
+
<Card title="Hello" active>
|
|
22
|
+
<p>This is a card.</p>
|
|
23
|
+
</Card>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Stateful Component
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import type { Stateful } from 'ajo'
|
|
30
|
+
|
|
31
|
+
type Args = { initial: number; step?: number }
|
|
32
|
+
|
|
33
|
+
const Counter: Stateful<Args, 'section'> = function* ({ initial }) {
|
|
34
|
+
|
|
35
|
+
// before loop: init state & handlers (use parameter for initial args)
|
|
36
|
+
|
|
37
|
+
let count = initial
|
|
38
|
+
let inputRef: HTMLInputElement | null = null
|
|
39
|
+
|
|
40
|
+
const inc = () => this.next(({ step = 1 }) => count += step)
|
|
41
|
+
const dec = () => this.next(() => count--)
|
|
42
|
+
|
|
43
|
+
const handleKeydown = (e: KeyboardEvent) => {
|
|
44
|
+
if (e.key === 'ArrowUp') inc()
|
|
45
|
+
else if (e.key === 'ArrowDown' && count > 0) dec()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
this.addEventListener('keydown', handleKeydown, { signal: this.signal }) // auto-cleanup on lifecycle end
|
|
49
|
+
|
|
50
|
+
for (const { step = 1 } of this) { // main render loop — `for...of this` yields fresh args each cycle
|
|
51
|
+
|
|
52
|
+
try { // optional: error boundary
|
|
53
|
+
|
|
54
|
+
// derived values
|
|
55
|
+
const isEven = count % 2 === 0
|
|
56
|
+
|
|
57
|
+
yield (
|
|
58
|
+
<>
|
|
59
|
+
<input
|
|
60
|
+
ref={el => inputRef = el}
|
|
61
|
+
value={count}
|
|
62
|
+
set:oninput={e => this.next(() => count = +(e.target as HTMLInputElement).value)}
|
|
63
|
+
/>
|
|
64
|
+
<button set:onclick={inc}>+{step}</button>
|
|
65
|
+
<button set:onclick={dec} disabled={count <= 0}>-</button>
|
|
66
|
+
<p memo={isEven}>Even: {isEven ? 'yes' : 'no'}</p>
|
|
67
|
+
<footer memo>Static content - rendered once</footer>
|
|
68
|
+
<div skip>{/* third-party managed DOM here */}</div>
|
|
69
|
+
</>
|
|
70
|
+
)
|
|
71
|
+
} catch (err: unknown) {
|
|
72
|
+
yield <p class="error">{err instanceof Error ? err.message : String(err)}</p>
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Counter.is = 'section' // wrapper element (default: div)
|
|
78
|
+
Counter.attrs = { class: 'counter-wrap' } // default wrapper attributes
|
|
79
|
+
Counter.args = { step: 1 } // default args
|
|
80
|
+
|
|
81
|
+
// Or use stateful() to avoid duplicating 'section':
|
|
82
|
+
// const Counter = stateful(function* ({ initial }: Args) { ... }, 'section')
|
|
83
|
+
// Counter.attrs = { class: 'counter-wrap' }
|
|
84
|
+
// Counter.args = { step: 1 }
|
|
85
|
+
|
|
86
|
+
// Example usage - special attrs apply to wrapper, rest goes to args:
|
|
87
|
+
|
|
88
|
+
let ref: ThisParameterType<typeof Counter> | null = null
|
|
89
|
+
|
|
90
|
+
<Counter
|
|
91
|
+
initial={0} step={5} // → args
|
|
92
|
+
attr:id="main" attr:class="my-counter" // → wrapper attributes (HTML attrs)
|
|
93
|
+
set:onclick={fn} // → wrapper properties (DOM props)
|
|
94
|
+
key={id} // → wrapper key
|
|
95
|
+
memo={[id]} // → wrapper memo (array)
|
|
96
|
+
memo={id} // → wrapper memo (single value)
|
|
97
|
+
memo // → wrapper memo (render once)
|
|
98
|
+
ref={el => ref = el} // → wrapper ref (el is <section> + .next()/.throw()/.return())
|
|
99
|
+
/>
|
|
100
|
+
|
|
101
|
+
ref?.next() // trigger re-render from outside
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
| Topic | Rule |
|
|
107
|
+
|-------|------|
|
|
108
|
+
| **Elements** | Everything becomes HTML attributes. `set:prop` assigns DOM properties instead (`node[prop] = value`) |
|
|
109
|
+
| **Stateless** | Everything goes to `args`. Special attrs like `memo` must be applied to elements inside |
|
|
110
|
+
| **Stateful** | `key`, `memo`, `skip`, `ref`, `set:*` apply to implicit wrapper element. `attr:*` sets wrapper attributes. Rest goes to `args` |
|
|
111
|
+
| **Events** | `set:onclick`, `set:oninput`, etc. Never `onClick` |
|
|
112
|
+
| **Classes** | `class`, never `className`. Must be string, no object/array syntax. Use `clsx()` or template literals |
|
|
113
|
+
| **Styles** | `style` must be string (`style="color: red"`), not object. No special handling |
|
|
114
|
+
| **Args** | Destructure in parameter for init code. Render loops: `for (const { prop } of this)` for fresh args each cycle (new bindings), or `for ({ prop } of this)` without `const` to reassign variables from the outer scope. `while (true)` when you don't need fresh args |
|
|
115
|
+
| **Generators** | Stateful components are JS generators with a main render loop (`while`/`for...of`). Since they're generators, yields can also appear outside the loop — sequential phases, conditional blocks, etc. are all valid |
|
|
116
|
+
| **Root JSX** | Use `<>...</>` in stateful to avoid double wrapper |
|
|
117
|
+
| **Re-render** | `this.next(fn?)` — returns `fn`'s result. Safe after unmount (no-op). Use `this.throw(e)` for explicit error routing |
|
|
118
|
+
| **Context** | `context<T>(fallback)` creates context. Stateless: read only. Stateful: read/write. Reads go inside loop (fresh each render). Constant writes can go before loop; dynamic writes go inside loop |
|
|
119
|
+
| **Lists** | Always provide unique `key` on elements |
|
|
120
|
+
| **Refs** | `ref={el => ...}` on elements. Receives `null` on unmount. Stateful ref type: `ThisParameterType<typeof Component>` |
|
|
121
|
+
| **Memo** | `memo={[deps]}` array, `memo={value}` single, or just `memo` (never re-render). Skips subtree if unchanged |
|
|
122
|
+
| **Skip** | `skip` excludes children from reconciliation. Use for `set:textContent`/`set:innerHTML` or third-party managed DOM |
|
|
123
|
+
| **Custom wrapper** | `stateful(fn, 'tagname')` sets `.is` and infers `this` type. Or manually: `Stateful<Args, 'tagname'>` + `.is = 'tagname'`. Default is `div` (no need to set) |
|
|
124
|
+
| **Default attrs** | `.attrs = { class: '...' }` on stateful component generator function |
|
|
125
|
+
| **Default args** | `.args = { prop: value }` on stateful component generator function |
|
|
126
|
+
| **Cleanup** | `this.signal` aborts on lifecycle end (unmount, `this.return()`, generator done). Use for fetch, addEventListener, etc. `try/finally` for the rest |
|
|
127
|
+
| **Error recovery** | `try { ... } catch { yield error UI }` inside loop |
|
|
128
|
+
| **this** | Stateful wrapper element with `.signal`, `.next(fn?)` (returns `fn`'s result), `.throw()`, `.return(deep?)`. Iterable: `for...of this` yields args. Type: `ThisParameterType<typeof Component>` |
|
|
129
|
+
|
|
130
|
+
## Common Patterns
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
import type { Stateful, Stateless, WithChildren } from 'ajo'
|
|
134
|
+
import { stateful } from 'ajo'
|
|
135
|
+
import { context } from 'ajo/context'
|
|
136
|
+
|
|
137
|
+
// Async data loading
|
|
138
|
+
type LoaderArgs = { url: string }
|
|
139
|
+
|
|
140
|
+
const DataLoader: Stateful<LoaderArgs> = function* ({ url }) {
|
|
141
|
+
|
|
142
|
+
let data: unknown = null
|
|
143
|
+
let error: Error | null = null
|
|
144
|
+
|
|
145
|
+
fetch(url, { signal: this.signal }) // signal auto-aborts on lifecycle end
|
|
146
|
+
.then(r => r.json())
|
|
147
|
+
.then(d => this.next(() => data = d)) // no-op if unmounted
|
|
148
|
+
.catch(e => this.next(() => error = e)) // no-op if unmounted
|
|
149
|
+
|
|
150
|
+
while (true) yield (
|
|
151
|
+
<>
|
|
152
|
+
{error ? <p>Error: {error.message}</p>
|
|
153
|
+
: data ? <div>{JSON.stringify(data)}</div>
|
|
154
|
+
: <p>Loading...</p>}
|
|
155
|
+
</>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// List with keys
|
|
160
|
+
type Item = { id: string; text: string }
|
|
161
|
+
|
|
162
|
+
const List: Stateless<{ items: Item[] }> = ({ items }) => (
|
|
163
|
+
<ul>
|
|
164
|
+
{items.map(item => <li key={item.id}>{item.text}</li>)}
|
|
165
|
+
</ul>
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
// Conditional rendering
|
|
169
|
+
type ShowArgs = WithChildren<{ when: boolean }>
|
|
170
|
+
|
|
171
|
+
const Show: Stateless<ShowArgs> = ({ when, children }) => when ? children : null
|
|
172
|
+
|
|
173
|
+
// Context - create with fallback value
|
|
174
|
+
const ThemeContext = context<'light' | 'dark'>('light')
|
|
175
|
+
const UserContext = context<{ name: string } | null>(null)
|
|
176
|
+
|
|
177
|
+
// Stateless - read only (call without args)
|
|
178
|
+
const ThemedCard: Stateless<{ title: string }> = ({ title }) => {
|
|
179
|
+
const theme = ThemeContext() // reads current value
|
|
180
|
+
return <div class={`card theme-${theme}`}>{title}</div>
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Stateful - read/write inside render loop
|
|
184
|
+
const ThemeProvider: Stateful<WithChildren> = function* () {
|
|
185
|
+
|
|
186
|
+
let theme: 'light' | 'dark' = 'light'
|
|
187
|
+
|
|
188
|
+
const toggle = () => this.next(() => theme = theme === 'light' ? 'dark' : 'light')
|
|
189
|
+
|
|
190
|
+
for (const { children } of this) {
|
|
191
|
+
|
|
192
|
+
ThemeContext(theme) // write: sets value for descendants
|
|
193
|
+
const user = UserContext() // read: gets value from ancestor
|
|
194
|
+
|
|
195
|
+
yield (
|
|
196
|
+
<>
|
|
197
|
+
<button set:onclick={toggle}>Theme: {theme}</button>
|
|
198
|
+
{user && <span>User: {user.name}</span>}
|
|
199
|
+
{children}
|
|
200
|
+
</>
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Ref typing for stateful components
|
|
206
|
+
let counterRef: ThisParameterType<typeof Counter> | null = null
|
|
207
|
+
<Counter ref={el => counterRef = el} initial={0} />
|
|
208
|
+
// counterRef is <section> element + .next(), .throw(), .return() methods
|
|
209
|
+
counterRef?.next() // trigger re-render from outside
|
|
210
|
+
|
|
211
|
+
// memo variations
|
|
212
|
+
<div memo={[a, b]}>{/* re-render when a or b changes */}</div>
|
|
213
|
+
<div memo={count}>{/* re-render when count changes (single value) */}</div>
|
|
214
|
+
<div memo>{/* render once, never update - good for static content */}</div>
|
|
215
|
+
|
|
216
|
+
// Boolean attributes
|
|
217
|
+
<input type="checkbox" checked disabled /> // checked="" disabled=""
|
|
218
|
+
<button disabled={false} /> // removes disabled attr
|
|
219
|
+
|
|
220
|
+
// Attributes vs Properties (HTML-first)
|
|
221
|
+
<input value="text" /> // HTML attribute: initial value only
|
|
222
|
+
<input set:value={text} /> // DOM property: syncs with state
|
|
223
|
+
<input type="checkbox" checked /> // HTML attribute: initial state
|
|
224
|
+
<input type="checkbox" set:checked={bool} /> // DOM property: syncs with state
|
|
225
|
+
<video set:currentTime={0} set:muted /> // DOM properties
|
|
226
|
+
<div set:textContent={str} skip /> // DOM property + skip (required!)
|
|
227
|
+
<div set:innerHTML={html} skip /> // DOM property + skip (required!)
|
|
228
|
+
|
|
229
|
+
// Post-render work (DOM is updated by the time the microtask runs)
|
|
230
|
+
const ScrollList: Stateful<{ items: Item[] }> = function* () {
|
|
231
|
+
let container: HTMLUListElement | null = null
|
|
232
|
+
for (const { items } of this) {
|
|
233
|
+
queueMicrotask(() => container!.scrollTop = container!.scrollHeight)
|
|
234
|
+
yield (
|
|
235
|
+
<ul ref={el => container = el}>
|
|
236
|
+
{items.map(item => <li key={item.id}>{item.text}</li>)}
|
|
237
|
+
</ul>
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Reassign parameter vars in render loop (no const/let — updates outer scope)
|
|
243
|
+
const Greeter: Stateful<{ name: string }> = function* ({ name }) {
|
|
244
|
+
// name is available for init code here
|
|
245
|
+
for ({ name } of this) { // reassigns `name` from parameter each cycle
|
|
246
|
+
yield <p>Hello, {name}!</p>
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Third-party managed DOM
|
|
251
|
+
let map: MapLibrary | null = null
|
|
252
|
+
<div skip ref={el => el ? (map ??= new MapLibrary(el)) : map?.destroy()} /> // skip lets library control children
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Anti-patterns
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
// ❌ React patterns: NEVER use
|
|
259
|
+
import React from 'react'
|
|
260
|
+
className="..."
|
|
261
|
+
onClick={...}
|
|
262
|
+
useState, useEffect, useCallback
|
|
263
|
+
|
|
264
|
+
// ❌ class/style as object or array: no special handling in Ajo
|
|
265
|
+
<div class={{ active: isActive }} /> // won't work
|
|
266
|
+
<div class={['btn', 'primary']} /> // won't work
|
|
267
|
+
<div style={{ color: 'red' }} /> // won't work
|
|
268
|
+
|
|
269
|
+
// ✅ class/style must be strings
|
|
270
|
+
<div class={`btn ${isActive ? 'active' : ''}`} />
|
|
271
|
+
<div class={clsx('btn', { active: isActive })} /> // use clsx library
|
|
272
|
+
<div style="color: red; font-size: 14px" />
|
|
273
|
+
<div style={`color: ${color}`} />
|
|
274
|
+
|
|
275
|
+
// ❌ memo in args but not applied to element - does nothing
|
|
276
|
+
const Bad: Stateless<{ memo: unknown }> = ({ memo }) => (
|
|
277
|
+
<div>content</div> // memo arg ignored, no memoization
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
// ✅ Pass deps in args, apply to root element
|
|
281
|
+
const Good: Stateless<{ deps?: unknown }> = ({ deps }) => (
|
|
282
|
+
<div memo={deps}>content</div> // memoized when deps provided
|
|
283
|
+
)
|
|
284
|
+
// <Good deps={[id]} /> or <Good deps={data} />
|
|
285
|
+
|
|
286
|
+
// ❌ Context read outside loop - stale values
|
|
287
|
+
function* Bad() {
|
|
288
|
+
const theme = ThemeContext() // frozen at mount, never updated
|
|
289
|
+
while (true) yield ...
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ✅ Context read inside loop - fresh each render
|
|
293
|
+
function* Good() {
|
|
294
|
+
while (true) {
|
|
295
|
+
const user = UserContext() // read: fresh value each render
|
|
296
|
+
yield ...
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ✅ Constant context write outside loop - value never changes, OK
|
|
301
|
+
function* Good() {
|
|
302
|
+
ThemeContext('dark') // write once: constant value
|
|
303
|
+
while (true) yield ...
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ✅ Dynamic context write inside loop - value depends on state
|
|
307
|
+
function* Good() {
|
|
308
|
+
let theme = 'light'
|
|
309
|
+
while (true) {
|
|
310
|
+
ThemeContext(theme) // write: updated each render
|
|
311
|
+
yield ...
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ❌ Missing key in lists
|
|
316
|
+
{items.map(item => <li>{item}</li>)}
|
|
317
|
+
|
|
318
|
+
// ❌ Direct state mutation without next()
|
|
319
|
+
const inc = () => count++ // won't re-render
|
|
320
|
+
|
|
321
|
+
// ❌ set:textContent/innerHTML without skip: content gets cleared
|
|
322
|
+
<div set:innerHTML={html} />
|
|
323
|
+
|
|
324
|
+
// ✅ Correct
|
|
325
|
+
const inc = () => this.next(() => count++)
|
|
326
|
+
<div set:innerHTML={html} skip />
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Setup
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
npm install ajo
|
|
333
|
+
pnpm add ajo
|
|
334
|
+
yarn add ajo
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Configure JSX automatic runtime (Vite example):
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
// vite.config.ts
|
|
341
|
+
export default defineConfig({
|
|
342
|
+
esbuild: {
|
|
343
|
+
jsx: 'automatic',
|
|
344
|
+
jsxImportSource: 'ajo',
|
|
345
|
+
},
|
|
346
|
+
})
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
For other build systems: `jsx: 'react-jsx'` (or `'automatic'`), `jsxImportSource: 'ajo'`. No manual imports needed — the build tool auto-imports from `ajo/jsx-runtime`.
|
package/context.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const Context = Symbol.for('ajo.context')
|
|
2
|
+
|
|
3
|
+
export const context = (fallback, key = Symbol()) => function (...args) {
|
|
4
|
+
|
|
5
|
+
const self = this ?? component
|
|
6
|
+
|
|
7
|
+
return self
|
|
8
|
+
? args.length
|
|
9
|
+
? self[Context][key] = args[0]
|
|
10
|
+
: key in self[Context]
|
|
11
|
+
? self[Context][key]
|
|
12
|
+
: fallback
|
|
13
|
+
: fallback
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let component = null
|
|
17
|
+
|
|
18
|
+
export const current = (...args) => args.length ? (component = args[0]) : component
|
package/dist/html.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./context.cjs"),$=new Set(["area","base","br","col","command","embed","hr","img","input","keygen","link","meta","param","source","track","wbr"]),y=Symbol.for("ajo.args"),u=e=>e.replace(/[&<>"']/g,t=>`&#${t.charCodeAt(0)};`),m=()=>{},p={tag:"div"},w=e=>{let t="";return c(e,n=>t+=n),t},c=(e,t)=>{if(e==null)return;const n=typeof e;if(n!="boolean")if(n=="string")t(u(e));else if(n=="number"||n=="bigint")t(u(String(e)));else if(Symbol.iterator in e)for(e of e)c(e,t);else"nodeName"in e?typeof e.nodeName=="function"?v(e,t):g(e,t):t(u(String(e)))},g=(e,t)=>{const{nodeName:n,children:s}=e;let l="";for(const o in e)o=="nodeName"||o=="children"||o=="key"||o=="skip"||o=="memo"||o=="ref"||o.startsWith("set:")||e[o]==null||e[o]===!1||(e[o]===!0?l+=` ${o}`:l+=` ${o}="${u(String(e[o]))}"`);$.has(n)?t(`<${n}${l}>`):(t(`<${n}${l}>`),s!=null&&c(s,t),t(`</${n}>`))},v=({nodeName:e,fallback:t=e.fallback,...n},s)=>{e.constructor.name=="GeneratorFunction"?x(e,n,s):c(e(n),s)},x=(e,t,n)=>{const s={...e.attrs},l={...e.args};for(const r in t)r.startsWith("attr:")?s[r.slice(5)]=t[r]:r=="key"||r=="skip"||r=="memo"||r=="ref"||r.startsWith("set:")?s[r]=t[r]:l[r]=t[r];const o=new AbortController,b={*[Symbol.iterator](){for(;;)yield this[y]},[a.Context]:Object.create(a.current()?.[a.Context]??null),[y]:l,signal:o.signal,next:m,return:m,throw:r=>{throw r}},f=e.call(b,l),k=a.current();a.current(b);const d=r=>({...s,nodeName:e.is??p.tag,children:r});let i="";try{g(d(f.next().value),r=>i+=r)}catch(r){i="",g(d(f.throw(r).value),S=>i+=S)}finally{f.return(),o.abort(),a.current(k)}n(i)};exports.defaults=p;exports.html=c;exports.render=w;
|
package/dist/html.js
CHANGED
|
@@ -1,53 +1,55 @@
|
|
|
1
|
-
import { Context as
|
|
2
|
-
const
|
|
3
|
-
},
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
},
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
r.startsWith("set:") || i[r] == null || i[r] === !1 || (i[r] === !0 ? o += ` ${r}` : o += ` ${r}="${y(String(i[r]))}"`);
|
|
10
|
-
p.has(e) ? yield `<${e}${o}>` : (yield `<${e}${o}>`, n != null && (yield* d(n)), yield `</${e}>`);
|
|
11
|
-
}, c = function* (e) {
|
|
12
|
-
if (e == null) return;
|
|
13
|
-
const n = typeof e;
|
|
1
|
+
import { Context as d, current as c } from "./context.js";
|
|
2
|
+
const w = /* @__PURE__ */ new Set(["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]), p = /* @__PURE__ */ Symbol.for("ajo.args"), i = (t) => t.replace(/[&<>"']/g, (e) => `&#${e.charCodeAt(0)};`), m = () => {
|
|
3
|
+
}, S = { tag: "div" }, C = (t) => {
|
|
4
|
+
let e = "";
|
|
5
|
+
return f(t, (n) => e += n), e;
|
|
6
|
+
}, f = (t, e) => {
|
|
7
|
+
if (t == null) return;
|
|
8
|
+
const n = typeof t;
|
|
14
9
|
if (n != "boolean")
|
|
15
|
-
if (n == "string")
|
|
16
|
-
else if (n == "number" || n == "bigint")
|
|
17
|
-
else if (Symbol.iterator in
|
|
18
|
-
else "nodeName" in
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
10
|
+
if (n == "string") e(i(t));
|
|
11
|
+
else if (n == "number" || n == "bigint") e(i(String(t)));
|
|
12
|
+
else if (Symbol.iterator in t) for (t of t) f(t, e);
|
|
13
|
+
else "nodeName" in t ? typeof t.nodeName == "function" ? v(t, e) : g(t, e) : e(i(String(t)));
|
|
14
|
+
}, g = (t, e) => {
|
|
15
|
+
const { nodeName: n, children: s } = t;
|
|
16
|
+
let l = "";
|
|
17
|
+
for (const o in t)
|
|
18
|
+
o == "nodeName" || o == "children" || o == "key" || o == "skip" || o == "memo" || o == "ref" || o.startsWith("set:") || t[o] == null || t[o] === !1 || (t[o] === !0 ? l += ` ${o}` : l += ` ${o}="${i(String(t[o]))}"`);
|
|
19
|
+
w.has(n) ? e(`<${n}${l}>`) : (e(`<${n}${l}>`), s != null && f(s, e), e(`</${n}>`));
|
|
20
|
+
}, v = ({ nodeName: t, fallback: e = t.fallback, ...n }, s) => {
|
|
21
|
+
t.constructor.name == "GeneratorFunction" ? x(t, n, s) : f(t(n), s);
|
|
22
|
+
}, x = (t, e, n) => {
|
|
23
|
+
const s = { ...t.attrs }, l = { ...t.args };
|
|
24
|
+
for (const r in e)
|
|
25
|
+
r.startsWith("attr:") ? s[r.slice(5)] = e[r] : r == "key" || r == "skip" || r == "memo" || r == "ref" || r.startsWith("set:") ? s[r] = e[r] : l[r] = e[r];
|
|
26
|
+
const o = new AbortController(), b = {
|
|
27
|
+
*[Symbol.iterator]() {
|
|
28
|
+
for (; ; ) yield this[p];
|
|
29
|
+
},
|
|
30
|
+
[d]: Object.create(c()?.[d] ?? null),
|
|
31
|
+
[p]: l,
|
|
32
|
+
signal: o.signal,
|
|
33
|
+
next: m,
|
|
34
|
+
return: m,
|
|
35
|
+
throw: (r) => {
|
|
36
|
+
throw r;
|
|
32
37
|
}
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
const
|
|
38
|
+
}, u = t.call(b, l), k = c();
|
|
39
|
+
c(b);
|
|
40
|
+
const y = (r) => ({ ...s, nodeName: t.is ?? S.tag, children: r });
|
|
41
|
+
let a = "";
|
|
36
42
|
try {
|
|
37
|
-
|
|
38
|
-
} catch (
|
|
39
|
-
|
|
43
|
+
g(y(u.next().value), (r) => a += r);
|
|
44
|
+
} catch (r) {
|
|
45
|
+
a = "", g(y(u.throw(r).value), ($) => a += $);
|
|
40
46
|
} finally {
|
|
41
|
-
|
|
47
|
+
u.return(), o.abort(), c(k);
|
|
42
48
|
}
|
|
43
|
-
|
|
44
|
-
if ("children" in r) {
|
|
45
|
-
const l = [...c(r.children)];
|
|
46
|
-
l.length ? r.children = l.length == 1 ? l[0] : l : delete r.children;
|
|
47
|
-
}
|
|
48
|
-
return r;
|
|
49
|
+
n(a);
|
|
49
50
|
};
|
|
50
51
|
export {
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
S as defaults,
|
|
53
|
+
f as html,
|
|
54
|
+
C as render
|
|
53
55
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("./context.cjs"),{isArray:x}=Array,{assign:N,create:A}=Object,a=Symbol.for("ajo.key"),g=Symbol.for("ajo.keyed"),C=Symbol.for("ajo.memo"),u=Symbol.for("ajo.cache"),f=Symbol.for("ajo.generator"),o=Symbol.for("ajo.iterator"),S=Symbol.for("ajo.render"),c=Symbol.for("ajo.args"),h=Symbol.for("ajo.controller"),E={tag:"div"},v=(r,t)=>(t&&(r.is=t),r),m=(r,t,e=t.firstChild,n=null)=>{for(p(r,i=>{const s=typeof i=="string"?M(i,e):W(i,t,e);e==null?b(t,s,n):s==e?e=s.nextSibling:s==e.nextSibling?(b(t,e,n),e=s.nextSibling):b(t,s,e)});e!=n;){const i=e.nextSibling;e.nodeType==1&&O(e),t.removeChild(e),e=i}},p=(r,t)=>{if(r==null)return;const e=typeof r;if(e!="boolean")if(e=="string")t(r);else if(e=="number"||e=="bigint")t(String(r));else if(Symbol.iterator in r)for(r of r)p(r,t);else"nodeName"in r?typeof r.nodeName=="function"?T(r,t):t(r):t(String(r))},T=({nodeName:r,...t},e)=>{r.constructor.name=="GeneratorFunction"?e(G(r,t)):p(r(t),e)},G=(r,t)=>{const e={...r.attrs},n={...r.args};for(const i in t)i.startsWith("attr:")?e[i.slice(5)]=t[i]:i=="key"||i=="skip"||i=="memo"||i=="ref"||i.startsWith("set:")?e[i]=t[i]:n[i]=t[i];return{...e,nodeName:r.is??E.tag,[f]:r,[c]:n}},M=(r,t)=>{for(;t&&t.nodeType!=3;)t=t.nextSibling;return t?t.data!=r&&(t.data=r):t=document.createTextNode(r),t},W=(r,t,e)=>{const{nodeName:n,children:i,key:s,skip:k,memo:w,[f]:y,[c]:j}=r;for(s!=null&&(e=(t[g]??=new Map).get(s)??e);e&&(e.localName!=n||e[a]!=null&&e[a]!=s||e[f]&&e[f]!=y);)e=e[a]!=null?null:e.nextElementSibling;return e??=document.createElementNS(r.xmlns??t.namespaceURI,n),s!=null&&t[g].set(e[a]=s,e),(w==null||I(e[C],e[C]=w))&&(B(e[u],e[u]=r,e),k||(y?R(y,j,e):m(i,e))),e},B=(r,t,e)=>{for(const n in{...r,...t})n=="nodeName"||n=="children"||n=="key"||n=="skip"||n=="memo"||r?.[n]===t[n]||(n=="ref"&&typeof t[n]=="function"?t[n](e):n.startsWith("set:")?e[n.slice(4)]=t[n]:t[n]==null||t[n]===!1?e.removeAttribute(n):e.setAttribute(n,t[n]===!0?"":t[n]))},I=(r,t)=>x(r)&&x(t)?r.some((e,n)=>e!==t[n]):r!==t,K=(r,t)=>{let e=t.firstElementChild;for(;e;)if(r(e)&&e.firstElementChild)e=e.firstElementChild;else{for(;e!=t&&!e.nextElementSibling;)e=e.parentNode??t;e=e!=t&&e.nextElementSibling}},b=(r,t,e)=>{if(t.isConnected&&t.contains(document.activeElement)){const n=t.nextSibling;for(;e&&e!=t;){const i=e.nextSibling;r.insertBefore(e,n),e=i}}else r.insertBefore(t,e)},O=r=>{let t=r;for(;t.firstElementChild;)t=t.firstElementChild;for(;;){const{nextElementSibling:e,parentNode:n}=t;if(t[a]!=null&&n?.[g]?.delete(t[a]),typeof t.return=="function"&&t.return(!1),typeof t[u]?.ref=="function"&&t[u].ref(null),t===r)break;if(t=e??n??r,e)for(;t.firstElementChild;)t=t.firstElementChild}},R=(r,t,e)=>{e[f]??=(N(e,q)[l.Context]=A(l.current()?.[l.Context]??null),r),e[c]=t,e[S]()},q={*[Symbol.iterator](){for(;;)yield this[c]},[S](){const r=l.current();l.current(this);try{this[o]||(this.signal=(this[h]=new AbortController).signal,this[o]=this[f].call(this,this[c]));const{value:t,done:e}=this[o].next();m(t,this),e&&this.return()}catch(t){this.throw(t)}finally{l.current(r)}},next(r,t){if(!this.isConnected)return t;try{typeof r=="function"&&(t=r.call(this,this[c]))}catch(e){return this.throw(e)}return l.current()?.contains(this)||this[S](),t},throw(r){for(let t=this;t;t=t.parentNode)if(t[o]?.throw)try{return m(t[o].throw(r).value,t)}catch(e){r=new Error(e?.message??e,{cause:r})}throw r},return(r=!0){r&&K(t=>typeof t.return=="function"?t.return():!0,this);try{this[o]?.return()}catch(t){this.throw(t)}finally{this[o]=null,this[h]?.abort()}}};exports.defaults=E;exports.render=m;exports.stateful=v;
|