wrec 0.25.1 → 0.25.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.
@@ -62,6 +62,7 @@ export declare abstract class Wrec extends HTMLElementBase implements ChangeList
62
62
  static getPropName(attrName: string): string;
63
63
  static get observedAttributes(): string[];
64
64
  propertyChangedCallback(_propName: string, _oldValue: unknown, _newValue: unknown): void;
65
+ ready(): void;
65
66
  setAttributeSafe(name: string, value: string): void;
66
67
  setFormValue(propName: string, value: string): void;
67
68
  static ssr(properties?: StringToAny): string;
package/dist/wrec.d.ts CHANGED
@@ -62,6 +62,7 @@ export declare abstract class Wrec extends HTMLElementBase implements ChangeList
62
62
  static getPropName(attrName: string): string;
63
63
  static get observedAttributes(): string[];
64
64
  propertyChangedCallback(_propName: string, _oldValue: unknown, _newValue: unknown): void;
65
+ ready(): void;
65
66
  setAttributeSafe(name: string, value: string): void;
66
67
  setFormValue(propName: string, value: string): void;
67
68
  static ssr(properties?: StringToAny): string;
package/dist/wrec.es.js CHANGED
@@ -413,7 +413,7 @@ const w = class w extends Ct {
413
413
  this[e] = s;
414
414
  }
415
415
  async connectedCallback() {
416
- a(this, i, Yt).call(this), a(this, i, _t).call(this), await a(this, i, Bt).call(this), this.hasAttribute("disabled") && a(this, i, ht).call(this), a(this, i, wt).call(this, this.shadowRoot), a(this, i, pt).call(this, this.shadowRoot), a(this, i, Zt).call(this), a(this, i, Dt).call(this);
416
+ a(this, i, Yt).call(this), a(this, i, _t).call(this), await a(this, i, Bt).call(this), this.hasAttribute("disabled") && a(this, i, ht).call(this), a(this, i, wt).call(this, this.shadowRoot), a(this, i, pt).call(this, this.shadowRoot), a(this, i, Zt).call(this), a(this, i, Dt).call(this), this.ready();
417
417
  }
