pudui 0.0.0 → 0.0.1

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.
@@ -55,14 +55,17 @@ var Component = class {
55
55
  componentInternals.set(this, {
56
56
  i: 0,
57
57
  c: /* @__PURE__ */ new Map(),
58
+ d: false,
58
59
  e: options.error,
60
+ a: options.memo,
59
61
  h: options.hydrate,
60
62
  f: /* @__PURE__ */ new Map(),
63
+ l: void 0,
61
64
  m: false,
62
65
  o: [],
63
66
  p: void 0,
64
67
  r: options.render,
65
- u: []
68
+ u: /* @__PURE__ */ new Set()
66
69
  });
67
70
  }
68
71
  /**
@@ -85,7 +88,9 @@ var Component = class {
85
88
  * @param callback Function to run after the update commits.
86
89
  */
87
90
  update(callback) {
88
- for (const listener of getComponentInternals(this).f.keys()) scheduleUpdate(listener, callback);
91
+ const internals = getComponentInternals(this);
92
+ internals.d = true;
93
+ for (const listener of internals.f.keys()) scheduleUpdate(listener, callback);
89
94
  }
90
95
  };
91
96
  /**
@@ -139,6 +144,27 @@ function renderComponentOutput(component) {
139
144
  return getComponentInternals(component).r(readComponentProps(component));
140
145
  }
141
146
  /**
147
+ * Checks whether a component should render for the current props.
148
+ *
149
+ * @param component Component to inspect.
150
+ * @returns `true` when rendering should continue.
151
+ */
152
+ function shouldRenderComponent(component) {
153
+ const internals = getComponentInternals(component);
154
+ if (internals.d || internals.l === void 0 || internals.a === void 0) return true;
155
+ return !internals.a(internals.l, readComponentProps(component));
156
+ }
157
+ /**
158
+ * Marks the current props as rendered.
159
+ *
160
+ * @param component Component that completed a render pass.
161
+ */
162
+ function markComponentRendered(component) {
163
+ const internals = getComponentInternals(component);
164
+ internals.d = false;
165
+ internals.l = readComponentProps(component);
166
+ }
167
+ /**
142
168
  * Runs a component's error callback for a failed render.
143
169
  *
144
170
  * @param component Component whose render failed.
@@ -158,7 +184,7 @@ function renderComponentErrorOutput(component, reason) {
158
184
  function beginComponentRender(component) {
159
185
  const internals = getComponentInternals(component);
160
186
  internals.i = 0;
161
- internals.u = [];
187
+ internals.u.clear();
162
188
  }
163
189
  /**
164
190
  * Marks the end of a component render pass and prunes unused child instances.
@@ -167,7 +193,7 @@ function beginComponentRender(component) {
167
193
  */
168
194
  function finishComponentRender(component) {
169
195
  const internals = getComponentInternals(component);
170
- for (const childSlot of internals.c.keys()) if (!internals.u.includes(childSlot)) internals.c.delete(childSlot);
196
+ for (const childSlot of internals.c.keys()) if (!internals.u.has(childSlot)) internals.c.delete(childSlot);
171
197
  }
