@epoch32/visage 1.1.3 → 1.1.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/dist/api/index.js +55 -4085
- package/dist/charts/index.js +48 -2806
- package/dist/devtools/index.js +18 -4771
- package/dist/dom/index.js +32 -1639
- package/dist/form/index.js +13 -1498
- package/dist/index-061jf06a.js +848 -0
- package/dist/index-0madxn60.js +442 -0
- package/dist/index-1xtrtz1t.js +256 -0
- package/dist/index-2zz0g2n2.js +212 -0
- package/dist/index-5s255b3w.js +303 -0
- package/dist/index-6kfe1nr4.js +1179 -0
- package/dist/index-8czhpea8.js +338 -0
- package/dist/index-8x8xz80v.js +727 -0
- package/dist/index-921pn75b.js +0 -0
- package/dist/index-965ryh36.js +537 -0
- package/dist/index-9fvarkrx.js +98 -0
- package/dist/index-a4xns6cc.js +219 -0
- package/dist/index-b5wc752a.js +1696 -0
- package/dist/index-df5e614s.js +334 -0
- package/dist/index-dyvgtp43.js +1094 -0
- package/dist/index-grhdtcb5.js +234 -0
- package/dist/index-h6tjqk6e.js +158 -0
- package/dist/index-js199er4.js +101 -0
- package/dist/index-x2rs3vab.js +124 -0
- package/dist/index-y9bq7h9g.js +1162 -0
- package/dist/index-zr6cvset.js +2092 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +458 -11920
- package/dist/jsx-runtime/index.js +13 -1719
- package/dist/query/index.js +47 -2505
- package/dist/router/index.js +25 -1945
- package/dist/rpc/index.js +39 -3567
- package/dist/server/index.js +11 -1256
- package/dist/signals/index.js +52 -1160
- package/dist/style/index.js +77 -2429
- package/dist/template/index.js +7 -1232
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +150 -4249
- package/dist/ui/tabs.d.ts +23 -1
- package/dist/virtual/index.js +12 -2044
- package/dist/vite/index.js +5 -744
- package/package.json +2 -2
- package/src/dom/render.ts +20 -7
- package/src/index.ts +2 -0
- package/src/signals/computed.ts +19 -12
- package/src/signals/effect.ts +7 -7
- package/src/signals/signal.ts +16 -12
- package/src/signals/system.ts +50 -21
- package/src/ui/index.ts +2 -0
- package/src/ui/tabs.tsx +211 -10
- package/src/ui/utils.ts +1 -3
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
computed2,
|
|
3
|
+
onCleanup2,
|
|
4
|
+
createContext2,
|
|
5
|
+
useContext2,
|
|
6
|
+
signal2
|
|
7
|
+
} from "./index-6kfe1nr4.js";
|
|
8
|
+
import {
|
|
9
|
+
Show2
|
|
10
|
+
} from "./index-5s255b3w.js";
|
|
11
|
+
import {
|
|
12
|
+
jsx2
|
|
13
|
+
} from "./index-9fvarkrx.js";
|
|
14
|
+
// src/router/router.tsx
|
|
15
|
+
var RouterContext = createContext2(null);
|
|
16
|
+
function compilePath(path) {
|
|
17
|
+
const paramNames = [];
|
|
18
|
+
const normalized = path.replace(/\/+$/, "") || "/";
|
|
19
|
+
const regexStr = normalized.replace(/:([a-zA-Z0-9_]+)/g, (_, name) => {
|
|
20
|
+
paramNames.push(name);
|
|
21
|
+
return "([^/]+)";
|
|
22
|
+
}).replace(/\*/g, "(.*)");
|
|
23
|
+
const matcher = new RegExp(`^${regexStr}(?:/)?$`);
|
|
24
|
+
return { matcher, paramNames };
|
|
25
|
+
}
|
|
26
|
+
function matchRoute(routes, pathname, base = "") {
|
|
27
|
+
const cleanPath = base && pathname.startsWith(base) ? pathname.slice(base.length) || "/" : pathname;
|
|
28
|
+
for (const route of routes) {
|
|
29
|
+
const match = cleanPath.match(route.matcher);
|
|
30
|
+
if (match) {
|
|
31
|
+
const params = {};
|
|
32
|
+
const captures = match.slice(1);
|
|
33
|
+
route.paramNames.forEach((name, i) => {
|
|
34
|
+
params[name] = decodeURIComponent(captures[i] ?? "");
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
path: route.path,
|
|
38
|
+
params,
|
|
39
|
+
component: route.component,
|
|
40
|
+
children: route.children
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function Router2(props) {
|
|
47
|
+
const base = props.base ?? "";
|
|
48
|
+
const mode = props.mode ?? "history";
|
|
49
|
+
const getPathname = () => {
|
|
50
|
+
if (typeof window === "undefined" || !window.location)
|
|
51
|
+
return "/";
|
|
52
|
+
if (mode === "hash") {
|
|
53
|
+
const hash = window.location.hash.slice(1) || "/";
|
|
54
|
+
return hash.split("?")[0] || "/";
|
|
55
|
+
}
|
|
56
|
+
return window.location.pathname || "/";
|
|
57
|
+
};
|
|
58
|
+
const getSearch = () => {
|
|
59
|
+
if (typeof window === "undefined" || !window.location)
|
|
60
|
+
return "";
|
|
61
|
+
if (mode === "hash") {
|
|
62
|
+
const hash = window.location.hash.slice(1);
|
|
63
|
+
const queryIdx = hash.indexOf("?");
|
|
64
|
+
return queryIdx !== -1 ? hash.slice(queryIdx) : "";
|
|
65
|
+
}
|
|
66
|
+
return window.location.search || "";
|
|
67
|
+
};
|
|
68
|
+
const getHash = () => {
|
|
69
|
+
if (typeof window === "undefined" || !window.location)
|
|
70
|
+
return "";
|
|
71
|
+
return window.location.hash || "";
|
|
72
|
+
};
|
|
73
|
+
const pathname = signal2(getPathname());
|
|
74
|
+
const search = signal2(getSearch());
|
|
75
|
+
const hash = signal2(getHash());
|
|
76
|
+
const routes = [];
|
|
77
|
+
const registeredRoutes = signal2([]);
|
|
78
|
+
const registerRoute = (route) => {
|
|
79
|
+
routes.push(route);
|
|
80
|
+
registeredRoutes.set([...routes]);
|
|
81
|
+
};
|
|
82
|
+
const syncLocation = () => {
|
|
83
|
+
pathname.set(getPathname());
|
|
84
|
+
search.set(getSearch());
|
|
85
|
+
hash.set(getHash());
|
|
86
|
+
};
|
|
87
|
+
if (typeof window !== "undefined") {
|
|
88
|
+
const eventName = mode === "hash" ? "hashchange" : "popstate";
|
|
89
|
+
window.addEventListener(eventName, syncLocation);
|
|
90
|
+
onCleanup2(() => window.removeEventListener(eventName, syncLocation));
|
|
91
|
+
}
|
|
92
|
+
const navigate = (to, options) => {
|
|
93
|
+
if (typeof window === "undefined" || !window.history) {
|
|
94
|
+
pathname.set(to);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const target = base ? `${base.replace(/\/$/, "")}/${to.replace(/^\//, "")}` : to;
|
|
98
|
+
if (mode === "hash") {
|
|
99
|
+
if (options?.replace) {
|
|
100
|
+
window.location.replace(`#${to}`);
|
|
101
|
+
} else {
|
|
102
|
+
window.location.hash = `#${to}`;
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
if (options?.replace) {
|
|
106
|
+
window.history.replaceState(null, "", target);
|
|
107
|
+
} else {
|
|
108
|
+
window.history.pushState(null, "", target);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
syncLocation();
|
|
112
|
+
};
|
|
113
|
+
const matchedRoute = computed2(() => {
|
|
114
|
+
const currentPath = pathname();
|
|
115
|
+
const routeList = registeredRoutes();
|
|
116
|
+
return matchRoute(routeList, currentPath, base);
|
|
117
|
+
});
|
|
118
|
+
const params = computed2(() => matchedRoute()?.params ?? {});
|
|
119
|
+
const routerValue = {
|
|
120
|
+
location: { pathname, search, hash },
|
|
121
|
+
navigate,
|
|
122
|
+
params,
|
|
123
|
+
base,
|
|
124
|
+
matchedRoute,
|
|
125
|
+
routes,
|
|
126
|
+
registerRoute
|
|
127
|
+
};
|
|
128
|
+
return RouterContext.Provider({
|
|
129
|
+
value: routerValue,
|
|
130
|
+
children: props.children
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function Route2(props) {
|
|
134
|
+
const router = useContext2(RouterContext);
|
|
135
|
+
if (!router) {
|
|
136
|
+
throw new Error("<Route> must be rendered inside a <Router>");
|
|
137
|
+
}
|
|
138
|
+
const { matcher, paramNames } = compilePath(props.path);
|
|
139
|
+
router.registerRoute({
|
|
140
|
+
path: props.path,
|
|
141
|
+
component: props.component,
|
|
142
|
+
children: props.children,
|
|
143
|
+
matcher,
|
|
144
|
+
paramNames
|
|
145
|
+
});
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
function Routes2(props) {
|
|
149
|
+
const router = useContext2(RouterContext);
|
|
150
|
+
if (!router) {
|
|
151
|
+
throw new Error("<Routes> must be rendered inside a <Router>");
|
|
152
|
+
}
|
|
153
|
+
const _ = typeof props.children === "function" ? props.children() : props.children;
|
|
154
|
+
return Show2({
|
|
155
|
+
when: () => router.matchedRoute() !== null,
|
|
156
|
+
fallback: props.fallback ?? (() => document.createTextNode("404 Not Found")),
|
|
157
|
+
children: () => {
|
|
158
|
+
const match = router.matchedRoute();
|
|
159
|
+
if (!match)
|
|
160
|
+
return null;
|
|
161
|
+
if (match.component) {
|
|
162
|
+
return match.component({ params: match.params });
|
|
163
|
+
}
|
|
164
|
+
return match.children ?? null;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function Link2(props) {
|
|
169
|
+
const router = useContext2(RouterContext);
|
|
170
|
+
const { href, class: className, activeClass = "active", replace, children, ...rest } = props;
|
|
171
|
+
const handleClick = (e) => {
|
|
172
|
+
if (e.ctrlKey || e.metaKey || e.altKey || e.shiftKey || e.button !== 0) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
e.preventDefault();
|
|
176
|
+
if (router) {
|
|
177
|
+
router.navigate(href, { replace });
|
|
178
|
+
} else if (typeof window !== "undefined") {
|
|
179
|
+
window.location.href = href;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
const isActive = computed2(() => {
|
|
183
|
+
if (!router)
|
|
184
|
+
return false;
|
|
185
|
+
const current = router.location.pathname();
|
|
186
|
+
return current === href || href !== "/" && current.startsWith(href);
|
|
187
|
+
});
|
|
188
|
+
const computedClass = computed2(() => {
|
|
189
|
+
const baseCls = typeof className === "function" ? className() : className ?? "";
|
|
190
|
+
if (activeClass && isActive()) {
|
|
191
|
+
return baseCls ? `${baseCls} ${activeClass}` : activeClass;
|
|
192
|
+
}
|
|
193
|
+
return baseCls;
|
|
194
|
+
});
|
|
195
|
+
return /* @__PURE__ */ jsx2("a", {
|
|
196
|
+
href,
|
|
197
|
+
class: computedClass,
|
|
198
|
+
onClick: handleClick,
|
|
199
|
+
...rest,
|
|
200
|
+
children
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
function useLocation2() {
|
|
204
|
+
const router = useContext2(RouterContext);
|
|
205
|
+
if (!router)
|
|
206
|
+
throw new Error("useLocation must be used within a <Router>");
|
|
207
|
+
return router.location;
|
|
208
|
+
}
|
|
209
|
+
function useNavigate2() {
|
|
210
|
+
const router = useContext2(RouterContext);
|
|
211
|
+
if (!router)
|
|
212
|
+
throw new Error("useNavigate must be used within a <Router>");
|
|
213
|
+
return router.navigate;
|
|
214
|
+
}
|
|
215
|
+
function useParams2() {
|
|
216
|
+
const router = useContext2(RouterContext);
|
|
217
|
+
if (!router)
|
|
218
|
+
throw new Error("useParams must be used within a <Router>");
|
|
219
|
+
return () => router.params();
|
|
220
|
+
}
|
|
221
|
+
function useSearchParams2() {
|
|
222
|
+
const router = useContext2(RouterContext);
|
|
223
|
+
if (!router)
|
|
224
|
+
throw new Error("useSearchParams must be used within a <Router>");
|
|
225
|
+
const params = computed2(() => new URLSearchParams(router.location.search()));
|
|
226
|
+
const setSearchParams = (next) => {
|
|
227
|
+
const sp = next instanceof URLSearchParams ? next : new URLSearchParams(next);
|
|
228
|
+
const queryString = sp.toString();
|
|
229
|
+
const newUrl = `${router.location.pathname()}${queryString ? `?${queryString}` : ""}${router.location.hash()}`;
|
|
230
|
+
router.navigate(newUrl);
|
|
231
|
+
};
|
|
232
|
+
return [params, setSearchParams];
|
|
233
|
+
}
|
|
234
|
+
export { Router2, Route2, Routes2, Link2, useLocation2, useNavigate2, useParams2, useSearchParams2 };
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batch2,
|
|
3
|
+
computed2,
|
|
4
|
+
signal2
|
|
5
|
+
} from "./index-6kfe1nr4.js";
|
|
6
|
+
import {
|
|
7
|
+
validateWithSchema2
|
|
8
|
+
} from "./index-2zz0g2n2.js";
|
|
9
|
+
|
|
10
|
+
// src/form/form.ts
|
|
11
|
+
function createForm2(options) {
|
|
12
|
+
const initialValues = { ...options.initialValues };
|
|
13
|
+
const validateMode = options.validateOn ?? "change";
|
|
14
|
+
const fields = {};
|
|
15
|
+
const isSubmitting = signal2(false);
|
|
16
|
+
const submitCount = signal2(0);
|
|
17
|
+
const customErrors = signal2({});
|
|
18
|
+
for (const key in initialValues) {
|
|
19
|
+
const initVal = initialValues[key];
|
|
20
|
+
const valSignal = signal2(initVal);
|
|
21
|
+
const touchedSignal = signal2(false);
|
|
22
|
+
const isDirty = computed2(() => !Object.is(valSignal(), initialValues[key]));
|
|
23
|
+
fields[key] = {
|
|
24
|
+
value: valSignal,
|
|
25
|
+
touched: touchedSignal,
|
|
26
|
+
isDirty,
|
|
27
|
+
error: computed2(() => {
|
|
28
|
+
const custom = customErrors()[key];
|
|
29
|
+
if (custom)
|
|
30
|
+
return custom;
|
|
31
|
+
if (validateMode === "blur" && !touchedSignal() && submitCount() === 0) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (!options.schema)
|
|
35
|
+
return null;
|
|
36
|
+
const currentValues = getValues();
|
|
37
|
+
const res = validateWithSchema2(options.schema, currentValues);
|
|
38
|
+
if (!res.success && res.issues) {
|
|
39
|
+
const match = res.issues.find((i) => i.path === key || i.path === `${key}`);
|
|
40
|
+
return match ? match.message : null;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
})
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const getValues = () => {
|
|
47
|
+
const res = {};
|
|
48
|
+
for (const key in fields) {
|
|
49
|
+
res[key] = fields[key].value();
|
|
50
|
+
}
|
|
51
|
+
return res;
|
|
52
|
+
};
|
|
53
|
+
const errors = computed2(() => {
|
|
54
|
+
const errMap = {};
|
|
55
|
+
for (const key in fields) {
|
|
56
|
+
const err = fields[key].error();
|
|
57
|
+
if (err) {
|
|
58
|
+
errMap[key] = err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return errMap;
|
|
62
|
+
});
|
|
63
|
+
const isValid = computed2(() => {
|
|
64
|
+
const errs = errors();
|
|
65
|
+
return Object.keys(errs).length === 0;
|
|
66
|
+
});
|
|
67
|
+
const isDirty = computed2(() => {
|
|
68
|
+
for (const key in fields) {
|
|
69
|
+
if (fields[key].isDirty())
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
});
|
|
74
|
+
const validate = () => {
|
|
75
|
+
if (!options.schema)
|
|
76
|
+
return true;
|
|
77
|
+
const res = validateWithSchema2(options.schema, getValues());
|
|
78
|
+
return res.success;
|
|
79
|
+
};
|
|
80
|
+
const handleSubmit = async (e) => {
|
|
81
|
+
if (e?.preventDefault) {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
}
|
|
84
|
+
submitCount.update((c) => c + 1);
|
|
85
|
+
batch2(() => {
|
|
86
|
+
for (const key in fields) {
|
|
87
|
+
fields[key].touched.set(true);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const valid = validate();
|
|
91
|
+
if (!valid) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if (options.onSubmit) {
|
|
95
|
+
isSubmitting.set(true);
|
|
96
|
+
try {
|
|
97
|
+
await options.onSubmit(getValues());
|
|
98
|
+
} finally {
|
|
99
|
+
isSubmitting.set(false);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return true;
|
|
103
|
+
};
|
|
104
|
+
const bind = (name) => {
|
|
105
|
+
const field = fields[name];
|
|
106
|
+
if (!field) {
|
|
107
|
+
throw new Error(`Field '${String(name)}' not found in form`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
name: String(name),
|
|
111
|
+
value: field.value,
|
|
112
|
+
onInput: (e) => {
|
|
113
|
+
const val = e?.target?.type === "checkbox" ? e.target.checked : e?.target?.value !== undefined ? e.target.value : e;
|
|
114
|
+
field.value.set(val);
|
|
115
|
+
},
|
|
116
|
+
onBlur: () => {
|
|
117
|
+
field.touched.set(true);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
const reset = (newValues) => {
|
|
122
|
+
const resetTarget = { ...initialValues, ...newValues };
|
|
123
|
+
batch2(() => {
|
|
124
|
+
for (const key in fields) {
|
|
125
|
+
fields[key].value.set(resetTarget[key]);
|
|
126
|
+
fields[key].touched.set(false);
|
|
127
|
+
}
|
|
128
|
+
customErrors.set({});
|
|
129
|
+
submitCount.set(0);
|
|
130
|
+
isSubmitting.set(false);
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
const setFieldValue = (name, value) => {
|
|
134
|
+
fields[name]?.value.set(value);
|
|
135
|
+
};
|
|
136
|
+
const setFieldError = (name, error) => {
|
|
137
|
+
customErrors.update((prev) => ({
|
|
138
|
+
...prev,
|
|
139
|
+
[name]: error ?? undefined
|
|
140
|
+
}));
|
|
141
|
+
};
|
|
142
|
+
return {
|
|
143
|
+
fields,
|
|
144
|
+
values: getValues,
|
|
145
|
+
errors,
|
|
146
|
+
isValid,
|
|
147
|
+
isDirty,
|
|
148
|
+
isSubmitting,
|
|
149
|
+
submitCount,
|
|
150
|
+
bind,
|
|
151
|
+
handleSubmit,
|
|
152
|
+
reset,
|
|
153
|
+
setFieldValue,
|
|
154
|
+
setFieldError,
|
|
155
|
+
validate
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export { createForm2 };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setAttribute2,
|
|
3
|
+
insertChild2
|
|
4
|
+
} from "./index-6kfe1nr4.js";
|
|
5
|
+
|
|
6
|
+
// src/template/template.ts
|
|
7
|
+
var templateCache = new Map;
|
|
8
|
+
function template2(htmlString) {
|
|
9
|
+
let templateEl = templateCache.get(htmlString);
|
|
10
|
+
if (!templateEl && typeof document !== "undefined") {
|
|
11
|
+
templateEl = document.createElement("template");
|
|
12
|
+
templateEl.innerHTML = htmlString.trim();
|
|
13
|
+
templateCache.set(htmlString, templateEl);
|
|
14
|
+
}
|
|
15
|
+
return () => {
|
|
16
|
+
if (!templateEl) {
|
|
17
|
+
const wrapper = document.createElement("div");
|
|
18
|
+
wrapper.innerHTML = htmlString.trim();
|
|
19
|
+
return wrapper.firstElementChild;
|
|
20
|
+
}
|
|
21
|
+
return templateEl.content.firstElementChild.cloneNode(true);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function html2(strings, ...values) {
|
|
25
|
+
const MARKER_PREFIX = "visage-bind-";
|
|
26
|
+
let htmlString = "";
|
|
27
|
+
const bindings = [];
|
|
28
|
+
for (let i = 0;i < strings.length; i++) {
|
|
29
|
+
htmlString += strings[i];
|
|
30
|
+
if (i < values.length) {
|
|
31
|
+
const val = values[i];
|
|
32
|
+
const markerId = `${MARKER_PREFIX}${i}`;
|
|
33
|
+
const lastOpeningTag = htmlString.lastIndexOf("<");
|
|
34
|
+
const lastClosingTag = htmlString.lastIndexOf(">");
|
|
35
|
+
if (lastOpeningTag > lastClosingTag) {
|
|
36
|
+
const match = htmlString.match(/([a-zA-Z0-9_-]+)=["']?$/);
|
|
37
|
+
if (match?.[1]) {
|
|
38
|
+
const attrName = match[1];
|
|
39
|
+
bindings.push({ id: markerId, value: val, isAttr: true, attrName });
|
|
40
|
+
htmlString += `${markerId}`;
|
|
41
|
+
} else {
|
|
42
|
+
bindings.push({ id: markerId, value: val, isAttr: true });
|
|
43
|
+
htmlString += `data-visage-prop-${i}="${markerId}"`;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
bindings.push({ id: markerId, value: val, isAttr: false });
|
|
47
|
+
htmlString += `<!--${markerId}-->`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const clone = template2(htmlString);
|
|
52
|
+
const root = clone();
|
|
53
|
+
for (const bind of bindings) {
|
|
54
|
+
if (bind.isAttr && bind.attrName) {
|
|
55
|
+
const targetEl = findElementWithAttributeValue(root, bind.attrName, bind.id);
|
|
56
|
+
if (targetEl) {
|
|
57
|
+
targetEl.removeAttribute(bind.attrName);
|
|
58
|
+
setAttribute2(targetEl, bind.attrName, bind.value);
|
|
59
|
+
}
|
|
60
|
+
} else if (!bind.isAttr) {
|
|
61
|
+
const commentNode = findCommentNode(root, bind.id);
|
|
62
|
+
if (commentNode?.parentNode) {
|
|
63
|
+
insertChild2(commentNode.parentNode, bind.value, commentNode);
|
|
64
|
+
commentNode.parentNode.removeChild(commentNode);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return root;
|
|
69
|
+
}
|
|
70
|
+
function findElementWithAttributeValue(root, attrName, value) {
|
|
71
|
+
if (root.getAttribute(attrName) === value) {
|
|
72
|
+
return root;
|
|
73
|
+
}
|
|
74
|
+
return root.querySelector(`[${attrName}="${value}"]`);
|
|
75
|
+
}
|
|
76
|
+
function findCommentNode(root, text) {
|
|
77
|
+
const iterator = typeof document !== "undefined" && document.createNodeIterator ? document.createNodeIterator(root, 128) : null;
|
|
78
|
+
if (iterator) {
|
|
79
|
+
let node = iterator.nextNode();
|
|
80
|
+
while (node) {
|
|
81
|
+
if (node.nodeValue === text) {
|
|
82
|
+
return node;
|
|
83
|
+
}
|
|
84
|
+
node = iterator.nextNode();
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const traverse = (node) => {
|
|
89
|
+
for (const child of node.childNodes || []) {
|
|
90
|
+
if (child.nodeType === 8 && child.data === text) {
|
|
91
|
+
return child;
|
|
92
|
+
}
|
|
93
|
+
const res = traverse(child);
|
|
94
|
+
if (res)
|
|
95
|
+
return res;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
};
|
|
99
|
+
return traverse(root);
|
|
100
|
+
}
|
|
101
|
+
export { template2, html2 };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import {
|
|
2
|
+
insertChild2,
|
|
3
|
+
onCleanup2,
|
|
4
|
+
createRoot2
|
|
5
|
+
} from "./index-6kfe1nr4.js";
|
|
6
|
+
|
|
7
|
+
// src/server/hydrate.ts
|
|
8
|
+
function hydrate2(fn, container) {
|
|
9
|
+
return createRoot2((dispose) => {
|
|
10
|
+
container.textContent = "";
|
|
11
|
+
const node = insertChild2(container, fn());
|
|
12
|
+
onCleanup2(() => {
|
|
13
|
+
if (node && node instanceof Node && node.parentNode === container) {
|
|
14
|
+
container.removeChild(node);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
return dispose;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
// src/server/render-to-string.ts
|
|
21
|
+
var VOID_ELEMENTS = new Set([
|
|
22
|
+
"area",
|
|
23
|
+
"base",
|
|
24
|
+
"br",
|
|
25
|
+
"col",
|
|
26
|
+
"embed",
|
|
27
|
+
"hr",
|
|
28
|
+
"img",
|
|
29
|
+
"input",
|
|
30
|
+
"link",
|
|
31
|
+
"meta",
|
|
32
|
+
"param",
|
|
33
|
+
"source",
|
|
34
|
+
"track",
|
|
35
|
+
"wbr"
|
|
36
|
+
]);
|
|
37
|
+
function escapeHtml(str) {
|
|
38
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
39
|
+
}
|
|
40
|
+
function renderChildToString2(child) {
|
|
41
|
+
if (child === null || child === undefined || child === false || child === true) {
|
|
42
|
+
return "";
|
|
43
|
+
}
|
|
44
|
+
if (typeof child === "function") {
|
|
45
|
+
return renderChildToString2(child());
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(child)) {
|
|
48
|
+
return child.map((c) => renderChildToString2(c)).join("");
|
|
49
|
+
}
|
|
50
|
+
if (typeof child === "object" && "nodeType" in child) {
|
|
51
|
+
if (child.nodeType === 11 && child.childNodes) {
|
|
52
|
+
return child.childNodes.map((c) => renderChildToString2(c)).join("");
|
|
53
|
+
}
|
|
54
|
+
return child.outerHTML || child.textContent || "";
|
|
55
|
+
}
|
|
56
|
+
return escapeHtml(String(child));
|
|
57
|
+
}
|
|
58
|
+
function renderToString2(fn) {
|
|
59
|
+
return createRoot2(() => {
|
|
60
|
+
const result = fn();
|
|
61
|
+
return renderNodeToString(result);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function renderNodeToString(node) {
|
|
65
|
+
if (node === null || node === undefined || node === false || node === true) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
if (typeof node === "function") {
|
|
69
|
+
return renderNodeToString(node());
|
|
70
|
+
}
|
|
71
|
+
if (Array.isArray(node)) {
|
|
72
|
+
return node.map((n) => renderNodeToString(n)).join("");
|
|
73
|
+
}
|
|
74
|
+
if (typeof node === "object" && node !== null) {
|
|
75
|
+
if (node.nodeType === 11) {
|
|
76
|
+
return (node.childNodes || []).map((c) => renderNodeToString(c)).join("");
|
|
77
|
+
}
|
|
78
|
+
if ("tagName" in node) {
|
|
79
|
+
const tag = String(node.tagName).toLowerCase();
|
|
80
|
+
const isVoid = VOID_ELEMENTS.has(tag);
|
|
81
|
+
let attrStr = "";
|
|
82
|
+
if (node.attributes instanceof Map) {
|
|
83
|
+
for (const [k, v] of node.attributes) {
|
|
84
|
+
attrStr += ` ${k}="${escapeHtml(String(v))}"`;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (node.style && typeof node.style === "object" && !(node.attributes instanceof Map && node.attributes.has("style"))) {
|
|
88
|
+
const styleEntries = Object.entries(node.style).filter(([_, v]) => typeof v === "string" && v.length > 0);
|
|
89
|
+
if (styleEntries.length > 0) {
|
|
90
|
+
const styleStr = styleEntries.map(([k, v]) => `${k.startsWith("--") ? k : k.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${v}`).join("; ");
|
|
91
|
+
attrStr += ` style="${escapeHtml(styleStr)}"`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (isVoid) {
|
|
95
|
+
return `<${tag}${attrStr} />`;
|
|
96
|
+
}
|
|
97
|
+
const inner = (node.childNodes || []).map((c) => renderNodeToString(c)).join("");
|
|
98
|
+
return `<${tag}${attrStr}>${inner}</${tag}>`;
|
|
99
|
+
}
|
|
100
|
+
if ("textContent" in node) {
|
|
101
|
+
return escapeHtml(node.textContent || "");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return escapeHtml(String(node));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/server/render-to-stream.ts
|
|
108
|
+
function renderToStream2(fn, options) {
|
|
109
|
+
const encoder = new TextEncoder;
|
|
110
|
+
return new ReadableStream({
|
|
111
|
+
start(controller) {
|
|
112
|
+
try {
|
|
113
|
+
const initialHtml = renderToString2(fn);
|
|
114
|
+
options?.onChunk?.(initialHtml);
|
|
115
|
+
controller.enqueue(encoder.encode(initialHtml));
|
|
116
|
+
controller.close();
|
|
117
|
+
} catch (err) {
|
|
118
|
+
options?.onError?.(err);
|
|
119
|
+
controller.error(err);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
export { hydrate2, renderChildToString2, renderToString2, renderToStream2 };
|