418
418
  disconnectedCallback() {
419
419
  for (const { state: t } of h(this, P).values())
@@ -486,6 +486,10 @@ const w = class w extends Ct {
486
486
  // Subclasses can override this to add functionality.
487
487
  propertyChangedCallback(t, e, s) {
488
488
  }
489
+ // Subclasses can override this to be notified when
490
+ // the component DOM has been built and made reactive.
491
+ ready() {
492
+ }
489
493
  // This follows the best practice
490
494
  // "Do not override author-set, global attributes."
491
495
  setAttributeSafe(t, e) {
@@ -739,7 +743,7 @@ J = function(t, e, s = void 0) {
739
743
  s ? rt(e, s, l) : a(this, i, gt).call(this, e, l);
740
744
  }, Jt = function(t, e) {
741
745
  const s = e?.trim() ?? "", o = h(this, m).properties[s];
742
- o || a(this, i, O).call(this, t, "ref", s), o.type !== Ct && a(this, i, g).call(this, t, "ref", `refers to property "${s}" whose type is not HTMLElement`), this[s] = t;
746
+ o || a(this, i, O).call(this, t, "ref", s), o.type !== Ct && a(this, i, g).call(this, t, "ref", `refers to property "${s}" whose type is not HTMLElement`), this[s] && a(this, i, g).call(this, t, "ref", `is a duplicate reference to the property "${s}"`), this[s] = t;
743
747
  }, g = function(t, e, s) {
744
748
  const o = t instanceof HTMLElement ? t.localName : "CSS rule";
745
749
  throw new v(
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "wrec",
3
3
  "description": "a small library that greatly simplifies building web components",
4
4
  "author": "R. Mark Volkmann",
5
- "version": "0.25.1",
5
+ "version": "0.25.2",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
package/scripts/lint.js CHANGED
@@ -21,6 +21,7 @@
21
21
  // - invalid `useState` map entries
22
22
  // - unsupported HTML attributes in templates
23
23
  // - invalid HTML element nesting in templates
24
+ // - duplicate ref attribute values
24
25
 
25
26
  import fs from 'node:fs';
26
27
  import path from 'node:path';
@@ -617,7 +618,8 @@ function extractProperties(sourceFile, checker, classNode) {
617
618
  function extractTemplateExpressions(
618
619
  classNode,
619
620
  findings,
620
- componentPropertyMaps
621
+ componentPropertyMaps,
622
+ supportedProps
621
623
  ) {
622
624
  const expressions = [];
623
625
 
@@ -676,7 +678,14 @@ function extractTemplateExpressions(
676
678
  }
677
679
 
678
680
  const root = parse(rendered, {comment: true});
679
- walkHtmlNode(root, expressions, findings, componentPropertyMaps);
681
+ walkHtmlNode(
682
+ root,
683
+ expressions,
684
+ findings,
685
+ componentPropertyMaps,
686
+ supportedProps,
687
+ new Set()
688
+ );
680
689
  }
681
690
 
682
691
  return expressions;
@@ -731,6 +740,7 @@ function formatReport(
731
740
  findings.reservedProperties.length > 0 ||
732
741
  findings.invalidUsedByReferences.length > 0 ||
733
742
  findings.invalidComputedProperties.length > 0 ||
743
+ findings.invalidRefAttributes.length > 0 ||
734
744
  findings.invalidValuesConfigurations.length > 0 ||
735
745
  findings.invalidDefaultValues.length > 0 ||
736
746
  findings.invalidFormAssocValues.length > 0 ||
@@ -798,6 +808,13 @@ function formatReport(
798
808
  );
799
809
  }
800
810
 
811
+ if (findings.invalidRefAttributes.length > 0) {
812
+ lines.push('invalid ref attributes:');
813
+ findings.invalidRefAttributes.forEach(message =>
814
+ lines.push(` ${message}`)
815
+ );
816
+ }
817
+
801
818
  if (findings.invalidValuesConfigurations.length > 0) {
802
819
  lines.push('invalid values configurations:');
803
820
  findings.invalidValuesConfigurations.forEach(message =>
@@ -1228,6 +1245,7 @@ export function lintSource(filePath, sourceText, options = {}) {
1228
1245
  invalidEventHandlers: [],
1229
1246
  invalidFormAssocValues: [],
1230
1247
  invalidHtmlNesting: [],
1248
+ invalidRefAttributes: [],
1231
1249
  invalidUseStateMaps: [],
1232
1250
  invalidUsedByReferences: [],
1233
1251
  invalidValuesConfigurations: [],
@@ -1244,7 +1262,8 @@ export function lintSource(filePath, sourceText, options = {}) {
1244
1262
  const templateExprs = extractTemplateExpressions(
1245
1263
  classNode,
1246
1264
  findings,
1247
- componentPropertyMaps
1265
+ componentPropertyMaps,
1266
+ supportedProps
1248
1267
  );
1249
1268
  const allExpressions = [...templateExprs, ...computedExprs];
1250
1269
 
@@ -1327,6 +1346,7 @@ export function lintSource(filePath, sourceText, options = {}) {
1327
1346
  findings.invalidEventHandlers.sort();
1328
1347
  findings.invalidFormAssocValues.sort();
1329
1348
  findings.invalidHtmlNesting.sort();
1349
+ findings.invalidRefAttributes.sort();
1330
1350
  findings.invalidUseStateMaps.sort();
1331
1351
  findings.invalidUsedByReferences.sort();
1332
1352
  findings.invalidValuesConfigurations.sort();
@@ -1670,6 +1690,7 @@ function validateHtmlAttribute(node, attrName, findings) {
1670
1690
  if (attrName.startsWith('aria-') || attrName.startsWith('data-')) return;
1671
1691
  if (attrName.startsWith('on')) return;
1672
1692
  if (attrName === 'form-assoc') return;
1693
+ if (attrName === 'ref') return;
1673
1694
 
1674
1695
  const [baseAttrName] = attrName.split(':');
1675
1696
  if (HTML_GLOBAL_ATTRIBUTES.has(baseAttrName)) return;
@@ -1697,6 +1718,42 @@ function validateValueBindingEvent(node, attrName, findings) {
1697
1718
  );
1698
1719
  }
1699
1720
 
1721
+ function validateRefAttribute(
1722
+ attrValue,
1723
+ supportedProps,
1724
+ findings,
1725
+ seenRefProps
1726
+ ) {
1727
+ if (!attrValue) return;
1728
+
1729
+ const propName = attrValue.trim();
1730
+ if (!propName) return;
1731
+
1732
+ const propInfo = supportedProps.get(propName);
1733
+ if (!propInfo) {
1734
+ findings.invalidRefAttributes.push(
1735
+ `ref="${attrValue}" refers to missing property "${propName}"`
1736
+ );
1737
+ return;
1738
+ }
1739
+
1740
+ if (propInfo.typeText !== 'HTMLElement') {
1741
+ findings.invalidRefAttributes.push(
1742
+ `ref="${attrValue}" refers to property "${propName}" whose type is not HTMLElement`
1743
+ );
1744
+ return;
1745
+ }
1746
+
1747
+ if (seenRefProps.has(propName)) {
1748
+ findings.invalidRefAttributes.push(
1749
+ `ref="${attrValue}" is a duplicate reference to the property "${propName}"`
1750
+ );
1751
+ return;
1752
+ }
1753
+
1754
+ seenRefProps.add(propName);
1755
+ }
1756
+
1700
1757
  function getHtmlTagName(node) {
1701
1758
  const tagName = node.rawTagName || node.tagName;
1702
1759
  return typeof tagName === 'string' ? tagName.toLowerCase() : '';
@@ -1737,7 +1794,14 @@ function validateHtmlNesting(node, findings) {
1737
1794
  }
1738
1795
  }
1739
1796
 
1740
- function walkHtmlNode(node, expressions, findings, componentPropertyMaps) {
1797
+ function walkHtmlNode(
1798
+ node,
1799
+ expressions,
1800
+ findings,
1801
+ componentPropertyMaps,
1802
+ supportedProps,
1803
+ seenRefProps
1804
+ ) {
1741
1805
  if (node.nodeType === 1) {
1742
1806
  validateHtmlNesting(node, findings);
1743
1807
 
@@ -1753,6 +1817,9 @@ function walkHtmlNode(node, expressions, findings, componentPropertyMaps) {
1753
1817
  );
1754
1818
  validateHtmlAttribute(node, attrName, findings);
1755
1819
  validateValueBindingEvent(node, attrName, findings);
1820
+ if (attrName === 'ref') {
1821
+ validateRefAttribute(attrValue, supportedProps, findings, seenRefProps);
1822
+ }
1756
1823
  if (
1757
1824
  REFS_TEST_RE.test(attrValue) ||
1758
1825
  (attrName.startsWith('on') && IDENTIFIER_RE.test(attrValue))
@@ -1785,7 +1852,14 @@ function walkHtmlNode(node, expressions, findings, componentPropertyMaps) {
1785
1852
  }
1786
1853
 
1787
1854
  for (const child of node.childNodes ?? []) {
1788
- walkHtmlNode(child, expressions, findings, componentPropertyMaps);
1855
+ walkHtmlNode(
1856
+ child,
1857
+ expressions,
1858
+ findings,
1859
+ componentPropertyMaps,
1860
+ supportedProps,
1861
+ seenRefProps
1862
+ );
1789
1863
  }
1790
1864
  }
1791
1865