@estjs/template 0.0.14-beta.6 → 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,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.6
7
+ * @estjs/template v0.0.14
8
8
  * (c) 2023-Present jiangxd <jiangxd2016@gmail.com>
9
9
  * @license MIT
10
10
  **/
@@ -17,6 +17,7 @@ var CHILDREN_PROP = "children";
17
17
  var EMPTY_TEMPLATE = "";
18
18
  var FRAGMENT_PROP_KEY = "0";
19
19
  var SINGLE_PROP_KEY = "1";
20
+ var REF_KEY = "ref";
20
21
  var PLACEHOLDER = " __PLACEHOLDER__ ";
21
22
  var RenderContext = class {
22
23
  constructor() {
@@ -163,7 +164,7 @@ var SSGNode = class extends LifecycleContext {
163
164
  }
164
165
  normalizeProps(props) {
165
166
  Object.entries(props).forEach(([key, value]) => {
166
- if (key === "children") {
167
+ if (key === CHILDREN_PROP) {
167
168
  delete props[key];
168
169
  } else if (shared.isFunction(value)) {
169
170
  delete props[key];
@@ -173,7 +174,7 @@ var SSGNode = class extends LifecycleContext {
173
174
  });
174
175
  }
175
176
  generateAttributes(props) {
176
- return Object.entries(props).filter(([key, value]) => key !== "children" && !shared.isFunction(value)).map(([key, value]) => `${key}="${shared.escape(String(value))}"`).join(" ");
177
+ return Object.entries(props).filter(([key, value]) => key !== CHILDREN_PROP && !shared.isFunction(value)).map(([key, value]) => `${key}="${shared.escape(String(value))}"`).join(" ");
177
178
  }
178
179
  renderChildren(children, findIndex) {
179
180
  children.forEach(([child]) => {
@@ -183,10 +184,10 @@ var SSGNode = class extends LifecycleContext {
183
184
  });
184
185
  }
185
186
  renderChild(child) {
186
- if (shared.isFunction(child)) {
187
- return this.renderChild(child(this.props));
188
- } else if (signal.isSignal(child)) {
187
+ if (signal.isSignal(child)) {
189
188
  return `<!--${1 /* TEXT_COMPONENT */}-${componentIndex}-->${child.value}<!$>`;
189
+ } else if (shared.isFunction(child)) {
190
+ return this.renderChild(child(this.props));
190
191
  } else if (isSSGNode(child)) {
191
192
  const childResult = child.mount();
192
193
  return shared.isFunction(childResult) ? childResult(this.props) : extractSignal(childResult);
@@ -198,9 +199,6 @@ var SSGNode = class extends LifecycleContext {
198
199
 
199
200
  // src/utils.ts
200
201
  var SELF_CLOSING_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr".split(",");
201
- var HTML_TAGS = "a,abbr,acronym,address,applet,area,article,aside,audio,b,base,basefont,bdi,bdo,bgsound,big,blink,blockquote,body,br,button,canvas,caption,center,cite,code,col,colgroup,command,content,data,datalist,dd,del,details,dfn,dialog,dir,div,dl,dt,em,embed,fieldset,figcaption,figure,font,footer,form,frame,frameset,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,i,iframe,image,img,input,ins,kbd,keygen,label,legend,li,link,listing,main,map,mark,marquee,menu,menuitem,meta,meter,nav,nobr,noframes,noscript,object,ol,optgroup,option,output,p,param,picture,plaintext,pre,progress,q,rb,rp,rt,rtc,ruby,s,samp,script,section,select,shadow,small,source,spacer,span,strike,strong,style,sub,summary,sup,table,tbody,td,template,textarea,tfoot,th,thead,time,title,tr,track,tt,u,ul,var,video,wbr,xmp".split(
202
- ","
203
- );
204
202
  function coerceNode(data) {
205
203
  if (isJsxElement(data) || data instanceof Node || isSSGNode(data)) {
206
204
  return data;
@@ -317,50 +315,10 @@ function bindNode(node, setter) {
317
315
  });
318
316
  }
319
317
  }
320
- Promise.resolve();
321
318
  function addEventListener(node, eventName, handler) {
322
319
  node.addEventListener(eventName, handler);
323
320
  return () => node.removeEventListener(eventName, handler);
324
321
  }
325
- function closeHtmlTags(input) {
326
- const tagStack = [];
327
- const output = [];
328
- const tagPattern = /<\/?([\da-z-]+)([^>]*)>/gi;
329
- let lastIndex = 0;
330
- while (true) {
331
- const match = tagPattern.exec(input);
332
- if (!match) break;
333
- const [fullMatch, tagName] = match;
334
- const isEndTag = fullMatch[1] === "/";
335
- output.push(input.slice(lastIndex, match.index));
336
- lastIndex = match.index + fullMatch.length;
337
- if (isEndTag) {
338
- while (tagStack.length > 0 && tagStack[tagStack.length - 1] !== tagName) {
339
- const unclosedTag = tagStack.pop();
340
- if (unclosedTag) {
341
- output.push(`</${unclosedTag}>`);
342
- }
343
- }
344
- if (tagStack.length > 0) {
345
- tagStack.pop();
346
- }
347
- } else if (!SELF_CLOSING_TAGS.includes(tagName)) {
348
- tagStack.push(tagName);
349
- }
350
- output.push(fullMatch);
351
- }
352
- output.push(input.slice(lastIndex));
353
- while (tagStack.length > 0) {
354
- const unclosedTag = tagStack.pop();
355
- if (unclosedTag) {
356
- output.push(`</${unclosedTag}>`);
357
- }
358
- }
359
- return output.join("");
360
- }
361
- function isHtmlTagName(tagName) {
362
- return HTML_TAGS.includes(tagName);
363
- }
364
322
  function convertToHtmlTag(tagName) {
365
323
  return SELF_CLOSING_TAGS.includes(tagName) ? `<${tagName}/>` : `<${tagName}></${tagName}>`;
366
324
  }
@@ -380,15 +338,11 @@ var ComponentNode = class extends LifecycleContext {
380
338
  this.emitter = /* @__PURE__ */ new Set();
381
339
  this.rootNode = null;
382
340
  this.trackMap = /* @__PURE__ */ new Map();
383
- this.key || (this.key = props == null ? void 0 : props.key);
384
- this.proxyProps = this.createProxyProps(props);
385
- }
386
- createProxyProps(props) {
387
- if (!props) return {};
388
- return signal.signalObject(
389
- props,
390
- (key) => shared.startsWith(key, EVENT_PREFIX) || shared.startsWith(key, UPDATE_PREFIX) || key === CHILDREN_PROP
391
- );
341
+ this.nodes = [];
342
+ this.parent = null;
343
+ this.before = null;
344
+ this.key || (this.key = props && props.key);
345
+ this.proxyProps || (this.proxyProps = signal.shallowReactive(props || {}));
392
346
  }
393
347
  get firstChild() {
394
348
  var _a, _b;
@@ -400,6 +354,7 @@ var ComponentNode = class extends LifecycleContext {
400
354
  }
401
355
  mount(parent, before) {
402
356
  var _a, _b, _c, _d;
357
+ this.parent = parent;
403
358
  if (!shared.isFunction(this.template)) {
404
359
  throw new Error("Template must be a function");
405
360
  }
@@ -407,21 +362,31 @@ var ComponentNode = class extends LifecycleContext {
407
362
  return (_b = (_a = this.rootNode) == null ? void 0 : _a.mount(parent, before)) != null ? _b : [];
408
363
  }
409
364
  this.initRef();
410
- this.rootNode = this.template(signal.useReactive(this.proxyProps, ["children"]));
411
- const mountedNode = (_d = (_c = this.rootNode) == null ? void 0 : _c.mount(parent, before)) != null ? _d : [];
365
+ this.rootNode = this.template(signal.useReactive(this.proxyProps, [CHILDREN_PROP]));
366
+ this.nodes = (_d = (_c = this.rootNode) == null ? void 0 : _c.mount(parent, before)) != null ? _d : [];
412
367
  this.callMountHooks();
413
368
  this.patchProps(this.props);
414
369
  this.removeRef();
415
- return mountedNode;
370
+ return this.nodes;
416
371
  }
417
372
  unmount() {
418
373
  var _a;
419
- this.callDestroyHooks();
420
- this.clearHooks();
374
+ this.callLifecycleHooks("destroy");
375
+ this.cleanup();
421
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() {
422
383
  this.rootNode = null;
423
384
  this.proxyProps = {};
424
- this.clearEmitter();
385
+ this.nodes = [];
386
+ this.parent = null;
387
+ }
388
+ callLifecycleHooks(type) {
389
+ this.hooks[type].forEach((handler) => handler());
425
390
  }
426
391
  callMountHooks() {
427
392
  this.hooks.mounted.forEach((handler) => handler());
@@ -440,9 +405,11 @@ var ComponentNode = class extends LifecycleContext {
440
405
  this.rootNode = node.rootNode;
441
406
  this.trackMap = node.trackMap;
442
407
  this.hooks = node.hooks;
443
- const props = this.props;
444
- this.props = node.props;
445
- 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
+ }
446
413
  }
447
414
  getNodeTrack(trackKey) {
448
415
  let track = this.trackMap.get(trackKey);
@@ -462,7 +429,7 @@ var ComponentNode = class extends LifecycleContext {
462
429
  for (const [key, prop] of Object.entries(props)) {
463
430
  if (shared.startsWith(key, EVENT_PREFIX) && ((_a = this.rootNode) == null ? void 0 : _a.firstChild)) {
464
431
  this.patchEventListener(key, prop);
465
- } else if (key === "ref") {
432
+ } else if (key === REF_KEY) {
466
433
  this.patchRef(prop);
467
434
  } else if (shared.startsWith(key, UPDATE_PREFIX)) {
468
435
  this.patchUpdateHandler(key, prop);
@@ -482,22 +449,25 @@ var ComponentNode = class extends LifecycleContext {
482
449
  prop.value = (_b = (_a = this.rootNode) == null ? void 0 : _a.firstChild) != null ? _b : null;
483
450
  }
484
451
  patchUpdateHandler(key, prop) {
485
- this.props[key] = extractSignal(prop);
452
+ this.proxyProps[key] = extractSignal(prop);
486
453
  }
487
454
  patchNormalProp(key, prop) {
488
- var _a, _b;
489
- const newValue = (_b = (_a = this.proxyProps)[key]) != null ? _b : _a[key] = signal.useSignal(prop);
490
455
  const track = this.getNodeTrack(key);
491
456
  track.cleanup = signal.useEffect(() => {
492
- newValue.value = shared.isFunction(prop) ? prop() : prop;
457
+ this.proxyProps[key] = shared.isFunction(prop) ? prop() : prop;
493
458
  });
494
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
+ }
495
469
  };
496
470
 
497
- // ../shared/src/comm.ts
498
- var _toString = Object.prototype.toString;
499
- var isPlainObject = (val) => _toString.call(val) === "[object Object]";
500
-
501
471
  // src/patch.ts
502
472
  function patchChildren(parent, childrenMap, nextChildren, before) {
503
473
  const result = /* @__PURE__ */ new Map();
@@ -611,14 +581,12 @@ var TemplateNode = class {
611
581
  this.template = template;
612
582
  this.props = props;
613
583
  this.key = key;
614
- // Private properties for managing the node's state
615
584
  this.treeMap = /* @__PURE__ */ new Map();
616
585
  this.mounted = false;
617
586
  this.nodes = [];
618
587
  this.trackMap = /* @__PURE__ */ new Map();
619
588
  this.bindValueKeys = [];
620
589
  this.parent = null;
621
- this.key || (this.key = props == null ? void 0 : props.key);
622
590
  if (renderContext.isSSR) {
623
591
  this.componentIndex = getComponentIndex(this.template);
624
592
  }
@@ -648,7 +616,7 @@ var TemplateNode = class {
648
616
  const firstChild = cloneNode.firstChild;
649
617
  if ((_a = firstChild == null ? void 0 : firstChild.hasAttribute) == null ? void 0 : _a.call(firstChild, "_svg_")) {
650
618
  firstChild.remove();
651
- firstChild == null ? void 0 : firstChild.childNodes.forEach((node) => {
619
+ firstChild.childNodes.forEach((node) => {
652
620
  cloneNode.append(node);
653
621
  });
654
622
  }
@@ -664,40 +632,16 @@ var TemplateNode = class {
664
632
  return this.nodes;
665
633
  }
666
634
  unmount() {
667
- var _a, _b;
668
635
  this.trackMap.forEach((track) => {
669
- var _a2;
670
- (_a2 = track.cleanup) == null ? void 0 : _a2.call(track);
636
+ track.cleanup && track.cleanup();
671
637
  });
672
638
  this.trackMap.clear();
673
639
  this.treeMap.clear();
674
640
  this.nodes.forEach((node) => removeChild(node));
675
- if (!this.template.innerHTML && !this.nodes.length) {
676
- const children = (_b = (_a = this.props) == null ? void 0 : _a[FRAGMENT_PROP_KEY]) == null ? void 0 : _b.children;
677
- if (children) {
678
- if (shared.isArray(children)) {
679
- children.forEach((child) => {
680
- this.deleteFragmentTextNode(child);
681
- });
682
- } else {
683
- this.deleteFragmentTextNode(children);
684
- }
685
- }
686
- }
687
641
  this.nodes = [];
688
642
  this.mounted = false;
689
- }
690
- deleteFragmentTextNode(child) {
691
- var _a;
692
- if (shared.isPrimitive(child)) {
693
- (_a = this.parent) == null ? void 0 : _a.childNodes.forEach((node) => {
694
- var _a2;
695
- if (node.nodeType === Node.TEXT_NODE && node.textContent === `${child}`) {
696
- (_a2 = this.parent) == null ? void 0 : _a2.removeChild(node);
697
- }
698
- });
699
- } else {
700
- removeChild(child);
643
+ if (this.key) {
644
+ componentCache.delete(this.key);
701
645
  }
702
646
  }
703
647
  inheritNode(node) {
@@ -713,7 +657,7 @@ var TemplateNode = class {
713
657
  this.treeMap.set(0, parent);
714
658
  this.walkNodeTree(parent, this.handleSSGNode.bind(this));
715
659
  }
716
- // Private method to map node tree
660
+ // protected method to map node tree
717
661
  mapNodeTree(parent, tree) {
718
662
  let index = 1;
719
663
  this.treeMap.set(0, parent);
@@ -765,9 +709,9 @@ var TemplateNode = class {
765
709
  Object.entries(props).forEach(([attr, value]) => {
766
710
  if (attr === CHILDREN_PROP && value) {
767
711
  this.patchChildren(key, node, value, isRoot);
768
- } else if (attr === "ref") {
712
+ } else if (attr === REF_KEY) {
769
713
  props[attr].value = node;
770
- } else if (shared.startsWith(attr, "on")) {
714
+ } else if (shared.startsWith(attr, EVENT_PREFIX)) {
771
715
  this.patchEventListener(key, node, attr, value);
772
716
  } else {
773
717
  if (this.bindValueKeys.includes(attr)) return;
@@ -777,8 +721,7 @@ var TemplateNode = class {
777
721
  });
778
722
  }
779
723
  getBindUpdateValue(props, key, attr) {
780
- const UPDATE_PREFIX2 = "update";
781
- const updateKey = `${UPDATE_PREFIX2}${shared.capitalize(attr)}`;
724
+ const updateKey = `${UPDATE_PREFIX}${shared.capitalize(attr)}`;
782
725
  if (updateKey && props[updateKey] && shared.isFunction(props[updateKey])) {
783
726
  this.bindValueKeys.push(updateKey);
784
727
  return props[updateKey];
@@ -807,14 +750,9 @@ var TemplateNode = class {
807
750
  }
808
751
  patchAttribute(key, element, attr, value, updateFn) {
809
752
  const track = this.getNodeTrack(`${key}:${attr}`);
810
- const val = shared.isFunction(value) ? value() : value;
811
- const triggerValue = signal.isSignal(val) ? val : signal.shallowSignal(val);
812
- setAttribute(element, attr, triggerValue.value);
753
+ const triggerValue = signal.shallowSignal();
813
754
  const cleanup = signal.useEffect(() => {
814
- const val2 = shared.isFunction(value) ? value() : value;
815
- if (isPlainObject(val2) && isPlainObject(triggerValue.peek()) && JSON.stringify(triggerValue.value) === JSON.stringify(val2))
816
- return;
817
- triggerValue.value = signal.isSignal(val2) ? val2.value : val2;
755
+ triggerValue.value = signal.isSignal(value) || signal.isComputed(value) ? value.value : value;
818
756
  setAttribute(element, attr, triggerValue.value);
819
757
  });
820
758
  let cleanupBind;
@@ -829,7 +767,6 @@ var TemplateNode = class {
829
767
  };
830
768
  }
831
769
  getNodeTrack(trackKey, trackLastNodes, isRoot) {
832
- var _a;
833
770
  let track = this.trackMap.get(trackKey);
834
771
  if (!track) {
835
772
  track = { cleanup: () => {
@@ -842,7 +779,7 @@ var TemplateNode = class {
842
779
  }
843
780
  this.trackMap.set(trackKey, track);
844
781
  }
845
- (_a = track.cleanup) == null ? void 0 : _a.call(track);
782
+ track.cleanup && track.cleanup();
846
783
  return track;
847
784
  }
848
785
  patchChild(track, parent, child, before) {
@@ -894,18 +831,36 @@ var TemplateNode = class {
894
831
  };
895
832
 
896
833
  // src/jsxRenderer.ts
897
- function h(template, props, key) {
898
- if (shared.isString(template)) {
899
- if (isHtmlTagName(template)) {
900
- const htmlTemplate = convertToHtmlTag(template);
901
- props = { [SINGLE_PROP_KEY]: props };
902
- return new TemplateNode(createTemplate(htmlTemplate), props, key);
903
- } else if (template === EMPTY_TEMPLATE) {
904
- props = { [FRAGMENT_PROP_KEY]: props };
905
- return new TemplateNode(createTemplate(EMPTY_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;
906
840
  }
907
841
  }
908
- return shared.isFunction(template) ? new ComponentNode(template, props, key) : new TemplateNode(template, props, key);
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
+ }
855
+ if (shared.isString(template)) {
856
+ const htmlTemplate = convertToHtmlTag(template);
857
+ const wrappedProps = { [SINGLE_PROP_KEY]: props };
858
+ return createNodeCache(TemplateNode, htmlTemplate, wrappedProps, key);
859
+ }
860
+ if (shared.isFunction(template)) {
861
+ return createNodeCache(ComponentNode, template, props, key);
862
+ }
863
+ return createNodeCache(TemplateNode, template, props, key);
909
864
  }
910
865
  function isComponent(node) {
911
866
  return node instanceof ComponentNode;
@@ -915,13 +870,17 @@ function isJsxElement(node) {
915
870
  }
916
871
  function createTemplate(html) {
917
872
  const template = document.createElement("template");
918
- template.innerHTML = closeHtmlTags(html);
873
+ template.innerHTML = html;
919
874
  return template;
920
875
  }
921
- function Fragment(props) {
922
- return h(EMPTY_TEMPLATE, {
923
- children: shared.isArray(props.children) ? props.children.filter(Boolean) : [props.children]
924
- });
876
+ function Fragment(template, props) {
877
+ const processedProps = props.children ? {
878
+ [FRAGMENT_PROP_KEY]: {
879
+ children: Array.isArray(props.children) ? props.children.filter(Boolean) : [props.children]
880
+ }
881
+ } : props;
882
+ const templateElement = template === EMPTY_TEMPLATE ? createTemplate(EMPTY_TEMPLATE) : template;
883
+ return createNodeCache(TemplateNode, templateElement, processedProps);
925
884
  }
926
885
  function onMount(cb) {
927
886
  assertInsideComponent("onMounted");
@@ -939,18 +898,15 @@ function assertInsideComponent(hookName, key) {
939
898
  );
940
899
  }
941
900
  }
942
- function useProvide(key, value) {
943
- assertInsideComponent("useProvide", key);
901
+ function provide(key, value) {
902
+ assertInsideComponent("provide", key);
944
903
  LifecycleContext.ref && LifecycleContext.ref.setContext(key, value);
945
904
  }
946
- function useInject(key, defaultValue) {
905
+ function inject(key, defaultValue) {
947
906
  var _a;
948
- assertInsideComponent("useInject", key);
907
+ assertInsideComponent("inject", key);
949
908
  return (_a = LifecycleContext.ref && LifecycleContext.ref.getContext(key)) != null ? _a : defaultValue;
950
909
  }
951
- function useRef() {
952
- return signal.shallowSignal(null);
953
- }
954
910
 
955
911
  // src/server.ts
956
912
  function renderToString(component, props) {
@@ -979,13 +935,12 @@ function ssg(component, props) {
979
935
  exports.Fragment = Fragment;
980
936
  exports.h = h;
981
937
  exports.hydrate = hydrate;
938
+ exports.inject = inject;
982
939
  exports.isComponent = isComponent;
983
940
  exports.isJsxElement = isJsxElement;
984
941
  exports.onDestroy = onDestroy;
985
942
  exports.onMount = onMount;
943
+ exports.provide = provide;
986
944
  exports.renderToString = renderToString;
987
945
  exports.ssg = ssg;
988
946
  exports.template = createTemplate;
989
- exports.useInject = useInject;
990
- exports.useProvide = useProvide;
991
- exports.useRef = useRef;