172
198
  /**
173
199
  * Resolves a component virtual node to a component instance.
@@ -179,9 +205,9 @@ function finishComponentRender(component) {
179
205
  function resolveComponent(vnode, owner) {
180
206
  if (!owner) return createComponent(vnode.type, vnode.props);
181
207
  const internals = getComponentInternals(owner);
182
- const slot = vnode.key === void 0 ? "." + internals.i++ : "#" + String(vnode.key);
208
+ const slot = vnode.key === void 0 ? "." + internals.i++ : typeof vnode.key === "number" ? vnode.key : "#" + vnode.key;
183
209
  const existing = internals.c.get(slot);
184
- internals.u.push(slot);
210
+ internals.u.add(slot);
185
211
  if (existing?.t === vnode.type) {
186
212
  setComponentProps(existing.v, vnode.props);
187
213
  return existing.v;
@@ -207,6 +233,28 @@ function createComponent(factory, props) {
207
233
  return component;
208
234
  }
209
235
  /**
236
+ * Creates a component factory that skips parent-driven rerenders when props are equal.
237
+ *
238
+ * @param factory Component factory to memoize.
239
+ * @param compare Optional prop equality check. Defaults to shallow equality.
240
+ * @returns Memoized component factory.
241
+ */
242
+ function memo(factory, compare = shallowEqualProps) {
243
+ return (props) => {
244
+ const component = createComponent(factory, props);
245
+ getComponentInternals(component).a = compare;
246
+ return component;
247
+ };
248
+ }
249
+ function shallowEqualProps(previousProps, nextProps) {
250
+ if (previousProps === nextProps) return true;
251
+ const previousKeys = Object.keys(previousProps);
252
+ const nextKeys = Object.keys(nextProps);
253
+ if (previousKeys.length !== nextKeys.length) return false;
254
+ for (const key of previousKeys) if (previousProps[key] !== nextProps[key] || !Object.hasOwn(nextProps, key)) return false;
255
+ return true;
256
+ }
257
+ /**
210
258
  * Subscribes a renderer to component updates.
211
259
  *
212
260
  * @param component Component to observe.
@@ -272,4 +320,4 @@ function isPromiseLike(value) {
272
320
  return typeof value?.then === "function";
273
321
  }
274
322
  //#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 };
323
+ export { fail as _, isComponent as a, queueComponentMount as c, renderComponentErrorOutput as d, renderComponentOutput as f, subscribeComponent as g, shouldRenderComponent as h, finishComponentRender as i, readComponentHydrateMeta as l, setComponentProps as m, beginComponentRender as n, markComponentRendered as o, resolveComponent as p, createComponent as r, memo as s, Component as t, readComponentProps as u, invariant as v };
@@ -130,6 +130,14 @@ declare class Component<Props extends AnyProps = AnyProps> {
130
130
  * @returns `true` when the value is a {@link Component}.
131
131
  */
132
132
  declare function isComponent(value: unknown): value is Component;
133
+ /**
134
+ * Creates a component factory that skips parent-driven rerenders when props are equal.
135
+ *
136
+ * @param factory Component factory to memoize.
137
+ * @param compare Optional prop equality check. Defaults to shallow equality.
138
+ * @returns Memoized component factory.
139
+ */
140
+ declare function memo<Props extends AnyProps>(factory: ComponentFactory<Props>, compare?: ComponentMemoCompare<Props>): ComponentFactoryWithProps<Props>;
133
141
  //#endregion
134
142
  //#region src/core.d.ts
135
143
  /**
@@ -235,6 +243,10 @@ type ComponentFactoryWithoutProps<Props extends AnyProps = AnyProps> = () => Com
235
243
  * A function component constructor used as a JSX tag.
236
244
  */
237
245
  type ComponentFactory<Props extends AnyProps = AnyProps> = ComponentFactoryWithProps<Props> | ComponentFactoryWithoutProps<Props>;
246
+ /**
247
+ * Returns `true` when two prop objects are equivalent for a memoized component.
248
+ */
249
+ type ComponentMemoCompare<Props extends AnyProps = AnyProps> = (previousProps: Readonly<Props>, nextProps: Readonly<Props>) => boolean;
238
250
  /**
239
251
  * A JSX tag target: either an intrinsic element name or a component factory.
240
252
  */
