mono-jsx 0.3.2 β 0.3.4
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/README.md +130 -94
- package/jsx-runtime.mjs +209 -161
- package/package.json +1 -1
- package/types/css.d.ts +6 -3
- package/types/html.d.ts +1 -1
- package/types/jsx.d.ts +2 -7
- package/types/mono.d.ts +47 -19
- package/types/render.d.ts +0 -5
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ mono-jsx is a JSX runtime that renders `<html>` element to `Response` object in
|
|
|
9
9
|
- π« Minimal state runtime
|
|
10
10
|
- π¨ Complete Web API TypeScript definitions
|
|
11
11
|
- β³ Streaming rendering
|
|
12
|
+
- π₯· [htmx](#using-htmx) integration
|
|
12
13
|
- π Universal, works in Node.js, Deno, Bun, Cloudflare Workers, etc.
|
|
13
14
|
|
|
14
15
|
## Installation
|
|
@@ -65,6 +66,7 @@ mono-jsx allows you to return an `<html>` JSX element as a `Response` object in
|
|
|
65
66
|
|
|
66
67
|
```tsx
|
|
67
68
|
// app.tsx
|
|
69
|
+
|
|
68
70
|
export default {
|
|
69
71
|
fetch: (req) => (
|
|
70
72
|
<html>
|
|
@@ -91,6 +93,7 @@ npx wrangler dev app.tsx
|
|
|
91
93
|
|
|
92
94
|
```tsx
|
|
93
95
|
// app.tsx
|
|
96
|
+
|
|
94
97
|
import { serve } from "srvx";
|
|
95
98
|
|
|
96
99
|
serve({
|
|
@@ -157,7 +160,7 @@ mono-jsx supports [pseudo classes](https://developer.mozilla.org/en-US/docs/Web/
|
|
|
157
160
|
</a>;
|
|
158
161
|
```
|
|
159
162
|
|
|
160
|
-
### `<slot>` Element
|
|
163
|
+
### Using `<slot>` Element
|
|
161
164
|
|
|
162
165
|
mono-jsx uses [`<slot>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/slot) elements to render slotted content (equivalent to React's `children` property). You can also add the `name` attribute to define named slots:
|
|
163
166
|
|
|
@@ -185,7 +188,7 @@ function App() {
|
|
|
185
188
|
}
|
|
186
189
|
```
|
|
187
190
|
|
|
188
|
-
### `html` Tag Function
|
|
191
|
+
### Using `html` Tag Function
|
|
189
192
|
|
|
190
193
|
mono-jsx provides an `html` tag function to render raw HTML in JSX instead of React's `dangerouslySetInnerHTML`:
|
|
191
194
|
|
|
@@ -270,12 +273,7 @@ mono-jsx also accepts functions for the `action` property on `form` elements, wh
|
|
|
270
273
|
```tsx
|
|
271
274
|
function App() {
|
|
272
275
|
return (
|
|
273
|
-
<form
|
|
274
|
-
action={(data: FormData, event: SubmitEvent) => {
|
|
275
|
-
event.preventDefault(); // true
|
|
276
|
-
console.log(data.get("name"));
|
|
277
|
-
}}
|
|
278
|
-
>
|
|
276
|
+
<form action={(data: FormData, event: SubmitEvent) => console.log(data.get("name"))}>
|
|
279
277
|
<input type="text" name="name" />
|
|
280
278
|
<button type="submit">Submit</button>
|
|
281
279
|
</form>
|
|
@@ -285,11 +283,11 @@ function App() {
|
|
|
285
283
|
|
|
286
284
|
## Reactive
|
|
287
285
|
|
|
288
|
-
mono-jsx provides a minimal state runtime for updating the view based on client-side state changes
|
|
286
|
+
mono-jsx provides a minimal state runtime for updating the view based on client-side state changes.
|
|
289
287
|
|
|
290
|
-
### Using State
|
|
288
|
+
### Using Component State
|
|
291
289
|
|
|
292
|
-
You can use `this`
|
|
290
|
+
You can use the `this` keyword in your components to manage state. The state is bound to the component instance and can be updated directly, and will automatically re-render the view when the state changes:
|
|
293
291
|
|
|
294
292
|
```tsx
|
|
295
293
|
function Counter(
|
|
@@ -377,9 +375,60 @@ function App(this: FC<{ input: string }>) {
|
|
|
377
375
|
}
|
|
378
376
|
```
|
|
379
377
|
|
|
380
|
-
###
|
|
378
|
+
### Using `<toggle>` Element with State
|
|
379
|
+
|
|
380
|
+
The `<toggle>` element conditionally renders content based on the value of a state.
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
function App(this: FC<{ show: boolean }>) {
|
|
384
|
+
this.show = false;
|
|
385
|
+
|
|
386
|
+
function toggle() {
|
|
387
|
+
this.show = !this.show;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<div>
|
|
392
|
+
<toggle value={this.show}>
|
|
393
|
+
<h1>Welcome to mono-jsx!</h1>
|
|
394
|
+
</toggle>
|
|
395
|
+
|
|
396
|
+
<button onClick={toggle}>
|
|
397
|
+
{this.computed(() => this.show ? "Hide" : "Show")}
|
|
398
|
+
</button>
|
|
399
|
+
</div>
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Using `<switch>` Element with State
|
|
405
|
+
|
|
406
|
+
The `<switch>` element renders different content based on the value of a state. Elements with matching `slot` attributes are displayed when their value matches, otherwise default content is shown:
|
|
407
|
+
|
|
408
|
+
```tsx
|
|
409
|
+
function App(this: FC<{ lang: "en" | "zh" | "emoji" }>) {
|
|
410
|
+
this.lang = "en";
|
|
411
|
+
|
|
412
|
+
return (
|
|
413
|
+
<div>
|
|
414
|
+
<switch value={this.lang}>
|
|
415
|
+
<h1 slot="en">Hello, world!</h1>
|
|
416
|
+
<h1 slot="zh">δ½ ε₯½οΌδΈηοΌ</h1>
|
|
417
|
+
<h1>βπβοΈ</h1>
|
|
418
|
+
</switch>
|
|
419
|
+
<p>
|
|
420
|
+
<button onClick={() => this.lang = "en"}>English</button>
|
|
421
|
+
<button onClick={() => this.lang = "zh"}>δΈζ</button>
|
|
422
|
+
<button onClick={() => this.lang = "emoji"}>Emoji</button>
|
|
423
|
+
</p>
|
|
424
|
+
</div>
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### Limitation of State
|
|
381
430
|
|
|
382
|
-
1\.
|
|
431
|
+
1\. Component state cannot be used in arrow function components.
|
|
383
432
|
|
|
384
433
|
```tsx
|
|
385
434
|
// β Won't work - state updates won't refresh the view
|
|
@@ -405,7 +454,7 @@ function App(this: FC) {
|
|
|
405
454
|
}
|
|
406
455
|
```
|
|
407
456
|
|
|
408
|
-
2\.
|
|
457
|
+
2\. Component state cannot be computed outside of the `this.computed` method.
|
|
409
458
|
|
|
410
459
|
```tsx
|
|
411
460
|
// β Won't work - state updates won't refresh the view
|
|
@@ -435,58 +484,82 @@ function App(this: FC) {
|
|
|
435
484
|
}
|
|
436
485
|
```
|
|
437
486
|
|
|
438
|
-
##
|
|
487
|
+
## Using `this` in Components
|
|
439
488
|
|
|
440
|
-
mono-jsx
|
|
489
|
+
mono-jsx binds a special `this` object to your components when they are rendered. This object contains properties and methods that you can use to manage state, context, and other features.
|
|
441
490
|
|
|
442
|
-
|
|
491
|
+
The `this` object contains the following properties:
|
|
443
492
|
|
|
444
|
-
|
|
493
|
+
- `app`: The app state defined on the root `<html>` element.
|
|
494
|
+
- `context`: The context defined on the root `<html>` element.
|
|
495
|
+
- `request`: The request object from the `fetch` handler.
|
|
496
|
+
- `computed`: A method to create computed properties based on state.
|
|
445
497
|
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
|
|
498
|
+
```ts
|
|
499
|
+
type FC<State = {}, AppState = {}, Context = {}> = {
|
|
500
|
+
readonly app: AppState;
|
|
501
|
+
readonly context: Context;
|
|
502
|
+
readonly request: Request;
|
|
503
|
+
readonly computed: <V = unknown>(computeFn: () => V) => V;
|
|
504
|
+
} & Omit<State, "app" | "context" | "request" | "computed">;
|
|
505
|
+
```
|
|
449
506
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
507
|
+
### Using State
|
|
508
|
+
|
|
509
|
+
Check the [Using State](#using-state) section for more details on how to use state in your components.
|
|
510
|
+
|
|
511
|
+
### Using Context
|
|
453
512
|
|
|
513
|
+
You can use the `context` property in `this` to access context values in your components. The context is defined on the root `<html>` element:
|
|
514
|
+
|
|
515
|
+
```tsx
|
|
516
|
+
function Dash(this: FC<{}, {}, { auth: { uuid: string; name: string } }>) {
|
|
517
|
+
const { auth } = this.context;
|
|
454
518
|
return (
|
|
455
519
|
<div>
|
|
456
|
-
<
|
|
457
|
-
|
|
458
|
-
</toggle>
|
|
459
|
-
|
|
460
|
-
<button onClick={toggle}>
|
|
461
|
-
{this.computed(() => this.show ? "Hide" : "Show")}
|
|
462
|
-
</button>
|
|
520
|
+
<h1>Welcome back, {auth.name}!</h1>
|
|
521
|
+
<p>Your UUID is {auth.uuid}</p>
|
|
463
522
|
</div>
|
|
464
523
|
);
|
|
465
524
|
}
|
|
525
|
+
|
|
526
|
+
export default {
|
|
527
|
+
fetch: async (req) => {
|
|
528
|
+
const auth = await doAuth(req);
|
|
529
|
+
return (
|
|
530
|
+
<html context={{ auth }} request={req}>
|
|
531
|
+
{!auth && <p>Please Login</p>}
|
|
532
|
+
{auth && <Dash />}
|
|
533
|
+
</html>
|
|
534
|
+
);
|
|
535
|
+
},
|
|
536
|
+
};
|
|
466
537
|
```
|
|
467
538
|
|
|
468
|
-
###
|
|
539
|
+
### Accessing Request Info
|
|
469
540
|
|
|
470
|
-
|
|
541
|
+
You can access request information in components via the `request` property in `this` which is set on the root `<html>` element:
|
|
471
542
|
|
|
472
543
|
```tsx
|
|
473
|
-
function
|
|
474
|
-
|
|
475
|
-
|
|
544
|
+
function RequestInfo(this: FC) {
|
|
545
|
+
const { request } = this;
|
|
476
546
|
return (
|
|
477
547
|
<div>
|
|
478
|
-
<
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
</switch>
|
|
483
|
-
|
|
484
|
-
<button onClick={() => this.lang = "en"}>English</button>
|
|
485
|
-
<button onClick={() => this.lang = "zh"}>δΈζ</button>
|
|
486
|
-
<button onClick={() => this.lang = "emoji"}>Emoji</button>
|
|
548
|
+
<h1>Request Info</h1>
|
|
549
|
+
<p>{request.method}</p>
|
|
550
|
+
<p>{request.url}</p>
|
|
551
|
+
<p>{request.headers.get("user-agent")}</p>
|
|
487
552
|
</div>
|
|
488
553
|
);
|
|
489
554
|
}
|
|
555
|
+
|
|
556
|
+
export default {
|
|
557
|
+
fetch: (req) => (
|
|
558
|
+
<html request={req}>
|
|
559
|
+
<RequestInfo />
|
|
560
|
+
</html>
|
|
561
|
+
),
|
|
562
|
+
};
|
|
490
563
|
```
|
|
491
564
|
|
|
492
565
|
## Streaming Rendering
|
|
@@ -533,61 +606,24 @@ export default {
|
|
|
533
606
|
};
|
|
534
607
|
```
|
|
535
608
|
|
|
536
|
-
|
|
609
|
+
You can add the `catch` attribute to handle errors in async components. The `catch` attribute should be a function that returns a JSX element:
|
|
537
610
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const { auth } = this.context;
|
|
543
|
-
return (
|
|
544
|
-
<div>
|
|
545
|
-
<h1>Welcome back, {auth.name}!</h1>
|
|
546
|
-
<p>Your UUID is {auth.uuid}</p>
|
|
547
|
-
</div>
|
|
548
|
-
);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
export default {
|
|
552
|
-
fetch: async (req) => {
|
|
553
|
-
const auth = await doAuth(req);
|
|
554
|
-
return (
|
|
555
|
-
<html context={{ auth }} request={req}>
|
|
556
|
-
{!auth && <p>Please Login</p>}
|
|
557
|
-
{auth && <Dash />}
|
|
558
|
-
</html>
|
|
559
|
-
);
|
|
560
|
-
},
|
|
561
|
-
};
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
## Accessing Request Info
|
|
565
|
-
|
|
566
|
-
You can access request information in components via the `request` property in `this` which is set on the root `<html>` element:
|
|
567
|
-
|
|
568
|
-
```tsx
|
|
569
|
-
function RequestInfo(this: FC) {
|
|
570
|
-
const { request } = this;
|
|
571
|
-
return (
|
|
572
|
-
<div>
|
|
573
|
-
<h1>Request Info</h1>
|
|
574
|
-
<p>{request.method}</p>
|
|
575
|
-
<p>{request.url}</p>
|
|
576
|
-
<p>{request.headers.get("user-agent")}</p>
|
|
577
|
-
</div>
|
|
578
|
-
);
|
|
611
|
+
```jsx
|
|
612
|
+
async function Hello() {
|
|
613
|
+
throw new Error("Something went wrong!");
|
|
614
|
+
return <p>Hello world!</p>;
|
|
579
615
|
}
|
|
580
616
|
|
|
581
617
|
export default {
|
|
582
618
|
fetch: (req) => (
|
|
583
|
-
<html
|
|
584
|
-
<
|
|
619
|
+
<html>
|
|
620
|
+
<Hello catch={err => <p>{err.messaage}</p>} />
|
|
585
621
|
</html>
|
|
586
622
|
),
|
|
587
623
|
};
|
|
588
624
|
```
|
|
589
625
|
|
|
590
|
-
## Customizing Response
|
|
626
|
+
## Customizing html Response
|
|
591
627
|
|
|
592
628
|
Add `status` or `headers` attributes to the root `<html>` element to customize the response:
|
|
593
629
|
|
|
@@ -607,7 +643,7 @@ export default {
|
|
|
607
643
|
};
|
|
608
644
|
```
|
|
609
645
|
|
|
610
|
-
|
|
646
|
+
### Using htmx
|
|
611
647
|
|
|
612
648
|
mono-jsx integrates with [htmx](https://htmx.org/) and [typed-htmx](https://github.com/Desdaemon/typed-htmx). To use htmx, add the `htmx` attribute to the root `<html>` element:
|
|
613
649
|
|
|
@@ -635,7 +671,7 @@ export default {
|
|
|
635
671
|
};
|
|
636
672
|
```
|
|
637
673
|
|
|
638
|
-
|
|
674
|
+
#### Adding htmx Extensions
|
|
639
675
|
|
|
640
676
|
You can add htmx [extensions](https://htmx.org/docs/#extensions) by adding the `htmx-ext-*` attribute to the root `<html>` element:
|
|
641
677
|
|
|
@@ -651,7 +687,7 @@ export default {
|
|
|
651
687
|
};
|
|
652
688
|
```
|
|
653
689
|
|
|
654
|
-
|
|
690
|
+
#### Specifying htmx Version
|
|
655
691
|
|
|
656
692
|
You can specify the htmx version by setting the `htmx` attribute to a specific version:
|
|
657
693
|
|
|
@@ -667,7 +703,7 @@ export default {
|
|
|
667
703
|
};
|
|
668
704
|
```
|
|
669
705
|
|
|
670
|
-
|
|
706
|
+
#### Installing htmx Manually
|
|
671
707
|
|
|
672
708
|
By default, mono-jsx installs htmx from [esm.sh](https://esm.sh/) CDN when you set the `htmx` attribute. You can also install htmx manually with your own CDN or local copy:
|
|
673
709
|
|
package/jsx-runtime.mjs
CHANGED
|
@@ -5,61 +5,16 @@ var $html = Symbol.for("jsx.html");
|
|
|
5
5
|
var $state = Symbol.for("mono.state");
|
|
6
6
|
var $computed = Symbol.for("mono.computed");
|
|
7
7
|
|
|
8
|
-
// state.ts
|
|
9
|
-
var collectDeps;
|
|
10
|
-
function createState(fc, appState, context, request) {
|
|
11
|
-
const computed = (fn) => {
|
|
12
|
-
const deps = /* @__PURE__ */ Object.create(null);
|
|
13
|
-
collectDeps = (fc2, key, value2) => deps[fc2 + ":" + key] = value2;
|
|
14
|
-
const value = fn.call(proxy);
|
|
15
|
-
collectDeps = void 0;
|
|
16
|
-
if (value instanceof Promise || deps.size === 0) return value;
|
|
17
|
-
return [$computed, { value, deps, fn: fn.toString(), fc }, $vnode];
|
|
18
|
-
};
|
|
19
|
-
const proxy = new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
20
|
-
get(target, key, receiver) {
|
|
21
|
-
switch (key) {
|
|
22
|
-
case "app":
|
|
23
|
-
return appState;
|
|
24
|
-
case "context":
|
|
25
|
-
return context ?? {};
|
|
26
|
-
case "request":
|
|
27
|
-
if (!request) {
|
|
28
|
-
throw new Error("request is not defined");
|
|
29
|
-
}
|
|
30
|
-
return request;
|
|
31
|
-
case "computed":
|
|
32
|
-
return computed;
|
|
33
|
-
default: {
|
|
34
|
-
const value = Reflect.get(target, key, receiver);
|
|
35
|
-
if (typeof key === "symbol") {
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
if (collectDeps) {
|
|
39
|
-
collectDeps(fc, key, value);
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
return [$state, { key, value, fc }, $vnode];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
set(target, key, value, receiver) {
|
|
47
|
-
return Reflect.set(target, key, value, receiver);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
return proxy;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
8
|
// runtime/index.ts
|
|
54
|
-
var STATE_JS = `const
|
|
55
|
-
var SUSPENSE_JS = `const n={},o=e=>e.getAttribute("chunk-id");c("m-portal",e=>{n[o(e)]=e}),c("m-chunk",e=>{const t=o(e),s=n[t];s&&
|
|
9
|
+
var STATE_JS = `const m=new Map,u=e=>m.get(e)??m.set(e,b(e)).get(e),f=(e,t)=>e.getAttribute(t),d=(e,t)=>e.hasAttribute(t),b=e=>{const t=Object.create(null),r=new Map,n=(i,c)=>{let a=c;Object.defineProperty(t,i,{get:()=>a,set:o=>{if(o!==a){const l=r.get(i);l&&queueMicrotask(()=>l.forEach(h=>h())),a=o}}})},s=(i,c)=>{let a=r.get(i);a||(a=[],r.set(i,a)),a.push(c)};return e>0&&Object.defineProperty(t,"app",{get:()=>u(0).store,enumerable:!1,configurable:!1}),{store:t,define:n,watch:s}},g=(e,t,r)=>{if(t==="toggle"){let n;return()=>{if(!n){const s=e.firstElementChild;s&&s.tagName==="TEMPLATE"&&d(s,"m-slot")?(n=s.content.childNodes,e.innerHTML=""):n=e.childNodes}r()?e.append(...n):e.innerHTML=""}}if(t==="switch"){let n=f(e,"match"),s,i,c=o=>s.get(o)??s.set(o,[]).get(o),a;return()=>{if(!s){s=new Map,i=[];for(const o of e.childNodes)if(o.nodeType===1&&o.tagName==="TEMPLATE"&&d(o,"m-slot")){for(const l of o.content.childNodes)l.nodeType===1&&d(l,"slot")?c(f(l,"slot")).push(l):i.push(l);o.remove()}else n?c(n).push(o):i.push(o)}a=r(),e.innerHTML="",e.append(...s.has(a)?s.get(a):i)}}if(t&&t.length>2&&t.startsWith("[")&&t.endsWith("]")){let n=t.slice(1,-1),s=e.parentElement;return s.tagName==="M-GROUP"&&(s=s.previousElementSibling),()=>{const i=r();i===!1?s.removeAttribute(n):(n==="class"||n==="style")&&i&&typeof i=="object"?s.setAttribute(n,n==="class"?$cx(i):$styleToCSS(i)):s.setAttribute(n,i===!0?"":""+i)}}return()=>e.textContent=""+r()},p=e=>{const t=e.indexOf(":");if(t>0)return[Number(e.slice(0,t)),e.slice(t+1)];throw new Error("Invalid state key")};customElements.define("m-state",class extends HTMLElement{connectedCallback(){const e=this,t=f(e,"key");if(t){const r=u(Number(f(e,"fc")));r.watch(t,g(e,f(e,"mode"),()=>r.store[t]));return}}}),Object.assign(window,{$state:e=>e!==void 0?u(e).store:void 0,$defineState:(e,t)=>{const[r,n]=p(e);u(r).define(n,t)},$defineComputed:(e,t,r)=>{const n=document.querySelector("m-state[computed='"+e+"']");if(n){const s=u(Number(f(n,"fc"))).store,i=g(n,f(n,"mode"),t.bind(s));for(const c of r){const[a,o]=p(c);u(a).watch(o,i)}}}});`;
|
|
10
|
+
var SUSPENSE_JS = `const n={},o=e=>e.getAttribute("chunk-id");c("m-portal",e=>{n[o(e)]=e}),c("m-chunk",e=>{setTimeout(()=>{const t=o(e),s=n[t];s&&(s.replaceWith(...e.firstChild.content.childNodes),e.remove(),delete n[t])})});function c(e,t){customElements.define(e,class extends HTMLElement{connectedCallback(){t(this)}})}`;
|
|
56
11
|
var UTILS_JS = {
|
|
57
|
-
/** cx.js (
|
|
58
|
-
cx: `
|
|
59
|
-
/** styleToCSS.js (
|
|
60
|
-
styleToCSS: `
|
|
61
|
-
/** event.js (
|
|
62
|
-
event: `
|
|
12
|
+
/** cx.js (225 bytes) */
|
|
13
|
+
cx: `var n=e=>typeof e=="string",o=e=>typeof e=="object"&&e!==null;function t(e){return n(e)?e:o(e)?Array.isArray(e)?e.map(t).filter(Boolean).join(" "):Object.entries(e).filter(([,r])=>!!r).map(([r])=>r).join(" "):""}window.$cx=t;`,
|
|
14
|
+
/** styleToCSS.js (1168 bytes) */
|
|
15
|
+
styleToCSS: `var a=new Set(["animation-iteration-count","aspect-ratio","border-image-outset","border-image-slice","border-image-width","box-flex-group","box-flex","box-ordinal-group","column-count","columns","fill-opacity","flex-grow","flex-negative","flex-order","flex-positive","flex-shrink","flex","flood-opacity","font-weight","grid-area","grid-column-end","grid-column-span","grid-column-start","grid-column","grid-row-end","grid-row-span","grid-row-start","grid-row","line-clamp","line-height","opacity","order","orphans","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-miterlimit","stroke-opacity","stroke-width","tab-size","widows","z-index","zoom"]),o=e=>typeof e=="string",u=e=>typeof e=="object"&&e!==null,f=e=>e.replace(/[a-z][A-Z]/g,r=>r.charAt(0)+"-"+r.charAt(1).toLowerCase());function p(e){if(o(e))return e;if(u(e)){let r="";for(let[i,t]of Array.isArray(e)?e:Object.entries(e))if(o(i)&&(o(t)||typeof t=="number")){let n=f(i),c=typeof t=="number"?a.has(n)?""+t:t+"px":s(""+t);r+=(r?";":"")+s(n)+":"+(n==="content"?JSON.stringify(c):c)}return r}return""}function s(e){return e.replace(/["<>]/g,r=>r==="<"?"<":r===">"?">":"'")}window.$styleToCSS=p;`,
|
|
16
|
+
/** event.js (347 bytes) */
|
|
17
|
+
event: `var w=window;w.$emit=(e,fn,fc)=>fn.call(w.$state?.(fc)??e.target,e);w.$onsubmit=(e,fn,fc)=>{e.preventDefault();fn.call(w.$state?.(fc)??e.target,new FormData(e.target),e)};w.$onstage=()=>document.querySelectorAll("[onmount]").forEach(t=>{const k="onmount",j=t.getAttribute(k);t.removeAttribute(k);new Function("event",j)({type:"mount",target:t})});`
|
|
63
18
|
};
|
|
64
19
|
|
|
65
20
|
// runtime/utils.ts
|
|
@@ -130,35 +85,36 @@ function styleToCSS(style) {
|
|
|
130
85
|
if (isObject(style)) {
|
|
131
86
|
let css = "";
|
|
132
87
|
for (const [k, v] of Array.isArray(style) ? style : Object.entries(style)) {
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
88
|
+
if (isString(k) && (isString(v) || typeof v === "number")) {
|
|
89
|
+
const cssKey = toHyphenCase(k);
|
|
90
|
+
const cssValue = typeof v === "number" ? cssBareUnitProps.has(cssKey) ? "" + v : v + "px" : escapeCSSText("" + v);
|
|
91
|
+
css += (css ? ";" : "") + escapeCSSText(cssKey) + ":" + (cssKey === "content" ? JSON.stringify(cssValue) : cssValue);
|
|
92
|
+
}
|
|
137
93
|
}
|
|
138
94
|
return css;
|
|
139
95
|
}
|
|
140
96
|
return "";
|
|
141
97
|
}
|
|
142
|
-
function escapeCSSText(
|
|
143
|
-
return
|
|
98
|
+
function escapeCSSText(str2) {
|
|
99
|
+
return str2.replace(/["<>]/g, (m) => {
|
|
144
100
|
if (m === "<") return "<";
|
|
145
101
|
if (m === ">") return ">";
|
|
146
102
|
return "'";
|
|
147
103
|
});
|
|
148
104
|
}
|
|
149
105
|
var regexpHtmlSafe = /["'&<>]/;
|
|
150
|
-
function escapeHTML(
|
|
151
|
-
if (typeof Bun === "object" && "escapeHTML" in Bun) return Bun.escapeHTML(
|
|
152
|
-
const match = regexpHtmlSafe.exec(
|
|
106
|
+
function escapeHTML(str2) {
|
|
107
|
+
if (typeof Bun === "object" && "escapeHTML" in Bun) return Bun.escapeHTML(str2);
|
|
108
|
+
const match = regexpHtmlSafe.exec(str2);
|
|
153
109
|
if (!match) {
|
|
154
|
-
return
|
|
110
|
+
return str2;
|
|
155
111
|
}
|
|
156
112
|
let escape;
|
|
157
113
|
let index;
|
|
158
114
|
let lastIndex = 0;
|
|
159
115
|
let html2 = "";
|
|
160
|
-
for (index = match.index; index <
|
|
161
|
-
switch (
|
|
116
|
+
for (index = match.index; index < str2.length; index++) {
|
|
117
|
+
switch (str2.charCodeAt(index)) {
|
|
162
118
|
case 34:
|
|
163
119
|
escape = """;
|
|
164
120
|
break;
|
|
@@ -178,21 +134,66 @@ function escapeHTML(str) {
|
|
|
178
134
|
continue;
|
|
179
135
|
}
|
|
180
136
|
if (lastIndex !== index) {
|
|
181
|
-
html2 +=
|
|
137
|
+
html2 += str2.slice(lastIndex, index);
|
|
182
138
|
}
|
|
183
139
|
lastIndex = index + 1;
|
|
184
140
|
html2 += escape;
|
|
185
141
|
}
|
|
186
|
-
return lastIndex !== index ? html2 +
|
|
142
|
+
return lastIndex !== index ? html2 + str2.slice(lastIndex, index) : html2;
|
|
187
143
|
}
|
|
188
144
|
|
|
189
145
|
// render.ts
|
|
190
146
|
var encoder = new TextEncoder();
|
|
191
147
|
var regexpHtmlTag = /^[a-z][\w\-$]*$/;
|
|
192
148
|
var selfClosingTags = new Set("area,base,br,col,embed,hr,img,input,keygen,link,meta,param,source,track,wbr".split(","));
|
|
149
|
+
var cdn = "https://raw.esm.sh";
|
|
193
150
|
var isVNode = (v) => Array.isArray(v) && v.length === 3 && v[2] === $vnode;
|
|
194
151
|
var hashCode = (s) => [...s].reduce((hash, c) => Math.imul(31, hash) + c.charCodeAt(0) | 0, 0);
|
|
195
|
-
var toAttrStringLit = (
|
|
152
|
+
var toAttrStringLit = (str2) => '"' + escapeHTML(str2) + '"';
|
|
153
|
+
var str = (v, str2) => v !== void 0 ? str2(v) : "";
|
|
154
|
+
var collectDeps;
|
|
155
|
+
function createThis(fc, appState, context, request) {
|
|
156
|
+
const computed = (fn) => {
|
|
157
|
+
const deps = /* @__PURE__ */ Object.create(null);
|
|
158
|
+
collectDeps = (fc2, key, value2) => deps[fc2 + ":" + key] = value2;
|
|
159
|
+
const value = fn.call(thisProxy);
|
|
160
|
+
collectDeps = void 0;
|
|
161
|
+
if (value instanceof Promise || Object.keys(deps).length === 0) return value;
|
|
162
|
+
return [$computed, { value, deps, fc, fn }, $vnode];
|
|
163
|
+
};
|
|
164
|
+
const thisProxy = new Proxy(/* @__PURE__ */ Object.create(null), {
|
|
165
|
+
get(target, key, receiver) {
|
|
166
|
+
switch (key) {
|
|
167
|
+
case "app":
|
|
168
|
+
return appState;
|
|
169
|
+
case "context":
|
|
170
|
+
return context ?? {};
|
|
171
|
+
case "request":
|
|
172
|
+
if (!request) {
|
|
173
|
+
throw new Error("request is not defined");
|
|
174
|
+
}
|
|
175
|
+
return request;
|
|
176
|
+
case "computed":
|
|
177
|
+
return computed;
|
|
178
|
+
default: {
|
|
179
|
+
const value = Reflect.get(target, key, receiver);
|
|
180
|
+
if (typeof key === "symbol") {
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
if (collectDeps) {
|
|
184
|
+
collectDeps(fc, key, value);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
return [$state, { key, value, fc }, $vnode];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
set(target, key, value, receiver) {
|
|
192
|
+
return Reflect.set(target, key, value, receiver);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
return thisProxy;
|
|
196
|
+
}
|
|
196
197
|
async function renderNode(rc, node, stripSlotProp) {
|
|
197
198
|
const { write } = rc;
|
|
198
199
|
switch (typeof node) {
|
|
@@ -207,7 +208,7 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
207
208
|
if (isVNode(node)) {
|
|
208
209
|
const [tag, props] = node;
|
|
209
210
|
const { stateStore } = rc;
|
|
210
|
-
if (tag === $fragment
|
|
211
|
+
if (tag === $fragment) {
|
|
211
212
|
if (props.children !== void 0) {
|
|
212
213
|
await renderChildren(rc, props.children);
|
|
213
214
|
}
|
|
@@ -230,10 +231,8 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
230
231
|
break;
|
|
231
232
|
}
|
|
232
233
|
if (tag === $computed) {
|
|
233
|
-
const { deps, value,
|
|
234
|
-
write(
|
|
235
|
-
'<m-state fc="' + fc + '" computed><script type="computed">$(' + fn + ", " + JSON.stringify(Object.keys(deps)) + ")<\/script>"
|
|
236
|
-
);
|
|
234
|
+
const { deps, value, fc } = props;
|
|
235
|
+
write('<m-state fc="' + fc + '" computed="' + rc.mcs.gen(node) + '">');
|
|
237
236
|
if (value !== void 0) {
|
|
238
237
|
write(escapeHTML("" + value));
|
|
239
238
|
}
|
|
@@ -264,14 +263,14 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
264
263
|
if (tag === "toggle") {
|
|
265
264
|
const { value: valueProp, children } = props;
|
|
266
265
|
if (children !== void 0) {
|
|
267
|
-
if (isVNode(valueProp) && valueProp[0] === $state || valueProp[0] === $computed) {
|
|
268
|
-
const { key, deps, value,
|
|
266
|
+
if (isVNode(valueProp) && (valueProp[0] === $state || valueProp[0] === $computed)) {
|
|
267
|
+
const { key, deps, value, fc } = valueProp[1];
|
|
269
268
|
write('<m-state mode="toggle" fc="' + fc + '" ');
|
|
270
269
|
if (key) {
|
|
271
270
|
write("key=" + toAttrStringLit(key) + ">");
|
|
272
271
|
stateStore.set(fc + ":" + key, !!value);
|
|
273
272
|
} else {
|
|
274
|
-
write('computed
|
|
273
|
+
write('computed="' + rc.mcs.gen(valueProp) + '">');
|
|
275
274
|
for (const [key2, value2] of Object.entries(deps)) {
|
|
276
275
|
if (!stateStore.has(key2)) {
|
|
277
276
|
stateStore.set(key2, value2);
|
|
@@ -300,14 +299,14 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
300
299
|
let computed;
|
|
301
300
|
let valueOrDefault;
|
|
302
301
|
if (isVNode(valueProp) && (valueProp[0] === $state || valueProp[0] === $computed)) {
|
|
303
|
-
const { key, deps, value,
|
|
302
|
+
const { key, deps, value, fc } = valueProp[1];
|
|
304
303
|
valueOrDefault = value ?? defaultValue;
|
|
305
304
|
stateful = '<m-state mode="switch" fc="' + fc + '" ';
|
|
306
305
|
if (key) {
|
|
307
306
|
stateful += "key=" + toAttrStringLit(key) + ">";
|
|
308
307
|
stateStore.set(fc + ":" + key, valueOrDefault);
|
|
309
308
|
} else {
|
|
310
|
-
stateful += 'computed
|
|
309
|
+
stateful += 'computed="' + rc.mcs.gen(valueProp) + '">';
|
|
311
310
|
for (const [key2, value2] of Object.entries(deps)) {
|
|
312
311
|
if (!stateStore.has(key2)) {
|
|
313
312
|
stateStore.set(key2, value2);
|
|
@@ -358,49 +357,56 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
358
357
|
break;
|
|
359
358
|
}
|
|
360
359
|
if (typeof tag === "function") {
|
|
361
|
-
const fcIndex = ++rc.
|
|
362
|
-
const { rendering, placeholder, catch: catchFC
|
|
360
|
+
const fcIndex = ++rc.status.fcIndex;
|
|
361
|
+
const { children, rendering, placeholder, catch: catchFC } = props;
|
|
363
362
|
try {
|
|
364
|
-
const v = tag.call(
|
|
365
|
-
const { children } = fcProps;
|
|
363
|
+
const v = tag.call(createThis(fcIndex, rc.appState, rc.context, rc.request), props);
|
|
366
364
|
const fcSlots = children !== void 0 ? Array.isArray(children) ? isVNode(children) ? [children] : children : [children] : void 0;
|
|
367
|
-
const eager = (rendering ?? tag.rendering) === "eager"
|
|
365
|
+
const eager = (rendering ?? tag.rendering) === "eager";
|
|
368
366
|
if (v instanceof Promise) {
|
|
369
367
|
if (eager) {
|
|
370
|
-
await renderNode({ ...rc, fcIndex,
|
|
368
|
+
await renderNode({ ...rc, fcIndex, fcSlots }, await v);
|
|
371
369
|
} else {
|
|
372
|
-
const
|
|
373
|
-
rc.suspenses.push(v.then(async (
|
|
374
|
-
|
|
375
|
-
await renderNode({
|
|
376
|
-
|
|
370
|
+
const chunkIdAttr = 'chunk-id="' + (rc.status.chunkIndex++).toString(36) + '"';
|
|
371
|
+
rc.suspenses.push(v.then(async (node2) => {
|
|
372
|
+
let buf = "<m-chunk " + chunkIdAttr + "><template>";
|
|
373
|
+
await renderNode({
|
|
374
|
+
...rc,
|
|
375
|
+
fcIndex,
|
|
376
|
+
fcSlots,
|
|
377
|
+
write: (chunk) => {
|
|
378
|
+
buf += chunk;
|
|
379
|
+
}
|
|
380
|
+
}, node2);
|
|
381
|
+
return buf + "</template></m-chunk>";
|
|
377
382
|
}));
|
|
378
|
-
write(
|
|
383
|
+
write("<m-portal " + chunkIdAttr + ">");
|
|
379
384
|
if (placeholder) {
|
|
380
|
-
await renderNode({ ...rc, fcIndex
|
|
385
|
+
await renderNode({ ...rc, fcIndex }, placeholder);
|
|
381
386
|
}
|
|
382
387
|
write("</m-portal>");
|
|
383
388
|
}
|
|
384
|
-
} else if (isObject(v) && Symbol.iterator in v && !isVNode(v)) {
|
|
385
|
-
for (const c of v) {
|
|
386
|
-
await renderNode({ ...rc, fcIndex, eager, fcSlots }, c);
|
|
387
|
-
}
|
|
388
389
|
} else if (isObject(v) && Symbol.asyncIterator in v) {
|
|
389
390
|
if (eager) {
|
|
390
391
|
for await (const c of v) {
|
|
391
|
-
await renderNode({ ...rc, fcIndex,
|
|
392
|
+
await renderNode({ ...rc, fcIndex, fcSlots }, c);
|
|
392
393
|
}
|
|
393
394
|
} else {
|
|
394
395
|
}
|
|
396
|
+
} else if (isObject(v) && Symbol.iterator in v && !isVNode(v)) {
|
|
397
|
+
for (const node2 of v) {
|
|
398
|
+
await renderNode({ ...rc, fcIndex, fcSlots }, node2);
|
|
399
|
+
}
|
|
395
400
|
} else {
|
|
396
|
-
await renderNode({ ...rc, fcIndex,
|
|
401
|
+
await renderNode({ ...rc, fcIndex, fcSlots }, v);
|
|
397
402
|
}
|
|
398
403
|
} catch (err) {
|
|
399
404
|
if (err instanceof Error) {
|
|
400
405
|
if (typeof catchFC === "function") {
|
|
401
|
-
await renderNode({ ...rc, fcIndex
|
|
406
|
+
await renderNode({ ...rc, fcIndex }, catchFC(err));
|
|
402
407
|
} else {
|
|
403
|
-
write('<pre style="color:red;font-size:1rem"><code>' + escapeHTML(err.
|
|
408
|
+
write('<pre style="color:red;font-size:1rem"><code>' + escapeHTML(err.message) + "</code></pre>");
|
|
409
|
+
console.error(err);
|
|
404
410
|
}
|
|
405
411
|
}
|
|
406
412
|
}
|
|
@@ -408,32 +414,31 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
408
414
|
}
|
|
409
415
|
if (isString(tag) && regexpHtmlTag.test(tag)) {
|
|
410
416
|
let buffer = "<" + tag;
|
|
411
|
-
let
|
|
412
|
-
let onMountHandler;
|
|
417
|
+
let stateTags = "";
|
|
413
418
|
for (let [propName, propValue] of Object.entries(props)) {
|
|
414
419
|
if (propName === "children") {
|
|
415
420
|
continue;
|
|
416
421
|
}
|
|
417
422
|
if (isVNode(propValue) && (propValue[0] === $state || propValue[0] === $computed)) {
|
|
418
|
-
const { key, value, deps,
|
|
423
|
+
const { key, value, deps, fc } = propValue[1];
|
|
419
424
|
if (propName === "class") {
|
|
420
|
-
rc.
|
|
425
|
+
rc.status.cx = true;
|
|
421
426
|
} else if (propName === "style") {
|
|
422
|
-
rc.
|
|
427
|
+
rc.status.styleToCSS = true;
|
|
423
428
|
}
|
|
424
|
-
|
|
429
|
+
stateTags += "<m-state mode=" + toAttrStringLit("[" + propName + "]") + ' fc="' + fc + '" ';
|
|
425
430
|
if (key) {
|
|
426
|
-
|
|
431
|
+
stateTags += "key=" + toAttrStringLit(key) + ">";
|
|
427
432
|
stateStore.set(fc + ":" + key, value);
|
|
428
433
|
} else {
|
|
429
|
-
|
|
434
|
+
stateTags += 'computed="' + rc.mcs.gen(propValue) + '">';
|
|
430
435
|
for (const [key2, value2] of Object.entries(deps)) {
|
|
431
436
|
if (!stateStore.has(key2)) {
|
|
432
437
|
stateStore.set(key2, value2);
|
|
433
438
|
}
|
|
434
439
|
}
|
|
435
440
|
}
|
|
436
|
-
|
|
441
|
+
stateTags += "</m-state>";
|
|
437
442
|
propValue = value;
|
|
438
443
|
}
|
|
439
444
|
switch (propName) {
|
|
@@ -469,19 +474,19 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
469
474
|
if (pseudoStyles.length > 0 || atRuleStyles.length > 0 || nestingStyles.length > 0) {
|
|
470
475
|
let css = "";
|
|
471
476
|
let raw = "";
|
|
472
|
-
let styleIds;
|
|
473
477
|
let id;
|
|
474
478
|
let cssSelector;
|
|
479
|
+
let cssIds;
|
|
475
480
|
if (style.length > 0) {
|
|
476
481
|
css = styleToCSS(style);
|
|
477
482
|
raw += css + "|";
|
|
478
483
|
}
|
|
479
484
|
raw += [pseudoStyles, atRuleStyles, nestingStyles].flat(1).map(([k, v]) => k + ">" + v).join("|");
|
|
480
|
-
styleIds = rc.styleIds ?? (rc.styleIds = /* @__PURE__ */ new Set());
|
|
481
485
|
id = hashCode(raw).toString(36);
|
|
482
486
|
cssSelector = "[data-css-" + id + "]";
|
|
483
|
-
|
|
484
|
-
|
|
487
|
+
cssIds = rc.cssIds ?? (rc.cssIds = /* @__PURE__ */ new Set());
|
|
488
|
+
if (!cssIds.has(id)) {
|
|
489
|
+
cssIds.add(id);
|
|
485
490
|
if (css) {
|
|
486
491
|
css = cssSelector + "{" + css + "}";
|
|
487
492
|
}
|
|
@@ -504,14 +509,13 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
504
509
|
break;
|
|
505
510
|
case "onMount":
|
|
506
511
|
if (typeof propValue === "function") {
|
|
507
|
-
|
|
512
|
+
rc.status.onmount++;
|
|
513
|
+
buffer += ' onmount="$emit(event,' + rc.mfs.gen(propValue) + str(rc.fcIndex, (i) => "," + i) + ')"';
|
|
508
514
|
}
|
|
509
515
|
break;
|
|
510
516
|
case "action":
|
|
511
517
|
if (typeof propValue === "function" && tag === "form") {
|
|
512
|
-
|
|
513
|
-
write("<script>function " + fn + "(fd){(" + propValue.toString() + ")(fd)}<\/script>");
|
|
514
|
-
buffer += ' onsubmit="$onsubmit(event,this,' + fn + (rc.fcIndex !== void 0 ? "," + rc.fcIndex : "") + ')"';
|
|
518
|
+
buffer += ' onsubmit="$onsubmit(event,' + rc.mfs.gen(propValue) + str(rc.fcIndex, (i) => "," + i) + ')"';
|
|
515
519
|
} else if (isString(propValue)) {
|
|
516
520
|
buffer += " action=" + toAttrStringLit(propValue);
|
|
517
521
|
}
|
|
@@ -525,9 +529,7 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
525
529
|
if (regexpHtmlTag.test(propName) && propValue !== void 0) {
|
|
526
530
|
if (propName.startsWith("on")) {
|
|
527
531
|
if (typeof propValue === "function") {
|
|
528
|
-
|
|
529
|
-
write("<script>function " + fn + "(e){(" + propValue.toString() + ")(e)}<\/script>");
|
|
530
|
-
buffer += " " + propName.toLowerCase() + '="$emit(event,this,' + fn + (rc.fcIndex !== void 0 ? "," + rc.fcIndex : "") + ')"';
|
|
532
|
+
buffer += " " + propName.toLowerCase() + '="$emit(event,' + rc.mfs.gen(propValue) + str(rc.fcIndex, (i) => "," + i) + ')"';
|
|
531
533
|
}
|
|
532
534
|
} else if (typeof propValue === "boolean") {
|
|
533
535
|
if (propValue) {
|
|
@@ -541,8 +543,8 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
541
543
|
}
|
|
542
544
|
write(buffer + ">");
|
|
543
545
|
if (!selfClosingTags.has(tag)) {
|
|
544
|
-
if (
|
|
545
|
-
write(
|
|
546
|
+
if (stateTags) {
|
|
547
|
+
write(stateTags);
|
|
546
548
|
}
|
|
547
549
|
if (props.innerHTML) {
|
|
548
550
|
write(props.innerHTML);
|
|
@@ -550,16 +552,12 @@ async function renderNode(rc, node, stripSlotProp) {
|
|
|
550
552
|
await renderChildren(rc, props.children);
|
|
551
553
|
}
|
|
552
554
|
write("</" + tag + ">");
|
|
553
|
-
} else if (
|
|
554
|
-
write("<m-group>" +
|
|
555
|
-
}
|
|
556
|
-
if (onMountHandler) {
|
|
557
|
-
rc.index.mf++;
|
|
558
|
-
write(
|
|
559
|
-
'<script>{const target=document.currentScript.previousElementSibling;addEventListener("load",()=>$emit({type:"mount",currentTarget:target,target},target,' + onMountHandler.toString() + (rc.fcIndex !== void 0 ? "," + rc.fcIndex : "") + "))}<\/script>"
|
|
560
|
-
);
|
|
555
|
+
} else if (stateTags) {
|
|
556
|
+
write("<m-group>" + stateTags + "</m-group>");
|
|
561
557
|
}
|
|
562
558
|
}
|
|
559
|
+
} else if (Array.isArray(node) && node.length > 0) {
|
|
560
|
+
renderChildren(rc, node, stripSlotProp);
|
|
563
561
|
}
|
|
564
562
|
break;
|
|
565
563
|
}
|
|
@@ -573,6 +571,21 @@ async function renderChildren(rc, children, stripSlotProp) {
|
|
|
573
571
|
await renderNode(rc, children, stripSlotProp);
|
|
574
572
|
}
|
|
575
573
|
}
|
|
574
|
+
var IdGenImpl = class extends Map {
|
|
575
|
+
constructor(_prefix) {
|
|
576
|
+
super();
|
|
577
|
+
this._prefix = _prefix;
|
|
578
|
+
}
|
|
579
|
+
_id = 0;
|
|
580
|
+
gen(v) {
|
|
581
|
+
let id = this.get(v);
|
|
582
|
+
if (id === void 0) {
|
|
583
|
+
id = this._prefix + (this._id++).toString(36);
|
|
584
|
+
this.set(v, id);
|
|
585
|
+
}
|
|
586
|
+
return id;
|
|
587
|
+
}
|
|
588
|
+
};
|
|
576
589
|
function render(node, renderOptions = {}) {
|
|
577
590
|
const { request, status, headers: headersInit } = renderOptions;
|
|
578
591
|
const headers = new Headers();
|
|
@@ -596,64 +609,99 @@ function render(node, renderOptions = {}) {
|
|
|
596
609
|
return new Response(
|
|
597
610
|
new ReadableStream({
|
|
598
611
|
async start(controller) {
|
|
599
|
-
const { appState: appStateInit, context,
|
|
612
|
+
const { appState: appStateInit, context, htmx } = renderOptions;
|
|
600
613
|
const write = (chunk) => controller.enqueue(encoder.encode(chunk));
|
|
601
|
-
const appState =
|
|
614
|
+
const appState = createThis(0, null, context, request);
|
|
602
615
|
const stateStore = /* @__PURE__ */ new Map();
|
|
603
616
|
const suspenses = [];
|
|
604
|
-
const rtComponents = { cx: false, styleToCSS: false };
|
|
605
617
|
const rc = {
|
|
606
618
|
write,
|
|
619
|
+
suspenses,
|
|
607
620
|
context,
|
|
608
621
|
request,
|
|
609
622
|
appState,
|
|
610
623
|
stateStore,
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
eager: rendering === "eager"
|
|
624
|
+
mcs: new IdGenImpl("$MC_"),
|
|
625
|
+
mfs: new IdGenImpl("$MF_"),
|
|
626
|
+
status: { fcIndex: 0, chunkIndex: 0, onmount: 0 }
|
|
615
627
|
};
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
if (value !== void 0) {
|
|
619
|
-
appState[key] = value;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
try {
|
|
624
|
-
write("<!DOCTYPE html>");
|
|
625
|
-
await renderNode(rc, node);
|
|
628
|
+
const runtimeJS = {};
|
|
629
|
+
const finalize = async () => {
|
|
626
630
|
let js = "";
|
|
627
|
-
if (rc.
|
|
631
|
+
if (rc.status.cx && !runtimeJS.cx) {
|
|
632
|
+
runtimeJS.cx = true;
|
|
633
|
+
js += UTILS_JS.cx;
|
|
634
|
+
}
|
|
635
|
+
if (rc.status.styleToCSS && !runtimeJS.styleToCSS) {
|
|
636
|
+
runtimeJS.styleToCSS = true;
|
|
637
|
+
js += UTILS_JS.styleToCSS;
|
|
638
|
+
}
|
|
639
|
+
if (rc.mfs.size > 0 && !runtimeJS.event) {
|
|
640
|
+
runtimeJS.event = true;
|
|
628
641
|
js += UTILS_JS.event;
|
|
629
642
|
}
|
|
643
|
+
if (suspenses.length > 0 && !runtimeJS.suspense) {
|
|
644
|
+
runtimeJS.suspense = true;
|
|
645
|
+
js += SUSPENSE_JS;
|
|
646
|
+
}
|
|
647
|
+
if (stateStore.size > 0 && !runtimeJS.state) {
|
|
648
|
+
runtimeJS.state = true;
|
|
649
|
+
js += STATE_JS;
|
|
650
|
+
}
|
|
651
|
+
if (js) {
|
|
652
|
+
write("<script>/* runtime.js (generated by mono-jsx) */(()=>{" + js + "})()<\/script>");
|
|
653
|
+
}
|
|
654
|
+
js = "";
|
|
655
|
+
if (rc.mfs.size > 0) {
|
|
656
|
+
for (const [fn, fname] of rc.mfs.entries()) {
|
|
657
|
+
js += "function " + fname + "(){(" + fn.toString() + ").apply(this,arguments)};";
|
|
658
|
+
}
|
|
659
|
+
rc.mfs.clear();
|
|
660
|
+
}
|
|
630
661
|
if (stateStore.size > 0) {
|
|
631
|
-
|
|
632
|
-
js +=
|
|
662
|
+
for (const [key, value] of stateStore.entries()) {
|
|
663
|
+
js += "$defineState(" + JSON.stringify(key) + (value !== void 0 ? "," + JSON.stringify(value) : "") + ");";
|
|
633
664
|
}
|
|
634
|
-
|
|
635
|
-
|
|
665
|
+
stateStore.clear();
|
|
666
|
+
}
|
|
667
|
+
if (rc.mcs.size > 0) {
|
|
668
|
+
for (const [vnode, fname] of rc.mcs.entries()) {
|
|
669
|
+
const { fn, deps } = vnode[1];
|
|
670
|
+
js += '$defineComputed("' + fname + '",function(){return(' + fn.toString() + ").call(this)}," + JSON.stringify(Object.keys(deps)) + ");";
|
|
636
671
|
}
|
|
637
|
-
|
|
638
|
-
js += "for(let[k,v]of" + JSON.stringify(Array.from(stateStore.entries()).map((e) => e[1] === void 0 ? [e[0]] : e)) + ")$defineState(k,v);";
|
|
672
|
+
rc.mcs.clear();
|
|
639
673
|
}
|
|
640
|
-
if (
|
|
641
|
-
|
|
674
|
+
if (rc.status.onmount > 0) {
|
|
675
|
+
rc.status.onmount = 0;
|
|
676
|
+
js += "$onstage();";
|
|
642
677
|
}
|
|
643
678
|
if (js) {
|
|
644
|
-
write("<script
|
|
679
|
+
write("<script>/* app.js (generated by mono-jsx) */" + js + "<\/script>");
|
|
680
|
+
}
|
|
681
|
+
if (suspenses.length > 0) {
|
|
682
|
+
await Promise.all(suspenses.splice(0, suspenses.length).map((suspense) => suspense.then(write)));
|
|
683
|
+
await finalize();
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
if (appStateInit) {
|
|
687
|
+
for (const [key, value] of Object.entries(appStateInit)) {
|
|
688
|
+
if (value !== void 0) {
|
|
689
|
+
appState[key] = value;
|
|
690
|
+
}
|
|
645
691
|
}
|
|
692
|
+
}
|
|
693
|
+
try {
|
|
694
|
+
write("<!DOCTYPE html>");
|
|
695
|
+
await renderNode(rc, node);
|
|
646
696
|
if (htmx) {
|
|
647
|
-
write(`<script src="
|
|
697
|
+
write(`<script src="${cdn}/htmx.org${htmx === true ? "" : escapeHTML("@" + htmx)}/dist/htmx.min.js"><\/script>`);
|
|
648
698
|
for (const [key, value] of Object.entries(renderOptions)) {
|
|
649
699
|
if (key.startsWith("htmx-ext-") && value) {
|
|
650
|
-
write(`<script src="
|
|
700
|
+
write(`<script src="${cdn}/${key}${value === true ? "" : escapeHTML("@" + value)}"><\/script>`);
|
|
651
701
|
}
|
|
652
702
|
}
|
|
653
703
|
}
|
|
654
|
-
|
|
655
|
-
await Promise.all(suspenses);
|
|
656
|
-
}
|
|
704
|
+
await finalize();
|
|
657
705
|
} finally {
|
|
658
706
|
controller.close();
|
|
659
707
|
}
|
package/package.json
CHANGED
package/types/css.d.ts
CHANGED
|
@@ -6107,7 +6107,8 @@ export interface StandardShorthandProperties<TLength = (string & {}) | 0, TTime
|
|
|
6107
6107
|
}
|
|
6108
6108
|
|
|
6109
6109
|
export interface StandardProperties<TLength = (string & {}) | 0, TTime = string & {}>
|
|
6110
|
-
extends StandardLonghandProperties<TLength, TTime>, StandardShorthandProperties<TLength, TTime>
|
|
6110
|
+
extends StandardLonghandProperties<TLength, TTime>, StandardShorthandProperties<TLength, TTime>
|
|
6111
|
+
{}
|
|
6111
6112
|
|
|
6112
6113
|
export interface VendorLonghandProperties<TLength = (string & {}) | 0, TTime = string & {}> {
|
|
6113
6114
|
/**
|
|
@@ -8018,7 +8019,8 @@ export interface VendorShorthandProperties<TLength = (string & {}) | 0, TTime =
|
|
|
8018
8019
|
}
|
|
8019
8020
|
|
|
8020
8021
|
export interface VendorProperties<TLength = (string & {}) | 0, TTime = string & {}>
|
|
8021
|
-
extends VendorLonghandProperties<TLength, TTime>, VendorShorthandProperties<TLength, TTime>
|
|
8022
|
+
extends VendorLonghandProperties<TLength, TTime>, VendorShorthandProperties<TLength, TTime>
|
|
8023
|
+
{}
|
|
8022
8024
|
|
|
8023
8025
|
export interface ObsoleteProperties<TLength = (string & {}) | 0, TTime = string & {}> {
|
|
8024
8026
|
/**
|
|
@@ -9143,7 +9145,8 @@ export interface Properties<TLength = (string & {}) | 0, TTime = string & {}>
|
|
|
9143
9145
|
StandardProperties<TLength, TTime>,
|
|
9144
9146
|
VendorProperties<TLength, TTime>,
|
|
9145
9147
|
ObsoleteProperties<TLength, TTime>,
|
|
9146
|
-
SvgProperties<TLength, TTime>
|
|
9148
|
+
SvgProperties<TLength, TTime>
|
|
9149
|
+
{}
|
|
9147
9150
|
|
|
9148
9151
|
export type AtRules =
|
|
9149
9152
|
| "@charset"
|
package/types/html.d.ts
CHANGED
|
@@ -822,7 +822,7 @@ export namespace HTML {
|
|
|
822
822
|
|
|
823
823
|
interface EventAttributes<T extends EventTarget> {
|
|
824
824
|
// mono-jsx specific
|
|
825
|
-
onMount?: (
|
|
825
|
+
onMount?: (event: { type: "mount"; target: T }) => void | Promise<void>;
|
|
826
826
|
|
|
827
827
|
// Input Events
|
|
828
828
|
onBeforeInput?: EventHandler<Event, T>;
|
package/types/jsx.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import type * as Mono from "./mono.d.ts";
|
|
4
4
|
import type { HTML } from "./html.d.ts";
|
|
5
5
|
|
|
6
|
-
export type ChildType = VNode | string | number | bigint | boolean | null;
|
|
6
|
+
export type ChildType = VNode | VNode[] | string | number | bigint | boolean | null;
|
|
7
7
|
|
|
8
8
|
export type VNode = readonly [
|
|
9
9
|
tag: string | symbol | FC<any>,
|
|
@@ -13,14 +13,9 @@ export type VNode = readonly [
|
|
|
13
13
|
|
|
14
14
|
export interface FC<P = {}> {
|
|
15
15
|
(props: P): ChildType | Promise<ChildType> | Generator<ChildType> | AsyncGenerator<ChildType>;
|
|
16
|
-
displayName?: string;
|
|
17
16
|
rendering?: string;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
|
-
export interface TC {
|
|
21
|
-
(strings: TemplateStringsArray, ...values: unknown[]): VNode;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
19
|
declare global {
|
|
25
20
|
namespace JSX {
|
|
26
21
|
type ElementType<P = any> =
|
|
@@ -28,9 +23,9 @@ declare global {
|
|
|
28
23
|
[K in keyof IntrinsicElements]: P extends IntrinsicElements[K] ? K : never;
|
|
29
24
|
}[keyof IntrinsicElements]
|
|
30
25
|
| FC<P>;
|
|
26
|
+
type Raw = (strings: TemplateStringsArray, ...values: unknown[]) => JSX.Element;
|
|
31
27
|
interface Element extends VNode, Response {}
|
|
32
28
|
interface IntrinsicAttributes extends Mono.BaseAttributes, Mono.AsyncComponentAttributes {}
|
|
33
29
|
interface IntrinsicElements extends HTML.Elements, HTML.SVGElements, Mono.Elements {}
|
|
34
30
|
}
|
|
35
|
-
var html: TC, css: TC, js: TC;
|
|
36
31
|
}
|
package/types/mono.d.ts
CHANGED
|
@@ -66,45 +66,73 @@ export interface CSSProperties extends BaseCSSProperties, AtRuleCSSProperties, P
|
|
|
66
66
|
[key: `&${" " | "." | "["}${string}`]: CSSProperties;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
export type ChildType = JSX.Element | string | number | bigint | boolean | null;
|
|
69
|
+
export type ChildType = JSX.Element | (JSX.Element | string | null)[] | string | number | bigint | boolean | null;
|
|
70
70
|
|
|
71
71
|
export interface BaseAttributes {
|
|
72
|
-
children?: ChildType |
|
|
72
|
+
children?: ChildType | ChildType[];
|
|
73
73
|
key?: string | number;
|
|
74
74
|
slot?: string;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
export interface AsyncComponentAttributes {
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Try to catch errors in the component.
|
|
80
|
+
*/
|
|
80
81
|
catch?: (err: any) => JSX.Element;
|
|
82
|
+
/**
|
|
83
|
+
* The loading spinner for the async component.
|
|
84
|
+
*/
|
|
85
|
+
placeholder?: JSX.Element;
|
|
86
|
+
/**
|
|
87
|
+
* Rendering mode
|
|
88
|
+
* - `eager`: render async component eagerly
|
|
89
|
+
*/
|
|
90
|
+
rendering?: "eager";
|
|
81
91
|
}
|
|
82
92
|
|
|
83
93
|
export interface Elements {
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
/**
|
|
95
|
+
* The `<toggle>` element is a custom element that represents a toggle switch.
|
|
96
|
+
*/
|
|
97
|
+
toggle: BaseAttributes & {
|
|
98
|
+
value?: boolean | string | number | null;
|
|
86
99
|
};
|
|
87
|
-
|
|
100
|
+
/**
|
|
101
|
+
* The `<switch>` element is a custom element that represents a switch.
|
|
102
|
+
*/
|
|
103
|
+
switch: BaseAttributes & {
|
|
88
104
|
value?: string;
|
|
89
105
|
defaultValue?: string;
|
|
90
106
|
};
|
|
91
|
-
cache: {
|
|
92
|
-
/** The cache key is used to identify the cache. */
|
|
93
|
-
key: string;
|
|
94
|
-
/** The `etag` (or **entity tag**) is an identifier for a specific version of a rendering cache. */
|
|
95
|
-
etag?: string;
|
|
96
|
-
/** The `max-age=N` prop indicates that the cache remains fresh until N seconds after the cache is generated. */
|
|
97
|
-
maxAge?: number;
|
|
98
|
-
/** The `stale-while-revalidate` prop indicates that the cache could reuse a stale rendering while it revalidates it to a cache. */
|
|
99
|
-
swr?: number;
|
|
100
|
-
};
|
|
101
107
|
}
|
|
102
108
|
|
|
103
109
|
declare global {
|
|
104
|
-
|
|
110
|
+
/**
|
|
111
|
+
* The `html` function is used to create XSS-unsafe HTML elements.
|
|
112
|
+
*/
|
|
113
|
+
var html: JSX.Raw, css: JSX.Raw, js: JSX.Raw;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* mono-jsx `this` object that is bound to the function component.
|
|
117
|
+
*/
|
|
118
|
+
type FC<State = {}, AppState = {}, Context = {}> = {
|
|
119
|
+
/**
|
|
120
|
+
* Application state.
|
|
121
|
+
* This is the state that is shared across the entire application.
|
|
122
|
+
*/
|
|
105
123
|
readonly app: AppState;
|
|
124
|
+
/**
|
|
125
|
+
* Context object.
|
|
126
|
+
*/
|
|
106
127
|
readonly context: Context;
|
|
128
|
+
/**
|
|
129
|
+
* Current request object.
|
|
130
|
+
*/
|
|
107
131
|
readonly request: Request;
|
|
132
|
+
/**
|
|
133
|
+
* The `computed` function is used to create a computed property.
|
|
134
|
+
* It takes a function that returns a value and returns the value.
|
|
135
|
+
*/
|
|
108
136
|
readonly computed: <V = unknown>(computeFn: () => V) => V;
|
|
109
|
-
} & Omit<
|
|
137
|
+
} & Omit<State, "app" | "context" | "request" | "computed">;
|
|
110
138
|
}
|
package/types/render.d.ts
CHANGED
|
@@ -41,11 +41,6 @@ export interface RenderOptions extends Partial<HtmxExts> {
|
|
|
41
41
|
lastModified?: string;
|
|
42
42
|
setCookie?: string;
|
|
43
43
|
};
|
|
44
|
-
/**
|
|
45
|
-
* Rendering mode.
|
|
46
|
-
* - **eager**: Render the component immediately.
|
|
47
|
-
*/
|
|
48
|
-
rendering?: "eager";
|
|
49
44
|
/**
|
|
50
45
|
* Install htmx script with the given version.
|
|
51
46
|
* @see https://htmx.org/
|