ziex 0.0.1-dev.7 → 0.1.0-dev.517
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 +87 -41
- package/index.js +18 -6
- package/package.json +2 -2
- package/react/dom.d.ts +72 -45
- package/react/index.d.ts +1 -1
- package/react/index.js +93 -8
- package/react/types.d.ts +28 -31
- package/wasm/index.d.ts +20 -2
- package/wasm/index.js +80 -17
- package/zx.d.ts +1 -0
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# ZX
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A full-stack web framework for Zig. Write declarative UI components using familiar JSX patterns, transpiled to efficient Zig code.
|
|
4
4
|
|
|
5
|
-
ZX combines the power and performance of Zig with the expressiveness of JSX, enabling you to build fast, type-safe web applications.
|
|
5
|
+
ZX combines the power and performance of Zig with the expressiveness of JSX, enabling you to build fast, type-safe web applications.
|
|
6
6
|
|
|
7
|
-
**[
|
|
7
|
+
**[Documentation →](https://ziex.dev/learn)**
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -31,7 +31,7 @@ winget install -e --id zig.zig # Windows
|
|
|
31
31
|
pub fn QuickExample(allocator: zx.Allocator) zx.Component {
|
|
32
32
|
const is_loading = true;
|
|
33
33
|
const chars = "Hello, ZX Dev!";
|
|
34
|
-
|
|
34
|
+
var i: usize = 0;
|
|
35
35
|
return (
|
|
36
36
|
<main @allocator={allocator}>
|
|
37
37
|
<section>
|
|
@@ -39,7 +39,7 @@ pub fn QuickExample(allocator: zx.Allocator) zx.Component {
|
|
|
39
39
|
</section>
|
|
40
40
|
|
|
41
41
|
<section>
|
|
42
|
-
{for (chars) |char| (<span>{
|
|
42
|
+
{for (chars) |char| (<span>{char}</span>)}
|
|
43
43
|
</section>
|
|
44
44
|
|
|
45
45
|
<section>
|
|
@@ -47,6 +47,10 @@ pub fn QuickExample(allocator: zx.Allocator) zx.Component {
|
|
|
47
47
|
<Profile name={user.name} age={user.age} role={user.role} />
|
|
48
48
|
)}
|
|
49
49
|
</section>
|
|
50
|
+
|
|
51
|
+
<section>
|
|
52
|
+
{while (i < 10) : (i += 1) (<p>{i}</p>)}
|
|
53
|
+
</section>
|
|
50
54
|
</main>
|
|
51
55
|
);
|
|
52
56
|
}
|
|
@@ -55,7 +59,7 @@ fn Profile(allocator: zx.Allocator, user: User) zx.Component {
|
|
|
55
59
|
return (
|
|
56
60
|
<div @allocator={allocator}>
|
|
57
61
|
<h1>{user.name}</h1>
|
|
58
|
-
<p>{
|
|
62
|
+
<p>{user.age}</p>
|
|
59
63
|
{switch (user.role) {
|
|
60
64
|
.admin => (<p>Admin</p>),
|
|
61
65
|
.member => (<p>Member</p>),
|
|
@@ -77,10 +81,19 @@ const zx = @import("zx");
|
|
|
77
81
|
## Feature Checklist
|
|
78
82
|
|
|
79
83
|
- [x] Server Side Rendering (SSR)
|
|
84
|
+
- [ ] `on`event handler
|
|
85
|
+
- [x] Streaming
|
|
80
86
|
- [x] Static Site Generation (SSG)
|
|
87
|
+
- [ ] `getStaticParams`, `getStaticProps`
|
|
81
88
|
- [ ] Client Side Rendering (CSR) via WebAssembly (_WIP_)
|
|
89
|
+
- [x] Virtual DOM and diffing
|
|
90
|
+
- [x] Rendering only changed nodes
|
|
91
|
+
- [x] `on`event handler
|
|
92
|
+
- [x] State managment
|
|
93
|
+
- [x] Hydration
|
|
94
|
+
- [ ] Lifecycle hook
|
|
95
|
+
- [ ] Server Actions
|
|
82
96
|
- [x] Client Side Rendering (CSR) via React
|
|
83
|
-
- [x] Type Safety
|
|
84
97
|
- [x] Routing
|
|
85
98
|
- [x] File-system Routing
|
|
86
99
|
- [x] Search Parameters
|
|
@@ -88,65 +101,98 @@ const zx = @import("zx");
|
|
|
88
101
|
- [x] Components
|
|
89
102
|
- [x] Control Flow
|
|
90
103
|
- [x] `if`
|
|
91
|
-
- [ ] `if` nested
|
|
92
104
|
- [x] `if/else`
|
|
93
|
-
- [x] `if/else` nested
|
|
94
105
|
- [x] `for`
|
|
95
|
-
- [x] `for` nested
|
|
96
106
|
- [x] `switch`
|
|
97
|
-
- [x] `switch` nested
|
|
98
107
|
- [x] `while`
|
|
99
|
-
- [x]
|
|
108
|
+
- [x] nesting control flows
|
|
109
|
+
- [x] error/optional captures in `while` and `if`
|
|
100
110
|
- [x] Assets
|
|
101
111
|
- [x] Copying
|
|
102
112
|
- [x] Serving
|
|
103
113
|
- [ ] Assets Optimization
|
|
104
114
|
- [ ] Image
|
|
105
|
-
- [
|
|
106
|
-
- [
|
|
107
|
-
- [
|
|
108
|
-
- [ ] Middleware
|
|
109
|
-
- [ ]
|
|
110
|
-
- [
|
|
115
|
+
- [x] CSS (via plugins such as Tailwind)
|
|
116
|
+
- [x] JS/TS (via esbuild)
|
|
117
|
+
- [x] HTML (optimized by default)
|
|
118
|
+
- [ ] Middleware (_cancalled_)
|
|
119
|
+
- [ ] Caching (configurable)
|
|
120
|
+
- [x] Component
|
|
121
|
+
- [ ] Layout
|
|
122
|
+
- [x] Page
|
|
123
|
+
- [ ] Assets
|
|
124
|
+
- [ ] API Route
|
|
125
|
+
- [ ] Plugin (_Alpha_)
|
|
126
|
+
- [x] Builtin TailwindCSS and Esbuild
|
|
127
|
+
- [x] Command based plugin system
|
|
128
|
+
- [ ] Source based plugin system
|
|
129
|
+
- [ ] Context (configurable)
|
|
130
|
+
- [ ] App
|
|
131
|
+
- [ ] Layout
|
|
132
|
+
- [ ] Page
|
|
133
|
+
- [x] Component
|
|
134
|
+
- [x] `error.zx` for default and per-route error page
|
|
135
|
+
- [x] `notfound.zx` for default and per-route error page
|
|
111
136
|
- [x] CLI
|
|
112
137
|
- [x] `init` Project Template
|
|
113
138
|
- [x] `transpile` Transpile .zx files to Zig source code
|
|
114
139
|
- [x] `serve` Serve the project
|
|
115
140
|
- [x] `dev` HMR or Rebuild on Change
|
|
116
|
-
- [x] `fmt` Format the ZX source code
|
|
141
|
+
- [x] `fmt` Format the ZX source code
|
|
117
142
|
- [x] `export` Generate static site assets
|
|
118
143
|
- [x] `bundle` Bundle the ZX executable with public/assets and exe
|
|
119
144
|
- [x] `version` Show the version of the ZX CLI
|
|
120
145
|
- [x] `update` Update the version of ZX dependency
|
|
121
146
|
- [x] `upgrade` Upgrade the version of ZX CLI
|
|
147
|
+
- [ ] Platform
|
|
148
|
+
- [x] Server
|
|
149
|
+
- [x] Browser
|
|
150
|
+
- [ ] iOS
|
|
151
|
+
- [ ] Android
|
|
152
|
+
- [ ] macOS
|
|
153
|
+
- [ ] Windows
|
|
122
154
|
|
|
123
155
|
#### Editor Support
|
|
124
156
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- [x] Auto Format
|
|
129
|
-
|
|
130
|
-
* Neovim
|
|
131
|
-
- [ ] Syntax Highlighting
|
|
132
|
-
- [ ] LSP Support
|
|
133
|
-
- [ ] Auto Format
|
|
157
|
+
- ##### [VSCode](https://marketplace.visualstudio.com/items?itemName=nurulhudaapon.zx)/[VSCode Forks](https://marketplace.visualstudio.com/items?itemName=nurulhudaapon.zx) Extension
|
|
158
|
+
- ##### [Neovim](/editors/neovim/)
|
|
159
|
+
- ##### [Zed](/editors/zed/)
|
|
134
160
|
|
|
135
161
|
## Similar Projects
|
|
136
162
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
* [
|
|
140
|
-
* [
|
|
141
|
-
* [
|
|
142
|
-
* [
|
|
143
|
-
* [
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
* [
|
|
148
|
-
* [
|
|
149
|
-
* [
|
|
163
|
+
### Rust
|
|
164
|
+
|
|
165
|
+
* [Leptos](https://github.com/leptos-rs/leptos) - Full-stack, isomorphic Rust web framework with fine-grained reactivity and JSX-like syntax
|
|
166
|
+
* [Dioxus](https://github.com/DioxusLabs/dioxus) - Cross-platform GUI framework with React-like API, supporting web, desktop, mobile, and SSR
|
|
167
|
+
* [Yew](https://github.com/yewstack/yew) - Rust / Wasm framework for creating reliable and efficient web applications with component-based architecture
|
|
168
|
+
* [Sycamore](https://github.com/sycamore-rs/sycamore) - Reactive web framework with fine-grained reactivity and minimal bundle sizes
|
|
169
|
+
* [Perseus](https://github.com/framesurge/perseus) - Full-stack framework built on Sycamore with SSR, SSG, and incremental regeneration
|
|
170
|
+
|
|
171
|
+
### Zig
|
|
172
|
+
|
|
173
|
+
* [Jetzig](https://github.com/jetzig-framework/jetzig) - Zig web framework with MVC architecture, built-in ORM, and powerful templating
|
|
174
|
+
* [ZTS](https://github.com/zigster64/zts) - Zig Templates made Simple, a templating system for Zig
|
|
175
|
+
* [zmpl](https://github.com/jetzig-framework/zmpl) - Mode-based templating language that compiles to Zig functions at build time, used in Jetzig
|
|
176
|
+
* [mustache-zig](https://github.com/batiati/mustache-zig) - Mustache template engine implementation in Zig
|
|
177
|
+
* [etch](https://github.com/haze/etch) - Compile-time tuned templating engine focusing on speed and simplicity
|
|
178
|
+
* [Zap](https://github.com/zigzap/zap) - High-performance backend framework in Zig
|
|
179
|
+
* [http.zig](https://github.com/karlseguin/http.zig) - Low-level HTTP/1.1 server written entirely in Zig (_ZX_'s backend)
|
|
180
|
+
* [tokamak](https://github.com/cztomsik/tokamak) - Server-side framework for Zig
|
|
181
|
+
* [zig-router](https://github.com/Cloudef/zig-router) - Straightforward HTTP-like request routing library for Zig
|
|
182
|
+
* [zig-webui](https://github.com/webui-dev/zig-webui/) - Zig library that allows using any web browser as a GUI
|
|
183
|
+
* [Zine](https://github.com/kristoff-it/zine) - Fast, scalable, flexible static site generator (SSG) written in Zig
|
|
184
|
+
* [Zinc](https://github.com/zon-dev/zinc/) - Web framework written in pure Zig with focus on high performance, usability, security, and extensibility
|
|
185
|
+
* [zUI](https://github.com/thienpow/zui) - UI kit for Jetzig framework with reusable components and styles
|
|
186
|
+
* [zig-pek](https://github.com/nektro/zig-pek) - Comptime HTML/XML parser and renderer in Zig
|
|
187
|
+
* [zigomponent](https://zigomp.prjct.dev/) - HTML compoenents in pure zig
|
|
188
|
+
|
|
189
|
+
## Related Projects
|
|
190
|
+
|
|
191
|
+
* [Codeberg Mirror](https://codeberg.org/nurulhudaapon/zx) - ZX repository mirror on Codeberg
|
|
192
|
+
* [zx-vscode](https://github.com/nurulhudaapon/zx-vscode) - Official VSCode/Cursor extension with syntax highlighting, LSP support, and auto-formatting
|
|
193
|
+
* [ziex.dev](https://github.com/nurulhudaapon/zx/tree/main/site) - Official documentation site of ZX made using ZX.
|
|
194
|
+
* [zx-example-portfolio](https://github.com/nurulhudaapon/zx-example-portfolio) - Demo portfolio web application built with ZX
|
|
195
|
+
* [thegates.dev](https://github.com/nurulhudaapon/thegates.dev) - Example clone demonstrating ZX capabilities
|
|
150
196
|
|
|
151
197
|
## Contributing
|
|
152
198
|
|
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var zx = {
|
|
3
3
|
name: "zx",
|
|
4
|
-
version: "0.0
|
|
4
|
+
version: "0.1.0-dev.517",
|
|
5
5
|
description: "ZX is a framework for building web applications with Zig.",
|
|
6
6
|
repository: "https://github.com/nurulhudaapon/zx",
|
|
7
7
|
fingerprint: 14616285862371232000,
|
|
@@ -12,18 +12,30 @@ var zx = {
|
|
|
12
12
|
hash: "httpz-0.0.0-PNVzrBgtBwCVkSJyophIX6WHwDR0r8XhBGQr96Kk-1El"
|
|
13
13
|
},
|
|
14
14
|
zli: {
|
|
15
|
-
|
|
16
|
-
hash: "zli-4.3.0-LeUjpu_fAABOSVASSCW2fFh8SFVNHrxQGDXGPNzcSE_i"
|
|
15
|
+
path: "vendor/cliz"
|
|
17
16
|
},
|
|
18
17
|
zig_js: {
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
path: "vendor/jsz"
|
|
19
|
+
},
|
|
20
|
+
tree_sitter: {
|
|
21
|
+
path: "vendor/zig-tree-sitter"
|
|
22
|
+
},
|
|
23
|
+
tree_sitter_zx: {
|
|
24
|
+
path: "packages/tree-sitter-zx"
|
|
25
|
+
},
|
|
26
|
+
cachez: {
|
|
27
|
+
path: "vendor/cachez"
|
|
21
28
|
}
|
|
22
29
|
},
|
|
23
30
|
paths: [
|
|
24
31
|
"build.zig",
|
|
25
32
|
"build.zig.zon",
|
|
26
|
-
"src"
|
|
33
|
+
"src",
|
|
34
|
+
"packages/tree-sitter-zx",
|
|
35
|
+
"vendor/cachez",
|
|
36
|
+
"vendor/cliz",
|
|
37
|
+
"vendor/jsz",
|
|
38
|
+
"vendor/zig-tree-sitter"
|
|
27
39
|
]
|
|
28
40
|
};
|
|
29
41
|
export {
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ziex",
|
|
3
|
+
"version": "0.1.0-dev.517",
|
|
3
4
|
"description": "ZX is a framework for building web applications with Zig.",
|
|
4
5
|
"main": "index.js",
|
|
5
6
|
"type": "module",
|
|
@@ -28,6 +29,5 @@
|
|
|
28
29
|
"author": "Nurul Huda (Apon) <me@nurulhudaapon.com>",
|
|
29
30
|
"license": "MIT",
|
|
30
31
|
"module": "index.js",
|
|
31
|
-
"types": "index.d.ts"
|
|
32
|
-
"version": "0.0.1-dev.7"
|
|
32
|
+
"types": "index.d.ts"
|
|
33
33
|
}
|
package/react/dom.d.ts
CHANGED
|
@@ -8,14 +8,15 @@ export type PreparedComponent = {
|
|
|
8
8
|
/**
|
|
9
9
|
* The HTML element where the component should be rendered.
|
|
10
10
|
*
|
|
11
|
-
* This is
|
|
12
|
-
*
|
|
13
|
-
* React will hydrate this element, replacing its contents with the interactive component.
|
|
11
|
+
* This is a container element created between the comment markers. The server-rendered
|
|
12
|
+
* content is moved into this container, and React will hydrate it with the interactive component.
|
|
14
13
|
*
|
|
15
14
|
* @example
|
|
16
15
|
* ```tsx
|
|
17
|
-
* //
|
|
18
|
-
* //
|
|
16
|
+
* // Server-rendered HTML with comment markers:
|
|
17
|
+
* // <!--$zx-abc123-0 CounterComponent {"max_count":10}-->
|
|
18
|
+
* // <button>0</button>
|
|
19
|
+
* // <!--/$zx-abc123-0-->
|
|
19
20
|
*
|
|
20
21
|
* const { domNode } = await prepareComponent(component);
|
|
21
22
|
* createRoot(domNode).render(<Component {...props} />);
|
|
@@ -23,42 +24,28 @@ export type PreparedComponent = {
|
|
|
23
24
|
*/
|
|
24
25
|
domNode: HTMLElement;
|
|
25
26
|
/**
|
|
26
|
-
* Component props parsed from the
|
|
27
|
+
* Component props parsed from the comment marker.
|
|
27
28
|
*
|
|
28
|
-
* Props are extracted from the
|
|
29
|
-
*
|
|
30
|
-
* `dangerouslySetInnerHTML` for React compatibility.
|
|
29
|
+
* Props are extracted from the start comment marker content. The comment format is:
|
|
30
|
+
* `<!--$id name props-->` where props is JSON-encoded.
|
|
31
31
|
*
|
|
32
32
|
* @example
|
|
33
33
|
* ```tsx
|
|
34
34
|
* // Server-rendered HTML:
|
|
35
|
-
* //
|
|
35
|
+
* // <!--$zx-abc123-0 CounterComponent {"max_count":10,"label":"Counter"}-->
|
|
36
|
+
* // <button>0</button>
|
|
37
|
+
* // <!--/$zx-abc123-0-->
|
|
36
38
|
*
|
|
37
39
|
* const { props } = await prepareComponent(component);
|
|
38
|
-
* // props = {
|
|
39
|
-
* // max_count: 10,
|
|
40
|
-
* // label: "Counter",
|
|
41
|
-
* // dangerouslySetInnerHTML: { __html: "<span>0</span>" }
|
|
42
|
-
* // }
|
|
40
|
+
* // props = { max_count: 10, label: "Counter" }
|
|
43
41
|
* ```
|
|
44
42
|
*/
|
|
45
43
|
props: Record<string, any> & {
|
|
46
44
|
/**
|
|
47
45
|
* React's special prop for setting inner HTML directly.
|
|
48
46
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```tsx
|
|
54
|
-
* // In ZX file:
|
|
55
|
-
* <MyComponent @rendering={.csr}>
|
|
56
|
-
* <p>Child content</p>
|
|
57
|
-
* </MyComponent>
|
|
58
|
-
*
|
|
59
|
-
* // Results in:
|
|
60
|
-
* // props.dangerouslySetInnerHTML = { __html: "<p>Child content</p>" }
|
|
61
|
-
* ```
|
|
47
|
+
* May be used when the component has server-rendered children that should
|
|
48
|
+
* be preserved during hydration.
|
|
62
49
|
*/
|
|
63
50
|
dangerouslySetInnerHTML?: {
|
|
64
51
|
__html: string;
|
|
@@ -85,12 +72,12 @@ export type PreparedComponent = {
|
|
|
85
72
|
Component: (props: any) => React.ReactElement;
|
|
86
73
|
};
|
|
87
74
|
/**
|
|
88
|
-
* Prepares a client-side component for hydration by locating its
|
|
89
|
-
* props
|
|
75
|
+
* Prepares a client-side component for hydration by locating its comment markers, extracting
|
|
76
|
+
* props from the marker content, and lazy-loading the component module.
|
|
90
77
|
*
|
|
91
78
|
* This function bridges server-rendered HTML (from ZX's Zig transpiler) and client-side React
|
|
92
|
-
* components. It
|
|
93
|
-
*
|
|
79
|
+
* components. It searches for comment markers in the format `<!--$id name props-->...<!--/$id-->`
|
|
80
|
+
* and extracts the component data from the marker content.
|
|
94
81
|
*
|
|
95
82
|
* @param component - The component metadata containing ID, import function, and other metadata
|
|
96
83
|
* needed to locate and load the component
|
|
@@ -98,8 +85,8 @@ export type PreparedComponent = {
|
|
|
98
85
|
* @returns A Promise that resolves to a `PreparedComponent` object containing the DOM node,
|
|
99
86
|
* parsed props, and the loaded React component function
|
|
100
87
|
*
|
|
101
|
-
* @throws {Error} If the component's
|
|
102
|
-
* happens if the component ID doesn't match any
|
|
88
|
+
* @throws {Error} If the component's comment markers cannot be found in the DOM. This typically
|
|
89
|
+
* happens if the component ID doesn't match any marker, the script runs before
|
|
103
90
|
* the HTML is loaded, or there's a mismatch between server and client metadata
|
|
104
91
|
*
|
|
105
92
|
* @example
|
|
@@ -118,18 +105,58 @@ export type PreparedComponent = {
|
|
|
118
105
|
*
|
|
119
106
|
* @example
|
|
120
107
|
* ```tsx
|
|
121
|
-
* //
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* createRoot(domNode).render(<Component {...props} />);
|
|
126
|
-
* } catch (error) {
|
|
127
|
-
* console.error(`Failed to hydrate ${component.name}:`, error);
|
|
128
|
-
* }
|
|
129
|
-
* }
|
|
130
|
-
*
|
|
131
|
-
* Promise.all(components.map(hydrateComponent));
|
|
108
|
+
* // Server-rendered HTML with comment markers:
|
|
109
|
+
* // <!--$zx-abc123-0 CounterComponent {"max_count":10}-->
|
|
110
|
+
* // <button>0</button>
|
|
111
|
+
* // <!--/$zx-abc123-0-->
|
|
132
112
|
* ```
|
|
133
113
|
*/
|
|
134
114
|
export declare function prepareComponent(component: ComponentMetadata): Promise<PreparedComponent>;
|
|
135
115
|
export declare function filterComponents(components: ComponentMetadata[]): ComponentMetadata[];
|
|
116
|
+
/**
|
|
117
|
+
* Discovered component from DOM traversal.
|
|
118
|
+
* Contains all metadata needed to hydrate the component.
|
|
119
|
+
*/
|
|
120
|
+
export type DiscoveredComponent = {
|
|
121
|
+
id: string;
|
|
122
|
+
name: string;
|
|
123
|
+
props: Record<string, any>;
|
|
124
|
+
container: HTMLElement;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Finds all React component markers in the DOM and returns their metadata.
|
|
128
|
+
*
|
|
129
|
+
* This is a DOM-first approach that:
|
|
130
|
+
* 1. Walks the DOM once to find all `<!--$id-->` markers
|
|
131
|
+
* 2. Reads metadata from companion `<script data-zx="id">` elements
|
|
132
|
+
* 3. Creates containers for React to render into
|
|
133
|
+
*
|
|
134
|
+
* @returns Array of discovered components with their containers and props
|
|
135
|
+
*/
|
|
136
|
+
export declare function discoverComponents(): DiscoveredComponent[];
|
|
137
|
+
/**
|
|
138
|
+
* Component registry mapping component names to their import functions.
|
|
139
|
+
*/
|
|
140
|
+
export type ComponentRegistry = Record<string, () => Promise<(props: any) => React.ReactElement>>;
|
|
141
|
+
/**
|
|
142
|
+
* Hydrates all React components found in the DOM.
|
|
143
|
+
*
|
|
144
|
+
* This is the simplest way to hydrate React islands - it automatically:
|
|
145
|
+
* 1. Discovers all component markers in the DOM
|
|
146
|
+
* 2. Looks up components by name in the registry
|
|
147
|
+
* 3. Renders each component into its container
|
|
148
|
+
*
|
|
149
|
+
* @param registry - Map of component names to import functions
|
|
150
|
+
* @param render - Function to render a component (e.g., `(el, Component, props) => createRoot(el).render(<Component {...props} />)`)
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* import { hydrateAll } from "ziex/react";
|
|
155
|
+
*
|
|
156
|
+
* hydrateAll({
|
|
157
|
+
* CounterComponent: () => import("./Counter"),
|
|
158
|
+
* ToggleComponent: () => import("./Toggle"),
|
|
159
|
+
* });
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export declare function hydrateAll(registry: ComponentRegistry, render: (container: HTMLElement, Component: (props: any) => React.ReactElement, props: Record<string, any>) => void): Promise<void>;
|
package/react/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { prepareComponent, filterComponents, type PreparedComponent } from "./dom";
|
|
1
|
+
export { prepareComponent, filterComponents, discoverComponents, hydrateAll, type PreparedComponent, type DiscoveredComponent, type ComponentRegistry, } from "./dom";
|
|
2
2
|
export type { ComponentMetadata } from "./types";
|
package/react/index.js
CHANGED
|
@@ -1,13 +1,56 @@
|
|
|
1
1
|
// src/react/dom.ts
|
|
2
|
+
function findCommentMarker(id) {
|
|
3
|
+
const startMarker = `$${id}`;
|
|
4
|
+
const endMarker = `/$${id}`;
|
|
5
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT, null);
|
|
6
|
+
let startComment = null;
|
|
7
|
+
let endComment = null;
|
|
8
|
+
let node;
|
|
9
|
+
while (node = walker.nextNode()) {
|
|
10
|
+
const text = node.textContent?.trim() || "";
|
|
11
|
+
if (text === startMarker) {
|
|
12
|
+
startComment = node;
|
|
13
|
+
}
|
|
14
|
+
if (text === endMarker) {
|
|
15
|
+
endComment = node;
|
|
16
|
+
break;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (startComment && endComment) {
|
|
20
|
+
return { startComment, endComment };
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function getComponentMetadata(id) {
|
|
25
|
+
const script = document.querySelector(`script[data-zx="${id}"]`);
|
|
26
|
+
if (script?.textContent) {
|
|
27
|
+
try {
|
|
28
|
+
const data = JSON.parse(script.textContent);
|
|
29
|
+
return { name: data.name || "", props: data.props || {} };
|
|
30
|
+
} catch {}
|
|
31
|
+
}
|
|
32
|
+
return { name: "", props: {} };
|
|
33
|
+
}
|
|
34
|
+
function createContainerBetweenMarkers(startComment, endComment) {
|
|
35
|
+
const container = document.createElement("div");
|
|
36
|
+
container.style.display = "contents";
|
|
37
|
+
let current = startComment.nextSibling;
|
|
38
|
+
while (current && current !== endComment) {
|
|
39
|
+
const next = current.nextSibling;
|
|
40
|
+
container.appendChild(current);
|
|
41
|
+
current = next;
|
|
42
|
+
}
|
|
43
|
+
endComment.parentNode?.insertBefore(container, endComment);
|
|
44
|
+
return container;
|
|
45
|
+
}
|
|
2
46
|
async function prepareComponent(component) {
|
|
3
|
-
const
|
|
4
|
-
if (!
|
|
5
|
-
throw new Error(`
|
|
6
|
-
const props = JSON.parse(domNode.getAttribute("data-props") || "{}");
|
|
7
|
-
const htmlChildren = domNode.getAttribute("data-children") ?? undefined;
|
|
8
|
-
if (htmlChildren) {
|
|
9
|
-
props.dangerouslySetInnerHTML = { __html: htmlChildren };
|
|
47
|
+
const marker = findCommentMarker(component.id);
|
|
48
|
+
if (!marker) {
|
|
49
|
+
throw new Error(`Comment marker for ${component.id} not found`, { cause: component });
|
|
10
50
|
}
|
|
51
|
+
const metadata = getComponentMetadata(component.id);
|
|
52
|
+
const props = metadata.props;
|
|
53
|
+
const domNode = createContainerBetweenMarkers(marker.startComment, marker.endComment);
|
|
11
54
|
const Component = await component.import();
|
|
12
55
|
return { domNode, props, Component };
|
|
13
56
|
}
|
|
@@ -15,7 +58,49 @@ function filterComponents(components) {
|
|
|
15
58
|
const currentPath = window.location.pathname;
|
|
16
59
|
return components.filter((component) => component.route === currentPath || !component.route);
|
|
17
60
|
}
|
|
61
|
+
function discoverComponents() {
|
|
62
|
+
const components = [];
|
|
63
|
+
const scripts = Array.from(document.querySelectorAll("script[data-zx]"));
|
|
64
|
+
for (const script of scripts) {
|
|
65
|
+
const id = script.getAttribute("data-zx");
|
|
66
|
+
if (!id)
|
|
67
|
+
continue;
|
|
68
|
+
let name = "";
|
|
69
|
+
let props = {};
|
|
70
|
+
try {
|
|
71
|
+
const data = JSON.parse(script.textContent || "{}");
|
|
72
|
+
name = data.name || "";
|
|
73
|
+
props = data.props || {};
|
|
74
|
+
} catch {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const marker = findCommentMarker(id);
|
|
78
|
+
if (!marker)
|
|
79
|
+
continue;
|
|
80
|
+
const container = createContainerBetweenMarkers(marker.startComment, marker.endComment);
|
|
81
|
+
components.push({ id, name, props, container });
|
|
82
|
+
}
|
|
83
|
+
return components;
|
|
84
|
+
}
|
|
85
|
+
async function hydrateAll(registry, render) {
|
|
86
|
+
const components = discoverComponents();
|
|
87
|
+
await Promise.all(components.map(async ({ name, props, container }) => {
|
|
88
|
+
const importer = registry[name];
|
|
89
|
+
if (!importer) {
|
|
90
|
+
console.warn(`Component "${name}" not found in registry`);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const Component = await importer();
|
|
95
|
+
render(container, Component, props);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(`Failed to hydrate "${name}":`, error);
|
|
98
|
+
}
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
18
101
|
export {
|
|
19
102
|
prepareComponent,
|
|
20
|
-
|
|
103
|
+
hydrateAll,
|
|
104
|
+
filterComponents,
|
|
105
|
+
discoverComponents
|
|
21
106
|
};
|
package/react/types.d.ts
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Metadata for a client-side component used within a ZX file.
|
|
3
3
|
*
|
|
4
4
|
* This type represents the metadata for components that are marked with the `@rendering` attribute
|
|
5
|
-
* in ZX files. When a component is declared with `@rendering={.
|
|
5
|
+
* in ZX files. When a component is declared with `@rendering={.react}` or `@rendering={.client}` in a
|
|
6
6
|
* `.zx` file, the ZX transpiler generates a `ComponentMetadata` entry that is included in the
|
|
7
7
|
* generated `components` array.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```tsx
|
|
11
11
|
* // In a ZX file (page.zx):
|
|
12
|
-
* <CounterComponent @rendering={.
|
|
12
|
+
* <CounterComponent @rendering={.react} max_count={10} />
|
|
13
13
|
*
|
|
14
14
|
* // Generated components array (components.ts):
|
|
15
15
|
* export const components: ComponentMetadata[] = [
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* name: "CounterComponent",
|
|
18
18
|
* path: "./components/CounterComponent.tsx",
|
|
19
19
|
* id: "zx-dcde04c415da9d1b15ca2690d8b497ae",
|
|
20
|
-
* type: "
|
|
20
|
+
* type: "react",
|
|
21
21
|
* import: () => import('./components/CounterComponent.tsx')
|
|
22
22
|
* }
|
|
23
23
|
* ];
|
|
@@ -44,7 +44,7 @@ export type ComponentMetadata = {
|
|
|
44
44
|
* @example
|
|
45
45
|
* ```tsx
|
|
46
46
|
* // In ZX file:
|
|
47
|
-
* <CounterComponent @rendering={.
|
|
47
|
+
* <CounterComponent @rendering={.react} />
|
|
48
48
|
*
|
|
49
49
|
* // name will be: "CounterComponent"
|
|
50
50
|
* ```
|
|
@@ -54,7 +54,7 @@ export type ComponentMetadata = {
|
|
|
54
54
|
* The file path to the component module.
|
|
55
55
|
*
|
|
56
56
|
* This is the relative or absolute path to the component file that will be dynamically imported
|
|
57
|
-
* at runtime. For CSR components, this typically points to a `.tsx` or `.jsx` file. For
|
|
57
|
+
* at runtime. For CSR components, this typically points to a `.tsx` or `.jsx` file. For Client
|
|
58
58
|
* components, this points to a Zig component file.
|
|
59
59
|
*
|
|
60
60
|
* The path is determined from the `@jsImport` directive in the ZX file, or defaults to
|
|
@@ -64,7 +64,7 @@ export type ComponentMetadata = {
|
|
|
64
64
|
* ```tsx
|
|
65
65
|
* // In ZX file:
|
|
66
66
|
* const CounterComponent = @jsImport("components/Counter.tsx");
|
|
67
|
-
* <CounterComponent @rendering={.
|
|
67
|
+
* <CounterComponent @rendering={.react} />
|
|
68
68
|
*
|
|
69
69
|
* // path will be: "components/Counter.tsx"
|
|
70
70
|
* ```
|
|
@@ -72,7 +72,7 @@ export type ComponentMetadata = {
|
|
|
72
72
|
* @example
|
|
73
73
|
* ```tsx
|
|
74
74
|
* // Without explicit @jsImport:
|
|
75
|
-
* <MyComponent @rendering={.
|
|
75
|
+
* <MyComponent @rendering={.react} />
|
|
76
76
|
*
|
|
77
77
|
* // path will default to: "./MyComponent.tsx"
|
|
78
78
|
* ```
|
|
@@ -85,17 +85,16 @@ export type ComponentMetadata = {
|
|
|
85
85
|
*/
|
|
86
86
|
route: string | null;
|
|
87
87
|
/**
|
|
88
|
-
* A unique
|
|
88
|
+
* A unique identifier for the component's hydration boundary.
|
|
89
89
|
*
|
|
90
90
|
* This ID is generated by hashing the component's path and name using MD5, then formatting it
|
|
91
|
-
* as a hex string with the "zx-" prefix. The ID is used to locate the
|
|
92
|
-
*
|
|
91
|
+
* as a hex string with the "zx-" prefix and a counter suffix. The ID is used to locate the
|
|
92
|
+
* component's comment markers in the DOM during client-side hydration.
|
|
93
93
|
*
|
|
94
|
-
* The ID format is: `zx-{32 hex characters}` (e.g., `zx-dcde04c415da9d1b15ca2690d8b497ae`)
|
|
94
|
+
* The ID format is: `zx-{32 hex characters}-{counter}` (e.g., `zx-dcde04c415da9d1b15ca2690d8b497ae-0`)
|
|
95
95
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* and `data-children` attributes to hydrate the component with the correct props and children.
|
|
96
|
+
* The ZX runtime renders components with comment markers in the format:
|
|
97
|
+
* `<!--$id name props-->...<!--/$id-->`
|
|
99
98
|
*
|
|
100
99
|
* @example
|
|
101
100
|
* ```tsx
|
|
@@ -103,36 +102,34 @@ export type ComponentMetadata = {
|
|
|
103
102
|
* {
|
|
104
103
|
* name: "CounterComponent",
|
|
105
104
|
* path: "./components/Counter.tsx",
|
|
106
|
-
* id: "zx-dcde04c415da9d1b15ca2690d8b497ae"
|
|
105
|
+
* id: "zx-dcde04c415da9d1b15ca2690d8b497ae-0"
|
|
107
106
|
* }
|
|
108
107
|
*
|
|
109
|
-
* // Generated HTML:
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* <!-- Server-rendered content -->
|
|
114
|
-
* </div>
|
|
108
|
+
* // Generated HTML with comment markers:
|
|
109
|
+
* <!--$zx-dcde04c415da9d1b15ca2690d8b497ae-0 CounterComponent {"max_count":10}-->
|
|
110
|
+
* <button>0</button>
|
|
111
|
+
* <!--/$zx-dcde04c415da9d1b15ca2690d8b497ae-0-->
|
|
115
112
|
* ```
|
|
116
113
|
*/
|
|
117
114
|
id: string;
|
|
118
115
|
/**
|
|
119
116
|
* The rendering type of the component, determining how it will be rendered on the client.
|
|
120
117
|
*
|
|
121
|
-
* - **"
|
|
118
|
+
* - **"react"** (Client Side React): The component is a React component that will be rendered
|
|
122
119
|
* using React's client-side rendering. The component file should export a default React
|
|
123
120
|
* component function. This is the most common type for interactive UI components.
|
|
124
121
|
*
|
|
125
|
-
* - **"
|
|
122
|
+
* - **"client"** (Client Side Zig): The component is a Zig component that will be compiled to
|
|
126
123
|
* WebAssembly and rendered on the client side. This allows you to use Zig's performance
|
|
127
124
|
* and type safety for client-side components.
|
|
128
125
|
*
|
|
129
|
-
* The type is determined by the `@rendering` attribute value in the ZX file (`.
|
|
126
|
+
* The type is determined by the `@rendering` attribute value in the ZX file (`.react` or `.client`).
|
|
130
127
|
*
|
|
131
128
|
* @example
|
|
132
129
|
* ```tsx
|
|
133
130
|
* // CSR component (React):
|
|
134
|
-
* <CounterComponent @rendering={.
|
|
135
|
-
* // type: "
|
|
131
|
+
* <CounterComponent @rendering={.react} max_count={10} />
|
|
132
|
+
* // type: "react"
|
|
136
133
|
*
|
|
137
134
|
* // Component file (CounterComponent.tsx):
|
|
138
135
|
* export default function CounterComponent({ max_count }: { max_count: number }) {
|
|
@@ -142,9 +139,9 @@ export type ComponentMetadata = {
|
|
|
142
139
|
*
|
|
143
140
|
* @example
|
|
144
141
|
* ```tsx
|
|
145
|
-
* //
|
|
146
|
-
* <CounterComponent @rendering={.
|
|
147
|
-
* // type: "
|
|
142
|
+
* // Client component (Zig/WASM):
|
|
143
|
+
* <CounterComponent @rendering={.client} />
|
|
144
|
+
* // type: "client"
|
|
148
145
|
*
|
|
149
146
|
* // Component file (CounterComponent.zig):
|
|
150
147
|
* pub fn CounterComponent(allocator: zx.Allocator) zx.Component {
|
|
@@ -152,7 +149,7 @@ export type ComponentMetadata = {
|
|
|
152
149
|
* }
|
|
153
150
|
* ```
|
|
154
151
|
*/
|
|
155
|
-
type: "
|
|
152
|
+
type: "react" | "client";
|
|
156
153
|
/**
|
|
157
154
|
* A lazy-loading function that dynamically imports the component module.
|
|
158
155
|
*
|
|
@@ -161,7 +158,7 @@ export type ComponentMetadata = {
|
|
|
161
158
|
* by only loading components when they are needed.
|
|
162
159
|
*
|
|
163
160
|
* For CSR components, the imported module should export a default React component.
|
|
164
|
-
* For
|
|
161
|
+
* For Client components, the import mechanism depends on the WASM module structure.
|
|
165
162
|
*
|
|
166
163
|
* The function is called during client-side hydration to load and render the component
|
|
167
164
|
* into its corresponding DOM container element.
|
package/wasm/index.d.ts
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
|
+
import { ZigJS } from "../../../../vendor/jsz/js/src";
|
|
2
|
+
export declare const jsz: ZigJS;
|
|
1
3
|
declare class ZXInstance {
|
|
2
4
|
#private;
|
|
3
5
|
exports: WebAssembly.Exports;
|
|
4
6
|
events: Event[];
|
|
5
|
-
actions: Record<string, (eventId: number) => void>;
|
|
6
7
|
constructor({ exports, events }: ZXInstanceOptions);
|
|
7
8
|
addEvent(event: Event): number;
|
|
9
|
+
/**
|
|
10
|
+
* Initialize event delegation on a root element
|
|
11
|
+
* This attaches a single event listener for each event type at the root,
|
|
12
|
+
* and uses __zx_ref to look up the corresponding VElement in WASM
|
|
13
|
+
*/
|
|
14
|
+
initEventDelegation(rootSelector?: string): void;
|
|
15
|
+
getZxRef(element: HTMLElement): number | undefined;
|
|
8
16
|
}
|
|
9
|
-
export declare function init(options?: InitOptions): Promise<
|
|
17
|
+
export declare function init(options?: InitOptions): Promise<WebAssembly.WebAssemblyInstantiatedSource>;
|
|
10
18
|
export type InitOptions = {
|
|
19
|
+
/** URL to the WASM file (default: /assets/main.wasm) */
|
|
11
20
|
url?: string;
|
|
21
|
+
/** CSS selector for the event delegation root element (default: 'body') */
|
|
22
|
+
eventDelegationRoot?: string;
|
|
23
|
+
importObject?: WebAssembly.Imports;
|
|
12
24
|
};
|
|
13
25
|
type ZXInstanceOptions = {
|
|
14
26
|
exports: ZXInstance['exports'];
|
|
@@ -18,5 +30,11 @@ declare global {
|
|
|
18
30
|
interface Window {
|
|
19
31
|
_zx: ZXInstance;
|
|
20
32
|
}
|
|
33
|
+
interface HTMLElement {
|
|
34
|
+
/**
|
|
35
|
+
* The VElement ID of the element
|
|
36
|
+
*/
|
|
37
|
+
__zx_ref?: number;
|
|
38
|
+
}
|
|
21
39
|
}
|
|
22
40
|
export {};
|
package/wasm/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// ../../vendor/jsz/js/src/zigjs.ts
|
|
2
2
|
var NAN_PREFIX = 2146959360;
|
|
3
3
|
var predefined = {
|
|
4
4
|
nan: 0,
|
|
@@ -170,26 +170,60 @@ class ZigJS {
|
|
|
170
170
|
// src/wasm/index.ts
|
|
171
171
|
var DEFAULT_URL = "/assets/main.wasm";
|
|
172
172
|
var MAX_EVENTS = 100;
|
|
173
|
+
var DELEGATED_EVENTS = [
|
|
174
|
+
"click",
|
|
175
|
+
"dblclick",
|
|
176
|
+
"input",
|
|
177
|
+
"change",
|
|
178
|
+
"submit",
|
|
179
|
+
"focus",
|
|
180
|
+
"blur",
|
|
181
|
+
"keydown",
|
|
182
|
+
"keyup",
|
|
183
|
+
"keypress",
|
|
184
|
+
"mouseenter",
|
|
185
|
+
"mouseleave",
|
|
186
|
+
"mousedown",
|
|
187
|
+
"mouseup",
|
|
188
|
+
"mousemove",
|
|
189
|
+
"touchstart",
|
|
190
|
+
"touchend",
|
|
191
|
+
"touchmove",
|
|
192
|
+
"scroll"
|
|
193
|
+
];
|
|
194
|
+
var EVENT_TYPE_MAP = {
|
|
195
|
+
click: 0,
|
|
196
|
+
dblclick: 1,
|
|
197
|
+
input: 2,
|
|
198
|
+
change: 3,
|
|
199
|
+
submit: 4,
|
|
200
|
+
focus: 5,
|
|
201
|
+
blur: 6,
|
|
202
|
+
keydown: 7,
|
|
203
|
+
keyup: 8,
|
|
204
|
+
keypress: 9,
|
|
205
|
+
mouseenter: 10,
|
|
206
|
+
mouseleave: 11,
|
|
207
|
+
mousedown: 12,
|
|
208
|
+
mouseup: 13,
|
|
209
|
+
mousemove: 14,
|
|
210
|
+
touchstart: 15,
|
|
211
|
+
touchend: 16,
|
|
212
|
+
touchmove: 17,
|
|
213
|
+
scroll: 18
|
|
214
|
+
};
|
|
173
215
|
var jsz = new ZigJS;
|
|
174
216
|
var importObject = {
|
|
175
|
-
module: {},
|
|
176
|
-
env: {},
|
|
177
217
|
...jsz.importObject()
|
|
178
218
|
};
|
|
179
219
|
|
|
180
220
|
class ZXInstance {
|
|
181
221
|
exports;
|
|
182
222
|
events;
|
|
183
|
-
|
|
223
|
+
#eventDelegationInitialized = false;
|
|
184
224
|
constructor({ exports, events = [] }) {
|
|
185
225
|
this.exports = exports;
|
|
186
226
|
this.events = events;
|
|
187
|
-
this.actions = {};
|
|
188
|
-
Object.entries(exports).forEach(([name, func]) => {
|
|
189
|
-
if (typeof func !== "function")
|
|
190
|
-
return;
|
|
191
|
-
this.actions[name] = this.#actionWrapper.bind(this, name);
|
|
192
|
-
});
|
|
193
227
|
}
|
|
194
228
|
addEvent(event) {
|
|
195
229
|
if (this.events.length >= MAX_EVENTS)
|
|
@@ -197,23 +231,52 @@ class ZXInstance {
|
|
|
197
231
|
const idx = this.events.push(event);
|
|
198
232
|
return idx - 1;
|
|
199
233
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
234
|
+
initEventDelegation(rootSelector = "body") {
|
|
235
|
+
if (this.#eventDelegationInitialized)
|
|
236
|
+
return;
|
|
237
|
+
const root = document.querySelector(rootSelector);
|
|
238
|
+
if (!root)
|
|
239
|
+
return;
|
|
240
|
+
for (const eventType of DELEGATED_EVENTS) {
|
|
241
|
+
root.addEventListener(eventType, (event) => {
|
|
242
|
+
this.#handleDelegatedEvent(eventType, event);
|
|
243
|
+
}, { passive: eventType.startsWith("touch") || eventType === "scroll" });
|
|
244
|
+
}
|
|
245
|
+
this.#eventDelegationInitialized = true;
|
|
246
|
+
}
|
|
247
|
+
#handleDelegatedEvent(eventType, event) {
|
|
248
|
+
let target = event.target;
|
|
249
|
+
while (target && target !== document.body) {
|
|
250
|
+
const zxRef = target.__zx_ref;
|
|
251
|
+
if (zxRef !== undefined) {
|
|
252
|
+
const eventId = this.addEvent(event);
|
|
253
|
+
const handleEvent = this.exports.handleEvent;
|
|
254
|
+
if (typeof handleEvent === "function") {
|
|
255
|
+
const eventTypeId = EVENT_TYPE_MAP[eventType] ?? 0;
|
|
256
|
+
handleEvent(BigInt(zxRef), eventTypeId, BigInt(eventId));
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
target = target.parentElement;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
getZxRef(element) {
|
|
264
|
+
return element.__zx_ref;
|
|
206
265
|
}
|
|
207
266
|
}
|
|
208
267
|
async function init(options = {}) {
|
|
209
268
|
const url = options?.url ?? DEFAULT_URL;
|
|
210
|
-
const
|
|
269
|
+
const wasmInstiatedSource = await WebAssembly.instantiateStreaming(fetch(url), Object.assign({}, importObject, options.importObject));
|
|
270
|
+
const { instance } = wasmInstiatedSource;
|
|
211
271
|
jsz.memory = instance.exports.memory;
|
|
212
272
|
window._zx = new ZXInstance({ exports: instance.exports });
|
|
273
|
+
window._zx.initEventDelegation(options.eventDelegationRoot ?? "body");
|
|
213
274
|
const main = instance.exports.mainClient;
|
|
214
275
|
if (typeof main === "function")
|
|
215
276
|
main();
|
|
277
|
+
return wasmInstiatedSource;
|
|
216
278
|
}
|
|
217
279
|
export {
|
|
280
|
+
jsz,
|
|
218
281
|
init
|
|
219
282
|
};
|
package/zx.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getZxInfo(): any;
|