@@ -387,6 +399,12 @@ type ComponentOptions<Props extends AnyProps> = {
387
399
  * Metadata consumed by the hydration macros and Vite runtime.
388
400
  */
389
401
  hydrate?: unknown;
402
+ /**
403
+ * Skips parent-driven rerenders while previous and next props are equivalent.
404
+ *
405
+ * Internal component updates still render even when props are unchanged.
406
+ */
407
+ memo?: ComponentMemoCompare<Props>;
390
408
  /**
391
409
  * Renders the component's current child tree.
392
410
  *
@@ -431,4 +449,4 @@ declare global {
431
449
  * Internal marker range and props for a server-rendered hydration boundary.
432
450
  */
433
451
  //#endregion
434
- export { Fragment as A, RefValue as C, VNode as D, UpdateCallback as E, jsxDEV as F, createElement as M, isVNode as N, Component as O, jsx as P, RefCleanup as S, StyleValue as T, PreloadResolver as _, ComponentFactoryWithoutProps as a, RawProps as b, ComponentPropsFor as c, EmptyChild as d, IntrinsicElementProps as f, PreloadMetadata as g, MountCallback as h, ComponentFactoryWithProps as i, Raw as j, isComponent as k, DangerouslySetInnerHTML as l, Key as m, Child as n, ComponentInstance as o, IntrinsicElementsByTagName as p, ComponentFactory as r, ComponentOptions as s, AnyProps as t, ElementType as u, PrimitiveChild as v, RenderableType as w, RefCallback as x, RawChild as y };
452
+ export { isComponent as A, RefCleanup as C, UpdateCallback as D, StyleValue as E, isVNode as F, jsx as I, jsxDEV as L, Fragment as M, Raw as N, VNode as O, createElement as P, RefCallback as S, RenderableType as T, PreloadMetadata as _, ComponentFactoryWithoutProps as a, RawChild as b, ComponentOptions as c, ElementType as d, EmptyChild as f, MountCallback as g, Key as h, ComponentFactoryWithProps as i, memo as j, Component as k, ComponentPropsFor as l, IntrinsicElementsByTagName as m, Child as n, ComponentInstance as o, IntrinsicElementProps as p, ComponentFactory as r, ComponentMemoCompare as s, AnyProps as t, DangerouslySetInnerHTML as u, PreloadResolver as v, RefValue as w, RawProps as x, PrimitiveChild as y };
@@ -1,4 +1,4 @@
1
- import { O as Component, i as ComponentFactoryWithProps, s as ComponentOptions, t as AnyProps } from "./core-Cypb6mR9.mjs";
1
+ import { c as ComponentOptions, i as ComponentFactoryWithProps, k as Component, t as AnyProps } from "./core-C_ayj9MV.mjs";
2
2
 
3
3
  //#region src/hmr-runtime.d.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { t as Component } from "./component-D3kwgmef.mjs";
1
+ import { t as Component } from "./component-CHY49Hb1.mjs";
2
2
  //#region src/hmr-runtime.ts
3
3
  const globalRegistryKey = "__puduiHmrRegistry";
4
4
  const moduleDataKey = "__puduiHmr";
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as Fragment, C as RefValue, D as VNode, E as UpdateCallback, F as jsxDEV, M as createElement, N as isVNode, O as Component, P as jsx, S as RefCleanup, T as StyleValue, _ as PreloadResolver, a as ComponentFactoryWithoutProps, b as RawProps, c as ComponentPropsFor, d as EmptyChild, f as IntrinsicElementProps, g as PreloadMetadata, h as MountCallback, i as ComponentFactoryWithProps, j as Raw, k as isComponent, l as DangerouslySetInnerHTML, m as Key, n as Child, o as ComponentInstance, p as IntrinsicElementsByTagName, r as ComponentFactory, s as ComponentOptions, t as AnyProps, u as ElementType, v as PrimitiveChild, w as RenderableType, x as RefCallback, y as RawChild } from "./core-Cypb6mR9.mjs";
1
+ import { A as isComponent, C as RefCleanup, D as UpdateCallback, E as StyleValue, F as isVNode, I as jsx, L as jsxDEV, M as Fragment, N as Raw, O as VNode, P as createElement, S as RefCallback, T as RenderableType, _ as PreloadMetadata, a as ComponentFactoryWithoutProps, b as RawChild, c as ComponentOptions, d as ElementType, f as EmptyChild, g as MountCallback, h as Key, i as ComponentFactoryWithProps, j as memo, k as Component, l as ComponentPropsFor, m as IntrinsicElementsByTagName, n as Child, o as ComponentInstance, p as IntrinsicElementProps, r as ComponentFactory, s as ComponentMemoCompare, t as AnyProps, u as DangerouslySetInnerHTML, v as PreloadResolver, w as RefValue, x as RawProps, y as PrimitiveChild } from "./core-C_ayj9MV.mjs";
2
2
 
3
3
  //#region src/client.d.ts
