@estjs/template 0.0.14-beta.9 → 0.0.14

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.
@@ -4,6 +4,17 @@ import * as csstype from 'csstype';
4
4
  type EssorComponent = (props: Record<string, unknown>) => JSX.Element | TemplateNode;
5
5
  type Hook = 'mounted' | 'destroy';
6
6
 
7
+ interface NodeTrack {
8
+ cleanup: () => void;
9
+ isRoot?: boolean;
10
+ lastNodes?: Map<string, Node | JSX.Element>;
11
+ }
12
+ interface NodeTrack {
13
+ cleanup: () => void;
14
+ isRoot?: boolean;
15
+ lastNodes?: Map<string, Node | JSX.Element>;
16
+ }
17
+
7
18
  type Props = Record<string, any>;
8
19
 
9
20
  interface EssorNode<T = Record<string, any>> {
@@ -2212,30 +2223,71 @@ declare class LifecycleContext {
2212
2223
  clearHooks(): void;
2213
2224
  }
2214
2225
 
2226
+ declare class TemplateNode$1 implements JSX.Element {
2227
+ template: HTMLTemplateElement;
2228
+ props: Props;
2229
+ key?: string | undefined;
2230
+ protected treeMap: Map<number, Node>;
2231
+ protected mounted: boolean;
2232
+ protected nodes: Node[];
2233
+ protected trackMap: Map<string, NodeTrack>;
2234
+ protected bindValueKeys: string[];
2235
+ protected componentIndex: number;
2236
+ protected parent: Node | null;
2237
+ constructor(template: HTMLTemplateElement, props: Props, key?: string | undefined);
2238
+ get firstChild(): Node | null;
2239
+ get isConnected(): boolean;
2240
+ addEventListener(): void;
2241
+ removeEventListener(): void;
2242
+ mount(parent: Node, before?: Node | null): Node[];
2243
+ unmount(): void;
2244
+ inheritNode(node: TemplateNode$1): void;
2245
+ protected mapSSGNodeTree(parent: Node): void;
2246
+ protected mapNodeTree(parent: Node, tree: Node): void;
2247
+ protected walkNodeTree(node: Node, handler: (node: Node) => void): void;
2248
+ protected handleSSGNode(node: Node): void;
2249
+ protected patchProps(props: Record<string, Record<string, unknown>> | undefined): void;
2250
+ protected patchProp(key: string, node: Node, props: Record<string, unknown>, isRoot: boolean): void;
2251
+ protected getBindUpdateValue(props: Record<string, any>, key: string, attr: string): any;
2252
+ protected patchChildren(key: string, node: Node, children: unknown, isRoot: boolean): void;
2253
+ protected patchEventListener(key: string, node: Node, attr: string, listener: EventListener): void;
2254
+ protected patchAttribute(key: string, element: HTMLElement, attr: string, value: unknown, updateFn?: Function): void;
2255
+ protected getNodeTrack(trackKey: string, trackLastNodes?: boolean, isRoot?: boolean): NodeTrack;
2256
+ protected patchChild(track: NodeTrack, parent: Node, child: unknown, before: Node | null): void;
2257
+ protected reconcileChildren(parent: Node, nextNodes: Node[], before: Node | null): Map<string, Node>;
2258
+ }
2259
+
2215
2260
  declare class ComponentNode$1 extends LifecycleContext implements JSX.Element {
2216
2261
  template: EssorComponent;
2217
- props?: Props | undefined;
2262
+ props: Props;
2218
2263
  key?: string | undefined;
2219
- private proxyProps;
2220
- private emitter;
2221
- private rootNode;
2222
- private trackMap;
2223
- constructor(template: EssorComponent, props?: Props | undefined, key?: string | undefined);
2224
- private createProxyProps;
2264
+ protected proxyProps: Record<string, Signal$1<unknown>>;
2265
+ protected emitter: Set<() => void>;
2266
+ protected rootNode: TemplateNode$1 | null;
2267
+ protected trackMap: Map<string, NodeTrack>;
2268
+ protected nodes: Node[];
2269
+ protected parent: Node | null;
2270
+ protected before: Node | null;
2271
+ constructor(template: EssorComponent, props: Props, key?: string | undefined);
2225
2272
  get firstChild(): Node | null;
2226
2273
  get isConnected(): boolean;
2227
- mount(parent: Node, before?: Node | null): Node[];
2274
+ mount(parent: Node, before: Node | null): Node[];
2228
2275
  unmount(): void;
2229
- private callMountHooks;
2230
- private callDestroyHooks;
2231
- private clearEmitter;
2276
+ private resetState;
2277
+ protected callLifecycleHooks(type: 'mounted' | 'destroy'): void;
2278
+ protected callMountHooks(): void;
2279
+ protected callDestroyHooks(): void;
2280
+ protected clearEmitter(): void;
2232
2281
  inheritNode(node: ComponentNode$1): void;
2233
- private getNodeTrack;
2282
+ protected getNodeTrack(trackKey: string): NodeTrack;
2234
2283
  patchProps(props: Record<string, any> | undefined): void;
2235
- private patchEventListener;
2236
- private patchRef;
2237
- private patchUpdateHandler;
2238
- private patchNormalProp;
2284
+ protected patchEventListener(key: string, prop: any): void;
2285
+ protected patchRef(prop: {
2286
+ value: Node | null;
2287
+ }): void;
2288
+ protected patchUpdateHandler(key: string, prop: any): void;
2289
+ protected patchNormalProp(key: string, prop: any): void;
2290
+ protected cleanup(): void;
2239
2291
  }
2240
2292
 
2241
2293
  /**
@@ -2270,14 +2322,12 @@ declare function isJsxElement(node: unknown): node is EssorNode;
2270
2322
  * @returns A new HTML template element.
2271
2323
  */
2272
2324
  declare function createTemplate(html: string): HTMLTemplateElement;
2273
- /**
2274
- * @param props - An object containing the `children` prop.
2275
- * @param props.children - The children to be rendered. Can be a single JSX element, string, number, boolean,
2276
- * or an array of these. Falsy values in arrays will be filtered out.
2277
- * @returns A JSX element representing the fragment, wrapping the filtered children.
2278
- */
2279
- declare function Fragment<T extends JSX.JSXElement | string | number | boolean | (JSX.JSXElement | string | number | boolean)[]>(props: {
2325
+ declare function Fragment<T extends JSX.JSXElement | string | number | boolean | Array<JSX.JSXElement | string | number | boolean>>(template: HTMLTemplateElement | '', props: {
2280
2326
  children: T;
2327
+ } | {
2328
+ [key: string]: {
2329
+ children: T;
2330
+ };
2281
2331
  }): JSX.Element;
2282
2332
 
2283
2333
  /**
@@ -2309,7 +2359,7 @@ interface InjectionKey<T> extends Symbol {
2309
2359
  * @param key - The key to store the value in the LifecycleContext with.
2310
2360
  * @param value - The value to store in the LifecycleContext with the given key.
2311
2361
  */
2312
- declare function useProvide<T, K = InjectionKey<T> | string | number>(key: K, value: K extends InjectionKey<infer V> ? V : T): void;
2362
+ declare function provide<T, K = InjectionKey<T> | string | number>(key: K, value: K extends InjectionKey<infer V> ? V : T): void;
2313
2363
  /**
2314
2364
  * Injects a value from the current component LifecycleContext.
2315
2365
  *
@@ -2322,21 +2372,7 @@ declare function useProvide<T, K = InjectionKey<T> | string | number>(key: K, va
2322
2372
  * @returns The value stored in the LifecycleContext with the given key, or the default
2323
2373
  * value if the key is not present in the LifecycleContext.
2324
2374
  */
2325
- declare function useInject<T, K = InjectionKey<T> | string | number>(key: K, defaultValue?: K extends InjectionKey<infer V> ? V : T): (K extends InjectionKey<infer V> ? V : T) | undefined;
2326
- /**
2327
- * Creates a reactive ref that can be used to reference a DOM node
2328
- * or a component instance within the component function body.
2329
- *
2330
- * @returns a reactive ref signal
2331
- *
2332
- * @example
2333
- * const inputRef = useRef<HTMLInputElement>()
2334
- *
2335
- * <input ref={inputRef} />
2336
- *
2337
- * inputRef.value // input element
2338
- */
2339
- declare function useRef<T>(): Signal$1<T | null>;
2375
+ declare function inject<T, K = InjectionKey<T> | string | number>(key: K, defaultValue?: K extends InjectionKey<infer V> ? V : T): (K extends InjectionKey<infer V> ? V : T) | undefined;
2340
2376
 
2341
2377
  declare class SSGNode extends LifecycleContext {
2342
2378
  private template;
@@ -2386,4 +2422,4 @@ declare function hydrate(component: EssorComponent, container: string | Element)
2386
2422
  */
2387
2423
  declare function ssg(component: EssorComponent, props?: Props): SSGNode | JSX.Element;
2388
2424
 
2389
- export { Fragment, type InjectionKey, h, hydrate, isComponent, isJsxElement, onDestroy, onMount, renderToString, ssg, createTemplate as template, useInject, useProvide, useRef };
2425
+ export { Fragment, type InjectionKey, h, hydrate, inject, isComponent, isJsxElement, onDestroy, onMount, provide, renderToString, ssg, createTemplate as template };
@@ -4,6 +4,17 @@ import * as csstype from 'csstype';
4
4
  type EssorComponent = (props: Record<string, unknown>) => JSX.Element | TemplateNode;
5
5
  type Hook = 'mounted' | 'destroy';
6
6
 
7
+ interface NodeTrack {
8
+ cleanup: () => void;
9
+ isRoot?: boolean;
10
+ lastNodes?: Map<string, Node | JSX.Element>;
11
+ }
12
+ interface NodeTrack {
13
+ cleanup: () => void;
14
+ isRoot?: boolean;
15
+ lastNodes?: Map<string, Node | JSX.Element>;
16
+ }
17
+
7
18
  type Props = Record<string, any>;
8
19
 
9
20
  interface EssorNode<T = Record<string, any>> {
@@ -2212,30 +2223,71 @@ declare class LifecycleContext {
2212
2223
  clearHooks(): void;
2213
2224
  }
2214
2225
 
2226
+ declare class TemplateNode$1 implements JSX.Element {
2227
+ template: HTMLTemplateElement;
2228
+ props: Props;
2229
+ key?: string | undefined;
2230
+ protected treeMap: Map<number, Node>;
2231
+ protected mounted: boolean;
2232
+ protected nodes: Node[];
2233
+ protected trackMap: Map<string, NodeTrack>;
2234
+ protected bindValueKeys: string[];
2235
+ protected componentIndex: number;
2236
+ protected parent: Node | null;
2237
+ constructor(template: HTMLTemplateElement, props: Props, key?: string | undefined);
2238
+ get firstChild(): Node | null;
2239
+ get isConnected(): boolean;
2240
+ addEventListener(): void;
2241
+ removeEventListener(): void;
2242
+ mount(parent: Node, before?: Node | null): Node[];
2243
+ unmount(): void;
2244
+ inheritNode(node: TemplateNode$1): void;
2245
+ protected mapSSGNodeTree(parent: Node): void;
2246
+ protected mapNodeTree(parent: Node, tree: Node): void;
2247
+ protected walkNodeTree(node: Node, handler: (node: Node) => void): void;
2248
+ protected handleSSGNode(node: Node): void;
2249
+ protected patchProps(props: Record<string, Record<string, unknown>> | undefined): void;
2250
+ protected patchProp(key: string, node: Node, props: Record<string, unknown>, isRoot: boolean): void;
2251
+ protected getBindUpdateValue(props: Record<string, any>, key: string, attr: string): any;
2252
+ protected patchChildren(key: string, node: Node, children: unknown, isRoot: boolean): void;
2253
+ protected patchEventListener(key: string, node: Node, attr: string, listener: EventListener): void;
2254
+ protected patchAttribute(key: string, element: HTMLElement, attr: string, value: unknown, updateFn?: Function): void;
2255
+ protected getNodeTrack(trackKey: string, trackLastNodes?: boolean, isRoot?: boolean): NodeTrack;
2256
+ protected patchChild(track: NodeTrack, parent: Node, child: unknown, before: Node | null): void;
2257
+ protected reconcileChildren(parent: Node, nextNodes: Node[], before: Node | null): Map<string, Node>;
2258
+ }
2259
+
2215
2260
  declare class ComponentNode$1 extends LifecycleContext implements JSX.Element {
2216
2261
  template: EssorComponent;
2217
- props?: Props | undefined;
2262
+ props: Props;
2218
2263
  key?: string | undefined;
2219
- private proxyProps;
2220
- private emitter;
2221
- private rootNode;
2222
- private trackMap;
2223
- constructor(template: EssorComponent, props?: Props | undefined, key?: string | undefined);
2224
- private createProxyProps;
2264
+ protected proxyProps: Record<string, Signal$1<unknown>>;
2265
+ protected emitter: Set<() => void>;
2266
+ protected rootNode: TemplateNode$1 | null;
2267
+ protected trackMap: Map<string, NodeTrack>;
2268
+ protected nodes: Node[];
2269
+ protected parent: Node | null;
2270
+ protected before: Node | null;
2271
+ constructor(template: EssorComponent, props: Props, key?: string | undefined);
2225
2272
  get firstChild(): Node | null;
2226
2273
  get isConnected(): boolean;
2227
- mount(parent: Node, before?: Node | null): Node[];
2274
+ mount(parent: Node, before: Node | null): Node[];
2228
2275
  unmount(): void;
2229
- private callMountHooks;
2230
- private callDestroyHooks;
2231
- private clearEmitter;
2276
+ private resetState;
2277
+ protected callLifecycleHooks(type: 'mounted' | 'destroy'): void;
2278
+ protected callMountHooks(): void;
2279
+ protected callDestroyHooks(): void;
2280
+ protected clearEmitter(): void;
2232
2281
  inheritNode(node: ComponentNode$1): void;
2233
- private getNodeTrack;
2282
+ protected getNodeTrack(trackKey: string): NodeTrack;
2234
2283
  patchProps(props: Record<string, any> | undefined): void;
2235
- private patchEventListener;
2236
- private patchRef;
2237
- private patchUpdateHandler;
2238
- private patchNormalProp;
2284
+ protected patchEventListener(key: string, prop: any): void;
2285
+ protected patchRef(prop: {
2286
+ value: Node | null;
2287
+ }): void;
2288
+ protected patchUpdateHandler(key: string, prop: any): void;
2289
+ protected patchNormalProp(key: string, prop: any): void;
2290
+ protected cleanup(): void;
2239
2291
  }
2240
2292
 
2241
2293
  /**
@@ -2270,14 +2322,12 @@ declare function isJsxElement(node: unknown): node is EssorNode;
2270
2322
  * @returns A new HTML template element.
2271
2323
  */
2272
2324
  declare function createTemplate(html: string): HTMLTemplateElement;
2273
- /**
2274
- * @param props - An object containing the `children` prop.
2275
- * @param props.children - The children to be rendered. Can be a single JSX element, string, number, boolean,
2276
- * or an array of these. Falsy values in arrays will be filtered out.
2277
- * @returns A JSX element representing the fragment, wrapping the filtered children.
2278
- */
2279
- declare function Fragment<T extends JSX.JSXElement | string | number | boolean | (JSX.JSXElement | string | number | boolean)[]>(props: {
2325
+ declare function Fragment<T extends JSX.JSXElement | string | number | boolean | Array<JSX.JSXElement | string | number | boolean>>(template: HTMLTemplateElement | '', props: {
2280
2326
  children: T;
2327
+ } | {
2328
+ [key: string]: {
2329
+ children: T;
2330
+ };
2281
2331
  }): JSX.Element;
2282
2332
 
2283
2333
  /**
@@ -2309,7 +2359,7 @@ interface InjectionKey<T> extends Symbol {
2309
2359
  * @param key - The key to store the value in the LifecycleContext with.
2310
2360
  * @param value - The value to store in the LifecycleContext with the given key.
2311
2361
  */
2312
- declare function useProvide<T, K = InjectionKey<T> | string | number>(key: K, value: K extends InjectionKey<infer V> ? V : T): void;
2362
+ declare function provide<T, K = InjectionKey<T> | string | number>(key: K, value: K extends InjectionKey<infer V> ? V : T): void;
2313
2363
  /**
2314
2364
  * Injects a value from the current component LifecycleContext.
2315
2365
  *
@@ -2322,21 +2372,7 @@ declare function useProvide<T, K = InjectionKey<T> | string | number>(key: K, va
2322
2372
  * @returns The value stored in the LifecycleContext with the given key, or the default
2323
2373
  * value if the key is not present in the LifecycleContext.
2324
2374
  */
2325
- declare function useInject<T, K = InjectionKey<T> | string | number>(key: K, defaultValue?: K extends InjectionKey<infer V> ? V : T): (K extends InjectionKey<infer V> ? V : T) | undefined;
2326
- /**
2327
- * Creates a reactive ref that can be used to reference a DOM node
2328
- * or a component instance within the component function body.
2329
- *
2330
- * @returns a reactive ref signal
2331
- *
2332
- * @example
2333
- * const inputRef = useRef<HTMLInputElement>()
2334
- *
2335
- * <input ref={inputRef} />
2336
- *
2337
- * inputRef.value // input element
2338
- */
2339
- declare function useRef<T>(): Signal$1<T | null>;
2375
+ declare function inject<T, K = InjectionKey<T> | string | number>(key: K, defaultValue?: K extends InjectionKey<infer V> ? V : T): (K extends InjectionKey<infer V> ? V : T) | undefined;
2340
2376
 
2341
2377
  declare class SSGNode extends LifecycleContext {
2342
2378
  private template;
@@ -2386,4 +2422,4 @@ declare function hydrate(component: EssorComponent, container: string | Element)
2386
2422
  */
2387
2423
  declare function ssg(component: EssorComponent, props?: Props): SSGNode | JSX.Element;
2388
2424
 
2389
- export { Fragment, type InjectionKey, h, hydrate, isComponent, isJsxElement, onDestroy, onMount, renderToString, ssg, createTemplate as template, useInject, useProvide, useRef };
2425
+ export { Fragment, type InjectionKey, h, hydrate, inject, isComponent, isJsxElement, onDestroy, onMount, provide, renderToString, ssg, createTemplate as template };
@@ -4,7 +4,7 @@ var shared = require('@estjs/shared');
4
4
  var signal = require('@estjs/signal');
5
5
 
6
6
  /**
7
- * @estjs/template v0.0.14-beta.9
7
+ * @estjs/template v0.0.14
8
8
  * (c) 2023-Present jiangxd <jiangxd2016@gmail.com>
9
9
  * @license MIT
10
10
  **/
@@ -184,10 +184,10 @@ var SSGNode = class extends LifecycleContext {
184
184
  });
185
185
  }
186
186
  renderChild(child) {
187
- if (shared.isFunction(child)) {
188
- return this.renderChild(child(this.props));
189
- } else if (signal.isSignal(child)) {
187
+ if (signal.isSignal(child)) {
190
188
  return `<!--${1 /* TEXT_COMPONENT */}-${componentIndex}-->${child.value}<!$>`;
189
+ } else if (shared.isFunction(child)) {
190
+ return this.renderChild(child(this.props));
191
191
  } else if (isSSGNode(child)) {
192
192
  const childResult = child.mount();
193
193
  return shared.isFunction(childResult) ? childResult(this.props) : extractSignal(childResult);
@@ -319,42 +319,6 @@ function addEventListener(node, eventName, handler) {
319
319
  node.addEventListener(eventName, handler);
320
320
  return () => node.removeEventListener(eventName, handler);
321
321
  }
322
- function closeHtmlTags(input) {
323
- const tagStack = [];
324
- const output = [];
325
- const tagPattern = /<\/?([\da-z-]+)([^>]*)>/gi;
326
- let lastIndex = 0;
327
- while (true) {
328
- const match = tagPattern.exec(input);
329
- if (!match) break;
330
- const [fullMatch, tagName] = match;
331
- const isEndTag = fullMatch[1] === "/";
332
- output.push(input.slice(lastIndex, match.index));
333
- lastIndex = match.index + fullMatch.length;
334
- if (isEndTag) {
335
- while (tagStack.length > 0 && tagStack[tagStack.length - 1] !== tagName) {
336
- const unclosedTag = tagStack.pop();
337
- if (unclosedTag) {
338
- output.push(`</${unclosedTag}>`);
339
- }
340
- }
341
- if (tagStack.length > 0) {
342
- tagStack.pop();
343
- }
344
- } else if (!SELF_CLOSING_TAGS.includes(tagName)) {
345
- tagStack.push(tagName);
346
- }
347
- output.push(fullMatch);
348
- }
349
- output.push(input.slice(lastIndex));
350
- while (tagStack.length > 0) {
351
- const unclosedTag = tagStack.pop();
352
- if (unclosedTag) {
353
- output.push(`</${unclosedTag}>`);
354
- }
355
- }
356
- return output.join("");
357
- }
358
322
  function convertToHtmlTag(tagName) {
359
323
  return SELF_CLOSING_TAGS.includes(tagName) ? `<${tagName}/>` : `<${tagName}></${tagName}>`;
360
324
  }
@@ -374,15 +338,11 @@ var ComponentNode = class extends LifecycleContext {
374
338
  this.emitter = /* @__PURE__ */ new Set();
375
339
  this.rootNode = null;
376
340
  this.trackMap = /* @__PURE__ */ new Map();
341
+ this.nodes = [];
342
+ this.parent = null;
343
+ this.before = null;
377
344
  this.key || (this.key = props && props.key);
378
- this.proxyProps = this.createProxyProps(props);
379
- }
380
- createProxyProps(props) {
381
- if (!props) return {};
382
- return signal.signalObject(
383
- props,
384
- (key) => shared.startsWith(key, EVENT_PREFIX) || shared.startsWith(key, UPDATE_PREFIX) || key === CHILDREN_PROP
385
- );
345
+ this.proxyProps || (this.proxyProps = signal.shallowReactive(props || {}));
386
346
  }
387
347
  get firstChild() {
388
348
  var _a, _b;
@@ -394,6 +354,7 @@ var ComponentNode = class extends LifecycleContext {
394
354
  }
395
355
  mount(parent, before) {
396
356
  var _a, _b, _c, _d;
357
+ this.parent = parent;
397
358
  if (!shared.isFunction(this.template)) {
398
359
  throw new Error("Template must be a function");
399
360
  }
@@ -402,20 +363,30 @@ var ComponentNode = class extends LifecycleContext {
402
363
  }
403
364
  this.initRef();
404
365
  this.rootNode = this.template(signal.useReactive(this.proxyProps, [CHILDREN_PROP]));
405
- const mountedNode = (_d = (_c = this.rootNode) == null ? void 0 : _c.mount(parent, before)) != null ? _d : [];
366
+ this.nodes = (_d = (_c = this.rootNode) == null ? void 0 : _c.mount(parent, before)) != null ? _d : [];
406
367
  this.callMountHooks();
407
368
  this.patchProps(this.props);
408
369
  this.removeRef();
409
- return mountedNode;
370
+ return this.nodes;
410
371
  }
411
372
  unmount() {
412
373
  var _a;
413
- this.callDestroyHooks();
414
- this.clearHooks();
374
+ this.callLifecycleHooks("destroy");
375
+ this.cleanup();
415
376
  (_a = this.rootNode) == null ? void 0 : _a.unmount();
377
+ this.resetState();
378
+ if (this.key) {
379
+ componentCache.delete(this.key);
380
+ }
381
+ }
382
+ resetState() {
416
383
  this.rootNode = null;
417
384
  this.proxyProps = {};
418
- this.clearEmitter();
385
+ this.nodes = [];
386
+ this.parent = null;
387
+ }
388
+ callLifecycleHooks(type) {
389
+ this.hooks[type].forEach((handler) => handler());
419
390
  }
420
391
  callMountHooks() {
421
392
  this.hooks.mounted.forEach((handler) => handler());
@@ -434,9 +405,11 @@ var ComponentNode = class extends LifecycleContext {
434
405
  this.rootNode = node.rootNode;
435
406
  this.trackMap = node.trackMap;
436
407
  this.hooks = node.hooks;
437
- const props = this.props;
438
- this.props = node.props;
439
- this.patchProps(props);
408
+ if (shared.hasChanged(node.props, this.props)) {
409
+ const props = this.props;
410
+ this.props = node.props;
411
+ this.patchProps(props);
412
+ }
440
413
  }
441
414
  getNodeTrack(trackKey) {
442
415
  let track = this.trackMap.get(trackKey);
@@ -476,16 +449,23 @@ var ComponentNode = class extends LifecycleContext {
476
449
  prop.value = (_b = (_a = this.rootNode) == null ? void 0 : _a.firstChild) != null ? _b : null;
477
450
  }
478
451
  patchUpdateHandler(key, prop) {
479
- this.props[key] = extractSignal(prop);
452
+ this.proxyProps[key] = extractSignal(prop);
480
453
  }
481
454
  patchNormalProp(key, prop) {
482
- var _a, _b;
483
- const newValue = (_b = (_a = this.proxyProps)[key]) != null ? _b : _a[key] = signal.useSignal(prop);
484
455
  const track = this.getNodeTrack(key);
485
456
  track.cleanup = signal.useEffect(() => {
486
- newValue.value = shared.isFunction(prop) ? prop() : prop;
457
+ this.proxyProps[key] = shared.isFunction(prop) ? prop() : prop;
487
458
  });
488
459
  }
460
+ cleanup() {
461
+ this.trackMap.forEach((track) => {
462
+ var _a;
463
+ return (_a = track.cleanup) == null ? void 0 : _a.call(track);
464
+ });
465
+ this.trackMap.clear();
466
+ this.emitter.forEach((cleanup) => cleanup());
467
+ this.emitter.clear();
468
+ }
489
469
  };
490
470
 
491
471
  // src/patch.ts
@@ -601,14 +581,12 @@ var TemplateNode = class {
601
581
  this.template = template;
602
582
  this.props = props;
603
583
  this.key = key;
604
- // Private properties for managing the node's state
605
584
  this.treeMap = /* @__PURE__ */ new Map();
606
585
  this.mounted = false;
607
586
  this.nodes = [];
608
587
  this.trackMap = /* @__PURE__ */ new Map();
609
588
  this.bindValueKeys = [];
610
589
  this.parent = null;
611
- this.key || (this.key = props && props.key);
612
590
  if (renderContext.isSSR) {
613
591
  this.componentIndex = getComponentIndex(this.template);
614
592
  }
@@ -654,39 +632,16 @@ var TemplateNode = class {
654
632
  return this.nodes;
655
633
  }
656
634
  unmount() {
657
- var _a, _b;
658
635
  this.trackMap.forEach((track) => {
659
636
  track.cleanup && track.cleanup();
660
637
  });
661
638
  this.trackMap.clear();
662
639
  this.treeMap.clear();
663
640
  this.nodes.forEach((node) => removeChild(node));
664
- if (!this.template.innerHTML && !this.nodes.length) {
665
- const children = (_b = (_a = this.props) == null ? void 0 : _a[FRAGMENT_PROP_KEY]) == null ? void 0 : _b.children;
666
- if (children) {
667
- if (shared.isArray(children)) {
668
- children.forEach((child) => {
669
- this.deleteFragmentTextNode(child);
670
- });
671
- } else {
672
- this.deleteFragmentTextNode(children);
673
- }
674
- }
675
- }
676
641
  this.nodes = [];
677
642
  this.mounted = false;
678
- }
679
- deleteFragmentTextNode(child) {
680
- if (shared.isPrimitive(child)) {
681
- if (this.parent && this.parent.childNodes.length) {
682
- this.parent.childNodes.forEach((node) => {
683
- if (node.nodeType === Node.TEXT_NODE && node.textContent === `${child}`) {
684
- this.parent.removeChild(node);
685
- }
686
- });
687
- }
688
- } else {
689
- removeChild(child);
643
+ if (this.key) {
644
+ componentCache.delete(this.key);
690
645
  }
691
646
  }
692
647
  inheritNode(node) {
@@ -702,7 +657,7 @@ var TemplateNode = class {
702
657
  this.treeMap.set(0, parent);
703
658
  this.walkNodeTree(parent, this.handleSSGNode.bind(this));
704
659
  }
705
- // Private method to map node tree
660
+ // protected method to map node tree
706
661
  mapNodeTree(parent, tree) {
707
662
  let index = 1;
708
663
  this.treeMap.set(0, parent);
@@ -795,14 +750,9 @@ var TemplateNode = class {
795
750
  }
796
751
  patchAttribute(key, element, attr, value, updateFn) {
797
752
  const track = this.getNodeTrack(`${key}:${attr}`);
798
- const val = shared.isFunction(value) ? value() : value;
799
- const triggerValue = signal.isSignal(val) ? val : signal.shallowSignal(val);
800
- setAttribute(element, attr, triggerValue.value);
753
+ const triggerValue = signal.shallowSignal();
801
754
  const cleanup = signal.useEffect(() => {
802
- const val2 = shared.isFunction(value) ? value() : value;
803
- if (shared.isPlainObject(val2) && shared.isPlainObject(triggerValue.peek()) && JSON.stringify(triggerValue.value) === JSON.stringify(val2))
804
- return;
805
- triggerValue.value = signal.isSignal(val2) ? val2.value : val2;
755
+ triggerValue.value = signal.isSignal(value) || signal.isComputed(value) ? value.value : value;
806
756
  setAttribute(element, attr, triggerValue.value);
807
757
  });
808
758
  let cleanupBind;
@@ -881,17 +831,36 @@ var TemplateNode = class {
881
831
  };
882
832
 
883
833
  // src/jsxRenderer.ts
884
- function h(template, props, key) {
834
+ var componentCache = /* @__PURE__ */ new Map();
835
+ function createNodeCache(NodeConstructor, template, props = {}, key) {
836
+ if (key) {
837
+ const cached = componentCache.get(key);
838
+ if (cached) {
839
+ return cached;
840
+ }
841
+ }
842
+ if (typeof template === "string") {
843
+ template = createTemplate(template);
844
+ }
845
+ const newNode = new NodeConstructor(template, props, key);
846
+ if (key && newNode instanceof ComponentNode) {
847
+ componentCache.set(key, newNode);
848
+ }
849
+ return newNode;
850
+ }
851
+ function h(template, props = {}, key) {
852
+ if (template === EMPTY_TEMPLATE) {
853
+ return Fragment(template, props);
854
+ }
885
855
  if (shared.isString(template)) {
886
- const isEmptyTemplate = template === EMPTY_TEMPLATE;
887
- const htmlTemplate = isEmptyTemplate ? EMPTY_TEMPLATE : convertToHtmlTag(template);
888
- props = { [isEmptyTemplate ? FRAGMENT_PROP_KEY : SINGLE_PROP_KEY]: props };
889
- return new TemplateNode(createTemplate(htmlTemplate), props, key);
856
+ const htmlTemplate = convertToHtmlTag(template);
857
+ const wrappedProps = { [SINGLE_PROP_KEY]: props };
858
+ return createNodeCache(TemplateNode, htmlTemplate, wrappedProps, key);
890
859
  }
891
860
  if (shared.isFunction(template)) {
892
- return new ComponentNode(template, props, key);
861
+ return createNodeCache(ComponentNode, template, props, key);
893
862
  }
894
- return new TemplateNode(template, props, key);
863
+ return createNodeCache(TemplateNode, template, props, key);
895
864
  }
896
865
  function isComponent(node) {
897
866
  return node instanceof ComponentNode;
@@ -901,15 +870,17 @@ function isJsxElement(node) {
901
870
  }
902
871
  function createTemplate(html) {
903
872
  const template = document.createElement("template");
904
- template.innerHTML = closeHtmlTags(html);
873
+ template.innerHTML = html;
905
874
  return template;
906
875
  }
907
- function Fragment(props) {
908
- return h(createTemplate(EMPTY_TEMPLATE), {
876
+ function Fragment(template, props) {
877
+ const processedProps = props.children ? {
909
878
  [FRAGMENT_PROP_KEY]: {
910
- children: shared.isArray(props.children) ? props.children.filter(Boolean) : [props.children]
879
+ children: Array.isArray(props.children) ? props.children.filter(Boolean) : [props.children]
911
880
  }
912
- });
881
+ } : props;
882
+ const templateElement = template === EMPTY_TEMPLATE ? createTemplate(EMPTY_TEMPLATE) : template;
883
+ return createNodeCache(TemplateNode, templateElement, processedProps);
913
884
  }
914
885
  function onMount(cb) {
915
886
  assertInsideComponent("onMounted");
@@ -927,18 +898,15 @@ function assertInsideComponent(hookName, key) {
927
898
  );
928
899
  }
929
900
  }
930
- function useProvide(key, value) {
931
- assertInsideComponent("useProvide", key);
901
+ function provide(key, value) {
902
+ assertInsideComponent("provide", key);
932
903
  LifecycleContext.ref && LifecycleContext.ref.setContext(key, value);
933
904
  }
934
- function useInject(key, defaultValue) {
905
+ function inject(key, defaultValue) {
935
906
  var _a;
936
- assertInsideComponent("useInject", key);
907
+ assertInsideComponent("inject", key);
937
908
  return (_a = LifecycleContext.ref && LifecycleContext.ref.getContext(key)) != null ? _a : defaultValue;
938
909
  }
939
- function useRef() {
940
- return signal.shallowSignal(null);
941
- }
942
910
 
943
911
  // src/server.ts
944
912
  function renderToString(component, props) {
@@ -967,13 +935,12 @@ function ssg(component, props) {
967
935
  exports.Fragment = Fragment;
968
936
  exports.h = h;
969
937
  exports.hydrate = hydrate;
938
+ exports.inject = inject;
970
939
  exports.isComponent = isComponent;
971
940
  exports.isJsxElement = isJsxElement;
972
941
  exports.onDestroy = onDestroy;
973
942
  exports.onMount = onMount;
943
+ exports.provide = provide;
974
944
  exports.renderToString = renderToString;
975
945
  exports.ssg = ssg;
976
946
  exports.template = createTemplate;
977
- exports.useInject = useInject;
978
- exports.useProvide = useProvide;
979
- exports.useRef = useRef;