pudui 0.0.0 → 0.0.2

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.
@@ -20,6 +20,7 @@ function fail(message) {
20
20
  //#endregion
21
21
  //#region src/core/component.ts
22
22
  const componentInternals = /* @__PURE__ */ new WeakMap();
23
+ const componentInternalsKey = Symbol();
23
24
  let updateFrameScheduled = false;
24
25
  let scheduledUpdates = /* @__PURE__ */ new Map();
25
26
  /**
@@ -52,9 +53,10 @@ var Component = class {
52
53
  * @param options Render, error, and hydration options for this component.
53
54
  */
54
55
  constructor(options) {
55
- componentInternals.set(this, {
56
+ const internals = {
56
57
  i: 0,
57
58
  c: /* @__PURE__ */ new Map(),
59
+ d: false,
58
60
  e: options.error,
59
61
  h: options.hydrate,
60
62
  f: /* @__PURE__ */ new Map(),
@@ -62,8 +64,10 @@ var Component = class {
62
64
  o: [],
63
65
  p: void 0,
64
66
  r: options.render,
65
- u: []
66
- });
67
+ u: /* @__PURE__ */ new Set()
68
+ };
69
+ componentInternals.set(this, internals);
70
+ Object.defineProperty(this, componentInternalsKey, { value: internals });
67
71
  }
68
72
  /**
69
73
  * Queues a callback to run after the component's first browser mount.
@@ -85,7 +89,9 @@ var Component = class {
85
89
  * @param callback Function to run after the update commits.
86
90
  */
87
91
  update(callback) {
88
- for (const listener of getComponentInternals(this).f.keys()) scheduleUpdate(listener, callback);
92
+ const internals = getComponentInternals(this);
93
+ internals.d = true;
94
+ for (const listener of internals.f.keys()) scheduleUpdate(listener, callback);
89
95
  }
90
96
  };
91
97
  /**
@@ -98,7 +104,7 @@ function isComponent(value) {
98
104
  return value instanceof Component;
99
105
  }
100
106
  function getComponentInternals(component) {
101
- const internals = componentInternals.get(component);
107
+ const internals = component[componentInternalsKey] ?? componentInternals.get(component);
102
108
  invariant(internals, "component");
103
109
  return internals;
104
110
  }
@@ -139,6 +145,14 @@ function renderComponentOutput(component) {
139
145
  return getComponentInternals(component).r(readComponentProps(component));
140
146
  }
141
147
  /**
148
+ * Marks the current props as rendered.
149
+ *
150
+ * @param component Component that completed a render pass.
151
+ */
152
+ function markComponentRendered(component) {
153
+ getComponentInternals(component).d = false;
154
+ }
155
+ /**
142
156
  * Runs a component's error callback for a failed render.
143
157
  *
144
158
  * @param component Component whose render failed.
@@ -158,7 +172,7 @@ function renderComponentErrorOutput(component, reason) {
158
172
  function beginComponentRender(component) {
159
173
  const internals = getComponentInternals(component);
160
174
  internals.i = 0;
161
- internals.u = [];
175
+ internals.u.clear();
162
176
  }
163
177
  /**
164
178
  * Marks the end of a component render pass and prunes unused child instances.
@@ -167,7 +181,7 @@ function beginComponentRender(component) {
167
181
  */
168
182
  function finishComponentRender(component) {
169
183
  const internals = getComponentInternals(component);
170
- for (const childSlot of internals.c.keys()) if (!internals.u.includes(childSlot)) internals.c.delete(childSlot);
184
+ for (const childSlot of internals.c.keys()) if (!internals.u.has(childSlot)) internals.c.delete(childSlot);
171
185
  }
172
186
  /**
173
187
  * Resolves a component virtual node to a component instance.
@@ -179,9 +193,9 @@ function finishComponentRender(component) {
179
193
  function resolveComponent(vnode, owner) {
180
194
  if (!owner) return createComponent(vnode.type, vnode.props);
181
195
  const internals = getComponentInternals(owner);
182
- const slot = vnode.key === void 0 ? "." + internals.i++ : "#" + String(vnode.key);
196
+ const slot = componentSlot(vnode, internals);
183
197
  const existing = internals.c.get(slot);
184
- internals.u.push(slot);
198
+ internals.u.add(slot);
185
199
  if (existing?.t === vnode.type) {
186
200
  setComponentProps(existing.v, vnode.props);
187
201
  return existing.v;
@@ -194,6 +208,20 @@ function resolveComponent(vnode, owner) {
194
208
  return component;
195
209
  }
196
210
  /**
211
+ * Marks a component virtual node slot as used without resolving the instance.
212
+ *
213
+ * @param vnode Component virtual node.
214
+ * @param owner Parent component that owns child instance slots.
215
+ */
216
+ function retainComponentSlot(vnode, owner) {
217
+ if (!owner) return;
218
+ const internals = getComponentInternals(owner);
219
+ internals.u.add(componentSlot(vnode, internals));
220
+ }
221
+ function componentSlot(vnode, internals) {
222
+ return vnode.key === void 0 ? "." + internals.i++ : typeof vnode.key === "number" ? vnode.key : "#" + vnode.key;
223
+ }
224
+ /**
197
225
  * Creates a component instance from a component factory.
198
226
  *
199
227
  * @param factory Component factory to invoke.
@@ -272,4 +300,4 @@ function isPromiseLike(value) {
272
300
  return typeof value?.then === "function";
273
301
  }
274
302
  //#endregion
275
- export { isComponent as a, readComponentProps as c, resolveComponent as d, setComponentProps as f, invariant as h, finishComponentRender as i, renderComponentErrorOutput as l, fail as m, beginComponentRender as n, queueComponentMount as o, subscribeComponent as p, createComponent as r, readComponentHydrateMeta as s, Component as t, renderComponentOutput as u };
303
+ export { invariant as _, isComponent as a, readComponentHydrateMeta as c, renderComponentOutput as d, resolveComponent as f, fail as g, subscribeComponent as h, finishComponentRender as i, readComponentProps as l, setComponentProps as m, beginComponentRender as n, markComponentRendered as o, retainComponentSlot as p, createComponent as r, queueComponentMount as s, Component as t, renderComponentErrorOutput as u };
@@ -1,4 +1,4 @@
1
- import { t as Component } from "./component-D3kwgmef.mjs";
1
+ import { t as Component } from "./component-2mAsZGIS.mjs";
2
2
  //#region src/hmr-runtime.ts
3
3
  const globalRegistryKey = "__puduiHmrRegistry";
4
4
  const moduleDataKey = "__puduiHmr";
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { c as jsxDEV, d as normalizeChildren, f as readRawChildHtml, l as isEmptyChild, n as Raw, o as isVNode, r as createElement, s as jsx, t as Fragment, u as isRawChild } from "./vnode-Lr-Tpypk.mjs";
1
+ import { c as jsxDEV, d as normalizeChildren, f as readRawChildHtml, l as isEmptyChild, n as Raw, o as isVNode, r as createElement, s as jsx, t as Fragment, u as isRawChild } from "./vnode-B7qKjJZs.mjs";
2
2
  import { a as isRawStartCommentData, g as readDangerouslySetInnerHTML, h as isPrimitiveValue, i as isRawEndCommentData, m as isChildlessElement, n as isHydrateMetaCommentData, o as parseHydrateMetaCommentData, p as attributeName, r as isHydrateStartCommentData, s as parseHydrateStartCommentData, t as isHydrateEndCommentData, v as toKebabCase } from "./hydration-markers-DdjOvH6g.mjs";
3
- import { a as isComponent, d as resolveComponent, f as setComponentProps, h as invariant, i as finishComponentRender, l as renderComponentErrorOutput, m as fail, n as beginComponentRender, o as queueComponentMount, p as subscribeComponent, r as createComponent, s as readComponentHydrateMeta, t as Component, u as renderComponentOutput } from "./component-D3kwgmef.mjs";
3
+ import { _ as invariant, a as isComponent, c as readComponentHydrateMeta, d as renderComponentOutput, f as resolveComponent, g as fail, h as subscribeComponent, i as finishComponentRender, m as setComponentProps, n as beginComponentRender, o as markComponentRendered, p as retainComponentSlot, r as createComponent, s as queueComponentMount, t as Component, u as renderComponentErrorOutput } from "./component-2mAsZGIS.mjs";
4
4
  //#region src/core/hydration.ts
5
5
  /**
6
6
  * Checks whether a value is a hydrated child boundary wrapper.
@@ -39,19 +39,24 @@ function updateHydratedChildrenBoundary(children, boundary) {
39
39
  * @param newProps Next intrinsic props.
40
40
  */
41
41
  function patchProps(element, oldProps, newProps) {
42
- for (const [rawName, value] of Object.entries(newProps)) {
42
+ if (oldProps === newProps) return;
43
+ for (const rawName in newProps) {
43
44
  if (shouldSkipProp(rawName)) continue;
44
- const name = attributeName(rawName);
45
+ const value = newProps[rawName];
45
46
  if (rawName === "style") {
46
47
  patchStyle(element, oldProps.style, value);
47
48
  continue;
48
49
  }
50
+ if (rawName === "className" || rawName === "class") {
51
+ patchAttribute(element, "class", readPropValue(oldProps, rawName), value);
52
+ continue;
53
+ }
54
+ const name = attributeName(rawName);
49
55
  const oldAttributeValue = attributeValue(readPropValue(oldProps, rawName));
50
56
  const newAttributeValue = attributeValue(value);
51
- if (oldAttributeValue !== newAttributeValue) if (newAttributeValue === void 0) element.removeAttribute(name);
52
- else element.setAttribute(name, newAttributeValue);
57
+ if (oldAttributeValue !== newAttributeValue) setAttribute(element, name, newAttributeValue);
53
58
  }
54
- for (const rawName of Object.keys(oldProps)) {
59
+ for (const rawName in oldProps) {
55
60
  if (shouldSkipProp(rawName) || readPropValue(newProps, rawName) !== void 0) continue;
56
61
  if (rawName === "style") {
57
62
  if (oldProps.style != null) element.removeAttribute("style");
@@ -61,8 +66,23 @@ function patchProps(element, oldProps, newProps) {
61
66
  if ("checked" in newProps && (element.type === "checkbox" || element.type === "radio")) element.checked = newProps.checked === true;
62
67
  }
63
68
  }
69
+ function patchAttribute(element, name, oldValue, newValue) {
70
+ const oldAttributeValue = attributeValue(oldValue);
71
+ const newAttributeValue = attributeValue(newValue);
72
+ if (oldAttributeValue !== newAttributeValue) setAttribute(element, name, newAttributeValue);
73
+ }
74
+ function setAttribute(element, name, value) {
75
+ if (value === void 0) element.removeAttribute(name);
76
+ else element.setAttribute(name, value);
77
+ }
64
78
  function shouldSkipProp(rawName) {
65
- return " children dangerouslySetInnerHTML key ref ".includes(" " + rawName + " ") || rawName.startsWith("on");
79
+ switch (rawName) {
80
+ case "children":
81
+ case "dangerouslySetInnerHTML":
82
+ case "key":
83
+ case "ref": return true;
84
+ default: return rawName.charCodeAt(0) === 111 && rawName.charCodeAt(1) === 110;
85
+ }
66
86
  }
67
87
  function readPropValue(props, rawName) {
68
88
  const value = props[rawName];
@@ -418,7 +438,7 @@ function hydrateBoundary(initialBoundary, factory, options) {
418
438
  hydrateOptions: options,
419
439
  owner: void 0,
420
440
  scheduleUpdate: commit
421
- }, boundary.s.nextSibling, component, parent, boundary.s, boundary.e).r;
441
+ }, boundary.s.nextSibling, component, parent, boundary.s, boundary.e, boundary.k).r;
422
442
  runEffects(effects.r);
423
443
  runEffects(effects.m);
424
444
  };
@@ -499,12 +519,15 @@ function childToRenderNode(child, context) {
499
519
  if (Array.isArray(child)) return arrayToRenderNode(child, context);
500
520
  if (isComponent(child)) return componentToRenderNode(child, context);
501
521
  if (isVNode(child)) {
502
- if (typeof child.type === "function") return childToRenderNode(resolveComponent(child, context.owner), context);
522
+ if (typeof child.type === "function") {
523
+ const vnode = child;
524
+ return componentToRenderNode(resolveComponent(vnode, context.owner), context, vnode.key, vnode.type, vnode);
525
+ }
503
526
  return elementToRenderNode(child, context);
504
527
  }
505
528
  return textToRenderNode(null, context.document);
506
529
  }
507
- function hydrateChildToDom(child, context, currentNode, rootComponent, parent) {
530
+ function hydrateChildToDom(child, context, currentNode, rootComponent, parent, key) {
508
531
  if (isHydratedChildren(child)) return hydrateHydratedChildren(child, currentNode, parent);
509
532
  if (isEmptyChild(child) || isPrimitiveValue(child)) return hydrateTextToDom(child, currentNode, parent);
510
533
  if (isRawChild(child)) return hydrateRawToDom(child, context, currentNode, parent);
@@ -530,10 +553,13 @@ function hydrateChildToDom(child, context, currentNode, rootComponent, parent) {
530
553
  }
531
554
  if (isComponent(child)) {
532
555
  if (child !== rootComponent && readComponentHydrateMeta(child) !== void 0) return hydrateSkippedBoundary(currentNode, parent);
533
- return hydrateComponentToDom(child, context, currentNode, rootComponent, parent);
556
+ return hydrateComponentToDom(child, context, currentNode, rootComponent, parent, void 0, void 0, key);
534
557
  }
535
558
  if (isVNode(child)) {
536
- if (typeof child.type === "function") return hydrateChildToDom(resolveComponent(child, context.owner), context, currentNode, rootComponent, parent);
559
+ if (typeof child.type === "function") {
560
+ const vnode = child;
561
+ return hydrateChildToDom(resolveComponent(vnode, context.owner), context, currentNode, rootComponent, parent, vnode.key);
562
+ }
537
563
  return hydrateElementToDom(child, context, currentNode, rootComponent, parent);
538
564
  }
539
565
  return hydrateTextToDom(null, currentNode, parent);
@@ -563,7 +589,7 @@ function hydrateElementToDom(vnode, context, currentNode, rootComponent, parent)
563
589
  r: node
564
590
  };
565
591
  }
566
- function hydrateComponentToDom(component, context, currentNode, rootComponent, parent, start, end) {
592
+ function hydrateComponentToDom(component, context, currentNode, rootComponent, parent, start, end, key) {
567
593
  const componentStart = start ?? insertMarker(parent, currentNode);
568
594
  const unsubscribe = subscribeComponent(component, context.scheduleUpdate);
569
595
  let result;
@@ -587,7 +613,10 @@ function hydrateComponentToDom(component, context, currentNode, rootComponent, p
587
613
  c: result.r,
588
614
  e: componentEnd,
589
615
  k: componentRenderNodeKind,
616
+ o: void 0,
617
+ q: key,
590
618
  s: componentStart,
619
+ t: void 0,
591
620
  u: unsubscribe,
592
621
  v: component
593
622
  }
@@ -706,7 +735,16 @@ function patchRenderNode(node, nextChild, context) {
706
735
  return replace();
707
736
  }
708
737
  if (isVNode(nextChild)) {
709
- if (typeof nextChild.type === "function") return patchRenderNode(node, resolveComponent(nextChild, context.owner), context);
738
+ if (typeof nextChild.type === "function") {
739
+ const component = resolveComponent(nextChild, context.owner);
740
+ if (node.k === componentRenderNodeKind && node.v === component) {
741
+ node.q = nextChild.key;
742
+ node.t = nextChild.type;
743
+ node.o = nextChild;
744
+ return patchComponentRenderNode(node, component, context);
745
+ }
746
+ return replaceRenderNode(node, component, context, nextChild.key, nextChild.type);
747
+ }
710
748
  const vnode = nextChild;
711
749
  if (node.k === elementRenderNodeKind && node.v.type === vnode.type && node.v.key === vnode.key) return patchElementRenderNode(node, vnode, context);
712
750
  return replace();
@@ -715,7 +753,7 @@ function patchRenderNode(node, nextChild, context) {
715
753
  }
716
754
  function patchArrayRenderNode(node, nextChildren, context) {
717
755
  const normalizedChildren = normalizeChildren(nextChildren);
718
- if (shouldPatchKeyedArray(node.c, normalizedChildren)) return patchKeyedArrayRenderNode(node, normalizedChildren, context);
756
+ if (shouldPatchKeyedArray(node.c, normalizedChildren)) return patchKeyedArrayRenderNode(node, reuseKeyedArrayChildren(node, normalizedChildren), context);
719
757
  const sharedLength = Math.min(node.c.length, normalizedChildren.length);
720
758
  for (let index = 0; index < sharedLength; index++) node.c[index] = patchRenderNode(node.c[index], normalizedChildren[index], context);
721
759
  const parent = node.e.parentNode;
@@ -739,9 +777,45 @@ function patchArrayRenderNode(node, nextChildren, context) {
739
777
  const firstRemovedChild = removedChildren[0];
740
778
  const lastRemovedChild = removedChildren.at(-1);
741
779
  if (firstRemovedChild && lastRemovedChild) removeNodeRange(renderNodeStart(firstRemovedChild), renderNodeEnd(lastRemovedChild));
780
+ node.h = void 0;
742
781
  return node;
743
782
  }
783
+ function reuseKeyedArrayChildren(node, nextChildren) {
784
+ const previousChildren = node.h;
785
+ const reusableChildren = previousChildren ? Array.from(nextChildren) : void 0;
786
+ const nextReusableChildren = /* @__PURE__ */ new Map();
787
+ for (let index = 0; index < nextChildren.length; index++) {
788
+ const child = nextChildren[index];
789
+ const key = readChildKey(child);
790
+ if (key === void 0) {
791
+ node.h = void 0;
792
+ return nextChildren;
793
+ }
794
+ const previousChild = previousChildren?.get(key);
795
+ const reusableChild = previousChild && canReuseKeyedChild(previousChild, child) ? previousChild : child;
796
+ if (reusableChildren) reusableChildren[index] = reusableChild;
797
+ nextReusableChildren.set(key, reusableChild);
798
+ }
799
+ node.h = nextReusableChildren;
800
+ return reusableChildren ?? nextChildren;
801
+ }
802
+ function canReuseKeyedChild(previousChild, nextChild) {
803
+ if (!isVNode(previousChild) || !isVNode(nextChild) || typeof previousChild.type !== "function" || previousChild.type !== nextChild.type || previousChild.key !== nextChild.key) return false;
804
+ return shallowEqualProps(previousChild.props, nextChild.props);
805
+ }
806
+ function shallowEqualProps(previousProps, nextProps) {
807
+ if (previousProps === nextProps) return true;
808
+ if (previousProps === void 0) return false;
809
+ const previousKeys = Object.keys(previousProps);
810
+ const nextKeys = Object.keys(nextProps);
811
+ if (previousKeys.length !== nextKeys.length) return false;
812
+ const previousRecord = previousProps;
813
+ const nextRecord = nextProps;
814
+ for (const key of previousKeys) if (previousRecord[key] !== nextRecord[key] || !Object.hasOwn(nextProps, key)) return false;
815
+ return true;
816
+ }
744
817
  function patchKeyedArrayRenderNode(node, normalizedChildren, context) {
818
+ if (patchSameLengthKeyedArrayRenderNode(node, normalizedChildren, context)) return node;
745
819
  const parent = node.e.parentNode;
746
820
  const oldEntries = /* @__PURE__ */ new Map();
747
821
  for (let index = 0; index < node.c.length; index++) oldEntries.set(readRenderNodeKey(node.c[index]), {
@@ -780,10 +854,62 @@ function patchKeyedArrayRenderNode(node, normalizedChildren, context) {
780
854
  node.c = nextNodes;
781
855
  return node;
782
856
  }
857
+ function patchSameLengthKeyedArrayRenderNode(node, normalizedChildren, context) {
858
+ const length = node.c.length;
859
+ if (length !== normalizedChildren.length) return false;
860
+ const changedIndexes = [];
861
+ const changedOldNodes = /* @__PURE__ */ new Map();
862
+ const nextNodes = node.c.slice();
863
+ for (let index = 0; index < length; index++) {
864
+ const oldNode = node.c[index];
865
+ const nextChild = normalizedChildren[index];
866
+ const oldKey = readRenderNodeKey(oldNode);
867
+ const nextKey = readChildKey(nextChild);
868
+ if (oldKey === void 0 || nextKey === void 0) return false;
869
+ if (oldKey === nextKey) nextNodes[index] = patchStableKeyedRenderNode(oldNode, nextChild, context);
870
+ else {
871
+ changedIndexes.push(index);
872
+ changedOldNodes.set(oldKey, oldNode);
873
+ }
874
+ }
875
+ if (changedIndexes.length === 0) {
876
+ node.c = nextNodes;
877
+ return true;
878
+ }
879
+ for (const index of changedIndexes) {
880
+ const nextChild = normalizedChildren[index];
881
+ const oldNode = changedOldNodes.get(readChildKey(nextChild));
882
+ if (oldNode === void 0) return false;
883
+ nextNodes[index] = patchStableKeyedRenderNode(oldNode, nextChild, context);
884
+ }
885
+ const parent = node.e.parentNode;
886
+ if (parent) for (let cursor = changedIndexes.length - 1; cursor >= 0; cursor--) {
887
+ const index = changedIndexes[cursor];
888
+ const child = nextNodes[index];
889
+ const anchor = index + 1 < nextNodes.length ? renderNodeStart(nextNodes[index + 1]) : node.e;
890
+ if (renderNodeEnd(child).nextSibling !== anchor) insertRenderNode(parent, child, anchor);
891
+ }
892
+ node.c = nextNodes;
893
+ return true;
894
+ }
895
+ function patchStableKeyedRenderNode(node, nextChild, context) {
896
+ if (canSkipStableKeyedRenderNodePatch(node, nextChild, context)) return node;
897
+ return patchRenderNode(node, nextChild, context);
898
+ }
899
+ function canSkipStableKeyedRenderNodePatch(node, nextChild, context) {
900
+ if (node.k === componentRenderNodeKind && node.o === nextChild) {
901
+ retainComponentSlot(nextChild, context.owner);
902
+ return true;
903
+ }
904
+ if (node.k !== componentRenderNodeKind || !isVNode(nextChild) || typeof nextChild.type !== "function" || node.t !== nextChild.type || node.q !== nextChild.key || !shallowEqualProps(node.o?.props, nextChild.props)) return false;
905
+ retainComponentSlot(nextChild, context.owner);
906
+ node.o = nextChild;
907
+ return true;
908
+ }
783
909
  function shouldPatchKeyedArray(oldNodes, nextChildren) {
784
910
  const firstOld = oldNodes[0];
785
911
  const firstNext = nextChildren[0];
786
- if (firstOld?.k !== elementRenderNodeKind || firstOld.v.key === void 0 || !isVNode(firstNext) || firstNext.key === void 0) return false;
912
+ if (firstOld === void 0 || readRenderNodeKey(firstOld) === void 0 || !isVNode(firstNext) || firstNext.key === void 0) return false;
787
913
  if (oldNodes.length <= nextChildren.length) {
788
914
  for (let index = 0; index < oldNodes.length; index++) if (readRenderNodeKey(oldNodes[index]) !== readChildKey(nextChildren[index])) return true;
789
915
  return false;
@@ -794,7 +920,8 @@ function readChildKey(child) {
794
920
  return isVNode(child) ? child.key : void 0;
795
921
  }
796
922
  function readRenderNodeKey(node) {
797
- return node.k === elementRenderNodeKind ? node.v.key : void 0;
923
+ if (node.k === elementRenderNodeKind) return node.v.key;
924
+ if (node.k === componentRenderNodeKind) return node.q;
798
925
  }
799
926
  function lis(values) {
800
927
  const p = Array.from({ length: values.length });
@@ -871,10 +998,10 @@ function patchElementRenderNode(node, vnode, context) {
871
998
  if (refChanged) collectRefEffects(node.n, vnode.props.ref, context.effects, node.r);
872
999
  return node;
873
1000
  }
874
- function replaceRenderNode(node, nextChild, context) {
1001
+ function replaceRenderNode(node, nextChild, context, key, type) {
875
1002
  const parent = renderNodeStart(node).parentNode;
876
1003
  const nextSibling = renderNodeEnd(node).nextSibling;
877
- const replacement = childToRenderNode(nextChild, context);
1004
+ const replacement = isComponent(nextChild) ? componentToRenderNode(nextChild, context, key, type) : childToRenderNode(nextChild, context);
878
1005
  cleanupRenderNode(node);
879
1006
  removeRenderNode(node);
880
1007
  if (parent) insertRenderNode(parent, replacement, nextSibling);
@@ -1175,14 +1302,22 @@ function textValue(value) {
1175
1302
  return isPrimitiveValue(value) ? String(value) : "";
1176
1303
  }
1177
1304
  function arrayToRenderNode(children, context) {
1178
- return {
1179
- c: normalizeChildren(children).map((child) => childToRenderNode(child, context)),
1305
+ const normalizedChildren = normalizeChildren(children);
1306
+ const node = {
1307
+ c: normalizedChildren.map((child) => childToRenderNode(child, context)),
1180
1308
  e: createMarker(context.document),
1181
1309
  k: arrayRenderNodeKind,
1182
1310
  s: createMarker(context.document)
1183
1311
  };
1312
+ if (isKeyedArrayChildren(normalizedChildren)) node.h = new Map(normalizedChildren.map((child) => [readChildKey(child), child]));
1313
+ return node;
1184
1314
  }
1185
- function componentToRenderNode(component, context) {
1315
+ function isKeyedArrayChildren(children) {
1316
+ if (children.length === 0) return false;
1317
+ for (const child of children) if (!isVNode(child) || child.key === void 0) return false;
1318
+ return true;
1319
+ }
1320
+ function componentToRenderNode(component, context, key, type, vnode) {
1186
1321
  const unsubscribe = subscribeComponent(component, context.scheduleUpdate);
1187
1322
  let child;
1188
1323
  try {
@@ -1200,9 +1335,10 @@ function componentToRenderNode(component, context) {
1200
1335
  }
1201
1336
  return {
1202
1337
  c: child,
1203
- e: createMarker(context.document),
1204
1338
  k: componentRenderNodeKind,
1205
- s: createMarker(context.document),
1339
+ o: vnode,
1340
+ q: key,
1341
+ t: type,
1206
1342
  u: unsubscribe,
1207
1343
  v: component
1208
1344
  };
@@ -1306,9 +1442,9 @@ function appendRenderNode(parent, node) {
1306
1442
  parent.appendChild(node.e);
1307
1443
  return;
1308
1444
  case componentRenderNodeKind:
1309
- parent.appendChild(node.s);
1445
+ if (node.s !== void 0) parent.appendChild(node.s);
1310
1446
  appendRenderNode(parent, node.c);
1311
- parent.appendChild(node.e);
1447
+ if (node.e !== void 0) parent.appendChild(node.e);
1312
1448
  return;
1313
1449
  case elementRenderNodeKind:
1314
1450
  parent.appendChild(node.n);
@@ -1393,10 +1529,14 @@ function cleanupRenderNode(node) {
1393
1529
  }
1394
1530
  }
1395
1531
  function renderNodeStart(node) {
1396
- return node.k === elementRenderNodeKind || node.k === textRenderNodeKind ? node.n : node.s;
1532
+ if (node.k === elementRenderNodeKind || node.k === textRenderNodeKind) return node.n;
1533
+ if (node.k === componentRenderNodeKind) return node.s ?? renderNodeStart(node.c);
1534
+ return node.s;
1397
1535
  }
1398
1536
  function renderNodeEnd(node) {
1399
- return node.k === elementRenderNodeKind || node.k === textRenderNodeKind ? node.n : node.e;
1537
+ if (node.k === elementRenderNodeKind || node.k === textRenderNodeKind) return node.n;
1538
+ if (node.k === componentRenderNodeKind) return node.e ?? renderNodeEnd(node.c);
1539
+ return node.e;
1400
1540
  }
1401
1541
  function ownerDocumentFor(node) {
1402
1542
  if (node instanceof Document) return node;
@@ -1411,6 +1551,7 @@ function renderComponentChild(component, effects, renderChild) {
1411
1551
  try {
1412
1552
  const result = renderChild(renderComponentOutput(component));
1413
1553
  queueComponentMount(component, effects.m);
1554
+ markComponentRendered(component);
1414
1555
  return result;
1415
1556
  } catch (reason) {
1416
1557
  effects.r.splice(refEffectStart);
@@ -1418,6 +1559,7 @@ function renderComponentChild(component, effects, renderChild) {
1418
1559
  beginComponentRender(component);
1419
1560
  const result = renderChild(renderComponentErrorOutput(component, reason));
1420
1561
  queueComponentMount(component, effects.m);
1562
+ markComponentRendered(component);
1421
1563
  return result;
1422
1564
  }
1423
1565
  } finally {
@@ -1,2 +1,2 @@
1
- import { c as jsxDEV, t as Fragment } from "./vnode-Lr-Tpypk.mjs";
1
+ import { c as jsxDEV, t as Fragment } from "./vnode-B7qKjJZs.mjs";
2
2
  export { Fragment, jsxDEV };
@@ -1,2 +1,2 @@
1
- import { s as jsx, t as Fragment } from "./vnode-Lr-Tpypk.mjs";
1
+ import { s as jsx, t as Fragment } from "./vnode-B7qKjJZs.mjs";
2
2
  export { Fragment, jsx, jsx as jsxs };
package/dist/server.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { a as isHostVNode, c as jsxDEV, d as normalizeChildren, f as readRawChildHtml, i as isComponentVNode, n as Raw, o as isVNode, r as createElement, s as jsx, t as Fragment, u as isRawChild } from "./vnode-Lr-Tpypk.mjs";
1
+ import { a as isHostVNode, c as jsxDEV, d as normalizeChildren, f as readRawChildHtml, i as isComponentVNode, n as Raw, o as isVNode, r as createElement, s as jsx, t as Fragment, u as isRawChild } from "./vnode-B7qKjJZs.mjs";
2
2
  import { _ as serializeAttributeValue, b as validateStyleName, c as serializeHydrateEndComment, d as serializeRawEndComment, f as serializeRawStartComment, g as readDangerouslySetInnerHTML, l as serializeHydrateMetaComment, m as isChildlessElement, p as attributeName, u as serializeHydrateStartComment, v as toKebabCase, x as validateStyleValue, y as validateMarkupName } from "./hydration-markers-DdjOvH6g.mjs";
3
- import { a as isComponent, c as readComponentProps, d as resolveComponent, f as setComponentProps, i as finishComponentRender, l as renderComponentErrorOutput, n as beginComponentRender, s as readComponentHydrateMeta, t as Component, u as renderComponentOutput } from "./component-D3kwgmef.mjs";
3
+ import { a as isComponent, c as readComponentHydrateMeta, d as renderComponentOutput, f as resolveComponent, i as finishComponentRender, l as readComponentProps, m as setComponentProps, n as beginComponentRender, t as Component, u as renderComponentErrorOutput } from "./component-2mAsZGIS.mjs";
4
4
  //#region src/server.ts
5
5
  /**
6
6
  * Renders a Pudui child tree to an HTML string.
@@ -36,6 +36,12 @@ function readRawChildHtml(raw) {
36
36
  * @returns `null`, a single child, or a normalized child array.
37
37
  */
38
38
  function packChildren(children) {
39
+ if (children.length === 0) return null;
40
+ if (children.length === 1) {
41
+ const child = children[0];
42
+ if (isEmptyChild(child)) return null;
43
+ return Array.isArray(child) ? packChildren(child) : child;
44
+ }
39
45
  const normalized = normalizeChildren(children);
40
46
  return normalized.length === 0 ? null : normalized.length === 1 ? normalized[0] : normalized;
41
47
  }
@@ -46,7 +52,12 @@ function packChildren(children) {
46
52
  * @returns Flat child array without booleans, `null`, or `undefined`.
47
53
  */
48
54
  function normalizeChildren(children) {
49
- return children.flat(Infinity).filter((child) => !isEmptyChild(child));
55
+ for (const child of children) if (isEmptyChild(child) || Array.isArray(child)) {
56
+ const normalized = [];
57
+ appendNormalizedChildren(normalized, children);
58
+ return normalized;
59
+ }
60
+ return children;
50
61
  }
51
62
  /**
52
63
  * Checks whether a child renders no output.
@@ -57,6 +68,13 @@ function normalizeChildren(children) {
57
68
  function isEmptyChild(child) {
58
69
  return child == null || typeof child === "boolean";
59
70
  }
71
+ function appendNormalizedChildren(normalized, children) {
72
+ for (const child of children) {
73
+ if (isEmptyChild(child)) continue;
74
+ if (Array.isArray(child)) appendNormalizedChildren(normalized, child);
75
+ else normalized.push(child);
76
+ }
77
+ }
60
78
  //#endregion
61
79
  //#region src/core/vnode.ts
62
80
  const vnodeType = Symbol();
@@ -164,11 +182,27 @@ function isHostVNode(vnode) {
164
182
  return typeof vnode.type === "string";
165
183
  }
166
184
  function createVNode(type, rawProps, key, fallbackChildren) {
167
- const props = { ...rawProps };
185
+ let props = rawProps ?? {};
186
+ let clonedProps = rawProps == null;
168
187
  const vnodeKey = key ?? props.key;
169
- delete props.key;
170
- if (fallbackChildren?.length) props.children = packChildren(fallbackChildren);
171
- else if ("children" in props) props.children = packChildren([props.children]);
188
+ if ("key" in props) {
189
+ props = { ...props };
190
+ clonedProps = true;
191
+ delete props.key;
192
+ }
193
+ if (fallbackChildren?.length) {
194
+ if (!clonedProps) {
195
+ props = { ...props };
196
+ clonedProps = true;
197
+ }
198
+ props.children = packChildren(fallbackChildren);
199
+ } else if ("children" in props) {
200
+ const packedChildren = packChildren([props.children]);
201
+ if (packedChildren !== props.children) {
202
+ if (!clonedProps) props = { ...props };
203
+ props.children = packedChildren;
204
+ }
205
+ }
172
206
  if (type === Fragment) return props.children ?? null;
173
207
  if (type === Raw) return Raw(props);
174
208
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pudui",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "A tiny JSX UI runtime built from the ground up.",
5
5
  "license": "MIT",
6
6
  "files": [