4
4
  /**
@@ -106,4 +106,4 @@ declare function render(child: Child, container: Element | DocumentFragment): Hy
106
106
  */
107
107
  declare function hydrate(root: Document | DocumentFragment | Element, options: HydrateOptions): HydrationRoot;
108
108
  //#endregion
109
- export { type AnyProps, type Child, Component, type ComponentFactory, type ComponentFactoryWithProps, type ComponentFactoryWithoutProps, type ComponentInstance, type ComponentOptions, type ComponentPropsFor, type DangerouslySetInnerHTML, type ElementType, type EmptyChild, type EventForName, type EventHandler, Fragment, type HydrateLoad, type HydrateOptions, type HydrationRoot, type IntrinsicElementProps, type IntrinsicElementsByTagName, type Key, type MountCallback, type PreloadMetadata, type PreloadResolver, type PrimitiveChild, Raw, type RawChild, type RawProps, type RefCallback, type RefCleanup, type RefValue, type RenderableType, type StyleValue, type UpdateCallback, type VNode, createElement, event, hydrate, isComponent, isVNode, jsx, jsx as jsxs, jsxDEV, render };
109
+ export { type AnyProps, type Child, Component, type ComponentFactory, type ComponentFactoryWithProps, type ComponentFactoryWithoutProps, type ComponentInstance, type ComponentMemoCompare, type ComponentOptions, type ComponentPropsFor, type DangerouslySetInnerHTML, type ElementType, type EmptyChild, type EventForName, type EventHandler, Fragment, type HydrateLoad, type HydrateOptions, type HydrationRoot, type IntrinsicElementProps, type IntrinsicElementsByTagName, type Key, type MountCallback, type PreloadMetadata, type PreloadResolver, type PrimitiveChild, Raw, type RawChild, type RawProps, type RefCallback, type RefCleanup, type RefValue, type RenderableType, type StyleValue, type UpdateCallback, type VNode, createElement, event, hydrate, isComponent, isVNode, jsx, jsx as jsxs, jsxDEV, memo, render };
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 fail, a as isComponent, c as queueComponentMount, d as renderComponentErrorOutput, f as renderComponentOutput, g as subscribeComponent, h as shouldRenderComponent, i as finishComponentRender, l as readComponentHydrateMeta, m as setComponentProps, n as beginComponentRender, o as markComponentRendered, p as resolveComponent, r as createComponent, s as memo, t as Component, v as invariant } from "./component-CHY49Hb1.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,12 @@ 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") return componentToRenderNode(resolveComponent(child, context.owner), context, child.key);
503
523
  return elementToRenderNode(child, context);
504
524
  }
505
525
  return textToRenderNode(null, context.document);
506
526
  }
507
- function hydrateChildToDom(child, context, currentNode, rootComponent, parent) {
527
+ function hydrateChildToDom(child, context, currentNode, rootComponent, parent, key) {
508
528
  if (isHydratedChildren(child)) return hydrateHydratedChildren(child, currentNode, parent);
509
529
  if (isEmptyChild(child) || isPrimitiveValue(child)) return hydrateTextToDom(child, currentNode, parent);
510
530
  if (isRawChild(child)) return hydrateRawToDom(child, context, currentNode, parent);
@@ -530,10 +550,13 @@ function hydrateChildToDom(child, context, currentNode, rootComponent, parent) {
530
550
  }
531
551
  if (isComponent(child)) {
532
552
  if (child !== rootComponent && readComponentHydrateMeta(child) !== void 0) return hydrateSkippedBoundary(currentNode, parent);
533
- return hydrateComponentToDom(child, context, currentNode, rootComponent, parent);
553
+ return hydrateComponentToDom(child, context, currentNode, rootComponent, parent, void 0, void 0, key);
534
554
  }
535
555
  if (isVNode(child)) {
536
- if (typeof child.type === "function") return hydrateChildToDom(resolveComponent(child, context.owner), context, currentNode, rootComponent, parent);
556
+ if (typeof child.type === "function") {
557
+ const vnode = child;
558
+ return hydrateChildToDom(resolveComponent(vnode, context.owner), context, currentNode, rootComponent, parent, vnode.key);
559
+ }
537
560
  return hydrateElementToDom(child, context, currentNode, rootComponent, parent);
538
561
  }
539
562
  return hydrateTextToDom(null, currentNode, parent);
@@ -563,7 +586,7 @@ function hydrateElementToDom(vnode, context, currentNode, rootComponent, parent)
563
586
  r: node
564
587
  };
565
588
  }
566
- function hydrateComponentToDom(component, context, currentNode, rootComponent, parent, start, end) {
589
+ function hydrateComponentToDom(component, context, currentNode, rootComponent, parent, start, end, key) {
567
590
  const componentStart = start ?? insertMarker(parent, currentNode);
568
591
  const unsubscribe = subscribeComponent(component, context.scheduleUpdate);
569
592
  let result;
@@ -587,6 +610,7 @@ function hydrateComponentToDom(component, context, currentNode, rootComponent, p
587
610
  c: result.r,
588
611
  e: componentEnd,
589
612
  k: componentRenderNodeKind,
613
+ q: key,
590
614
  s: componentStart,
591
615
  u: unsubscribe,
592
616
  v: component
@@ -706,7 +730,14 @@ function patchRenderNode(node, nextChild, context) {
706
730
  return replace();
707
731
  }
708
732
  if (isVNode(nextChild)) {
709
- if (typeof nextChild.type === "function") return patchRenderNode(node, resolveComponent(nextChild, context.owner), context);
733
+ if (typeof nextChild.type === "function") {
734
+ const component = resolveComponent(nextChild, context.owner);
735
+ if (node.k === componentRenderNodeKind && node.v === component) {
736
+ node.q = nextChild.key;
737
+ return patchComponentRenderNode(node, component, context);
738
+ }
739
+ return replaceRenderNode(node, component, context, nextChild.key);
740
+ }
710
741
  const vnode = nextChild;
711
742
  if (node.k === elementRenderNodeKind && node.v.type === vnode.type && node.v.key === vnode.key) return patchElementRenderNode(node, vnode, context);
712
743
  return replace();
@@ -783,7 +814,7 @@ function patchKeyedArrayRenderNode(node, normalizedChildren, context) {
783
814
  function shouldPatchKeyedArray(oldNodes, nextChildren) {
784
815
  const firstOld = oldNodes[0];
785
816
  const firstNext = nextChildren[0];
786
- if (firstOld?.k !== elementRenderNodeKind || firstOld.v.key === void 0 || !isVNode(firstNext) || firstNext.key === void 0) return false;
817
+ if (firstOld === void 0 || readRenderNodeKey(firstOld) === void 0 || !isVNode(firstNext) || firstNext.key === void 0) return false;
787
818
  if (oldNodes.length <= nextChildren.length) {
788
819
  for (let index = 0; index < oldNodes.length; index++) if (readRenderNodeKey(oldNodes[index]) !== readChildKey(nextChildren[index])) return true;
789
820
  return false;
@@ -794,7 +825,8 @@ function readChildKey(child) {
794
825
  return isVNode(child) ? child.key : void 0;
795
826
  }
796
827
  function readRenderNodeKey(node) {
797
- return node.k === elementRenderNodeKind ? node.v.key : void 0;
828
+ if (node.k === elementRenderNodeKind) return node.v.key;
829
+ if (node.k === componentRenderNodeKind) return node.q;
798
830
  }
799
831
  function lis(values) {
800
832
  const p = Array.from({ length: values.length });
@@ -820,6 +852,7 @@ function lis(values) {
820
852
  return t;
821
853
  }
822
854
  function patchComponentRenderNode(node, component, context) {
855
+ if (!shouldRenderComponent(component)) return node;
823
856
  return renderComponentChild(component, context.effects, (renderedChild) => {
824
857
  node.c = patchRenderNode(node.c, renderedChild, renderContextWithOwner(context, component));
825
858
  return node;
@@ -871,10 +904,10 @@ function patchElementRenderNode(node, vnode, context) {
871
904
  if (refChanged) collectRefEffects(node.n, vnode.props.ref, context.effects, node.r);
872
905
  return node;
873
906
  }
874
- function replaceRenderNode(node, nextChild, context) {
907
+ function replaceRenderNode(node, nextChild, context, key) {
875
908
  const parent = renderNodeStart(node).parentNode;
876
909
  const nextSibling = renderNodeEnd(node).nextSibling;
877
- const replacement = childToRenderNode(nextChild, context);
910
+ const replacement = isComponent(nextChild) ? componentToRenderNode(nextChild, context, key) : childToRenderNode(nextChild, context);
878
911
  cleanupRenderNode(node);
879
912
  removeRenderNode(node);
880
913
  if (parent) insertRenderNode(parent, replacement, nextSibling);
@@ -1182,7 +1215,7 @@ function arrayToRenderNode(children, context) {
1182
1215
  s: createMarker(context.document)
1183
1216
  };
1184
1217
  }
1185
- function componentToRenderNode(component, context) {
1218
+ function componentToRenderNode(component, context, key) {
1186
1219
  const unsubscribe = subscribeComponent(component, context.scheduleUpdate);
1187
1220
  let child;
1188
1221
  try {
@@ -1200,9 +1233,8 @@ function componentToRenderNode(component, context) {
1200
1233
  }
1201
1234
  return {
1202
1235
  c: child,
1203
- e: createMarker(context.document),
1204
1236
  k: componentRenderNodeKind,
1205
- s: createMarker(context.document),
1237
+ q: key,
1206
1238
  u: unsubscribe,
1207
1239
  v: component
1208
1240
  };
@@ -1306,9 +1338,9 @@ function appendRenderNode(parent, node) {
1306
1338
  parent.appendChild(node.e);
1307
1339
  return;
1308
1340
  case componentRenderNodeKind:
1309
- parent.appendChild(node.s);
1341
+ if (node.s !== void 0) parent.appendChild(node.s);
1310
1342
  appendRenderNode(parent, node.c);
1311
- parent.appendChild(node.e);
1343
+ if (node.e !== void 0) parent.appendChild(node.e);
1312
1344
  return;
1313
1345
  case elementRenderNodeKind:
1314
1346
  parent.appendChild(node.n);
@@ -1393,10 +1425,14 @@ function cleanupRenderNode(node) {
1393
1425
  }
1394
1426
  }
1395
1427
  function renderNodeStart(node) {
1396
- return node.k === elementRenderNodeKind || node.k === textRenderNodeKind ? node.n : node.s;
1428
+ if (node.k === elementRenderNodeKind || node.k === textRenderNodeKind) return node.n;
1429
+ if (node.k === componentRenderNodeKind) return node.s ?? renderNodeStart(node.c);
1430
+ return node.s;
1397
1431
  }
1398
1432
  function renderNodeEnd(node) {
1399
- return node.k === elementRenderNodeKind || node.k === textRenderNodeKind ? node.n : node.e;
1433
+ if (node.k === elementRenderNodeKind || node.k === textRenderNodeKind) return node.n;
1434
+ if (node.k === componentRenderNodeKind) return node.e ?? renderNodeEnd(node.c);
1435
+ return node.e;
1400
1436
  }
1401
1437
  function ownerDocumentFor(node) {
1402
1438
  if (node instanceof Document) return node;
@@ -1411,6 +1447,7 @@ function renderComponentChild(component, effects, renderChild) {
1411
1447
  try {
1412
1448
  const result = renderChild(renderComponentOutput(component));
1413
1449
  queueComponentMount(component, effects.m);
1450
+ markComponentRendered(component);
1414
1451
  return result;
1415
1452
  } catch (reason) {
1416
1453
  effects.r.splice(refEffectStart);
@@ -1418,6 +1455,7 @@ function renderComponentChild(component, effects, renderChild) {
1418
1455
  beginComponentRender(component);
1419
1456
  const result = renderChild(renderComponentErrorOutput(component, reason));
1420
1457
  queueComponentMount(component, effects.m);
1458
+ markComponentRendered(component);
1421
1459
  return result;
1422
1460
  }
1423
1461
  } finally {
@@ -1502,4 +1540,4 @@ function isCommentData(node, isMarkerCommentData) {
1502
1540
  return node?.nodeType === Node.COMMENT_NODE && isMarkerCommentData(node.data);
1503
1541
  }
1504
1542
  //#endregion
1505
- export { Component, Fragment, Raw, createElement, event, hydrate, isComponent, isVNode, jsx, jsx as jsxs, jsxDEV, render };
1543
+ export { Component, Fragment, Raw, createElement, event, hydrate, isComponent, isVNode, jsx, jsx as jsxs, jsxDEV, memo, render };
@@ -1,4 +1,4 @@
1
- import { A as Fragment, F as jsxDEV, c as ComponentPropsFor, f as IntrinsicElementProps, n as Child, p as IntrinsicElementsByTagName } from "./core-Cypb6mR9.mjs";
1
+ import { L as jsxDEV, M as Fragment, l as ComponentPropsFor, m as IntrinsicElementsByTagName, n as Child, p as IntrinsicElementProps } from "./core-C_ayj9MV.mjs";
2
2
 
3
3
  //#region src/jsx-dev-runtime.d.ts
4
4
  /**
@@ -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,4 +1,4 @@
1
- import { A as Fragment, P as jsx, c as ComponentPropsFor, f as IntrinsicElementProps, n as Child, p as IntrinsicElementsByTagName } from "./core-Cypb6mR9.mjs";
1
+ import { I as jsx, M as Fragment, l as ComponentPropsFor, m as IntrinsicElementsByTagName, n as Child, p as IntrinsicElementProps } from "./core-C_ayj9MV.mjs";
2
2
 
3
3
  //#region src/jsx-runtime.d.ts
4
4
  /**
@@ -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.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as Fragment, C as RefValue, D as VNode, E as UpdateCallback, F as jsxDEV, M as createElement, N as isVNode, O as Component, P as jsx, S as RefCleanup, T as StyleValue, _ as PreloadResolver, a as ComponentFactoryWithoutProps, b as RawProps, c as ComponentPropsFor, d as EmptyChild, f as IntrinsicElementProps, h as MountCallback, i as ComponentFactoryWithProps, j as Raw, k as isComponent, l as DangerouslySetInnerHTML, m as Key, n as Child, o as ComponentInstance, p as IntrinsicElementsByTagName, r as ComponentFactory, s as ComponentOptions, t as AnyProps, u as ElementType, v as PrimitiveChild, w as RenderableType, x as RefCallback, y as RawChild } from "./core-Cypb6mR9.mjs";
1
+ import { A as isComponent, C as RefCleanup, D as UpdateCallback, E as StyleValue, F as isVNode, I as jsx, L as jsxDEV, M as Fragment, N as Raw, O as VNode, P as createElement, S as RefCallback, T as RenderableType, a as ComponentFactoryWithoutProps, b as RawChild, c as ComponentOptions, d as ElementType, f as EmptyChild, g as MountCallback, h as Key, i as ComponentFactoryWithProps, k as Component, l as ComponentPropsFor, m as IntrinsicElementsByTagName, n as Child, o as ComponentInstance, p as IntrinsicElementProps, r as ComponentFactory, t as AnyProps, u as DangerouslySetInnerHTML, v as PreloadResolver, w as RefValue, x as RawProps, y as PrimitiveChild } from "./core-C_ayj9MV.mjs";
2
2
 
3
3
  //#region src/server.d.ts
4
4
  /**
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, d as renderComponentErrorOutput, f as renderComponentOutput, i as finishComponentRender, l as readComponentHydrateMeta, m as setComponentProps, n as beginComponentRender, p as resolveComponent, t as Component, u as readComponentProps } from "./component-CHY49Hb1.mjs";
4
4
  //#region src/server.ts
5
5
  /**
6
6
  * Renders a Pudui child tree to an HTML string.
@@ -1,4 +1,4 @@
1
- import { g as PreloadMetadata } from "./core-Cypb6mR9.mjs";
1
+ import { _ as PreloadMetadata } from "./core-C_ayj9MV.mjs";
2
2
 
3
3
  //#region src/vite-runtime.d.ts
4
4
  /**
@@ -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.1",
4
4
  "description": "A tiny JSX UI runtime built from the ground up.",
5
5
  "license": "MIT",
6
6
  "files": [