wrec 0.29.2 → 0.29.3
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.
- package/dist/{wrec-DHp2V7DA.js → wrec-DEac2MyH.js} +74 -67
- package/dist/wrec-ssr.d.ts +1 -0
- package/dist/wrec-ssr.es.js +1 -1
- package/dist/wrec.d.ts +1 -0
- package/dist/wrec.es.js +1 -1
- package/package.json +2 -2
- package/scripts/lint.js +7 -1
- package/scripts/used-by.js +282 -203
|
@@ -286,13 +286,13 @@ var W = class e extends m {
|
|
|
286
286
|
constructor() {
|
|
287
287
|
super(), this.attachShadow({ mode: "open" });
|
|
288
288
|
let e = this.#n;
|
|
289
|
-
this.#C("attrToPropMap") || (e.attrToPropMap = /* @__PURE__ */ new Map()), this.#C("computedGraph") || (e.computedGraph = null), this.#C("properties") || (e.properties = {}), this.#C("propToAttrMap") || (e.propToAttrMap = /* @__PURE__ */ new Map()), this.#C("propToComputedMap") || (e.propToComputedMap = /* @__PURE__ */ new Map()), this.#C("propToExprsMap") || (e.propToExprsMap = /* @__PURE__ */ new Map()), this.#C("registeredComputedProps") || (e.registeredComputedProps = /* @__PURE__ */ new Set());
|
|
289
|
+
this.#C("attrToPropMap") || (e.attrToPropMap = /* @__PURE__ */ new Map()), this.#C("computedGraph") || (e.computedGraph = null), this.#C("computedPropsRegistered") || (e.computedPropsRegistered = !1), this.#C("properties") || (e.properties = {}), this.#C("propToAttrMap") || (e.propToAttrMap = /* @__PURE__ */ new Map()), this.#C("propToComputedMap") || (e.propToComputedMap = /* @__PURE__ */ new Map()), this.#C("propToExprsMap") || (e.propToExprsMap = /* @__PURE__ */ new Map()), this.#C("registeredComputedProps") || (e.registeredComputedProps = /* @__PURE__ */ new Set());
|
|
290
290
|
}
|
|
291
291
|
attributeChangedCallback(t, n, r) {
|
|
292
292
|
t === "disabled" && this.#h();
|
|
293
293
|
let i = e.getPropName(t);
|
|
294
294
|
if (!this.#T(i) && this.#w(i)) {
|
|
295
|
-
let e = this.#
|
|
295
|
+
let e = this.#R(i, r);
|
|
296
296
|
this[i] = e;
|
|
297
297
|
let t = this.#i[i];
|
|
298
298
|
t && this.setFormValue(t, String(e));
|
|
@@ -308,7 +308,7 @@ var W = class e extends m {
|
|
|
308
308
|
}
|
|
309
309
|
let r = this.#x(Object.keys(e));
|
|
310
310
|
for (let [e, i] of r) {
|
|
311
|
-
this.#
|
|
311
|
+
this.#M(e, this.#v(i));
|
|
312
312
|
let r = t.get(e) ?? [];
|
|
313
313
|
for (let e of r) n.add(e);
|
|
314
314
|
}
|
|
@@ -329,36 +329,37 @@ var W = class e extends m {
|
|
|
329
329
|
this[t] = n;
|
|
330
330
|
}
|
|
331
331
|
async connectedCallback() {
|
|
332
|
-
this.#
|
|
332
|
+
this.#W(), this.#p(), await this.#d(), this.hasAttribute("disabled") && this.#h(), this.#J(this.shadowRoot), this.#E(this.shadowRoot), this.#U(), this.#f(), this.ready();
|
|
333
333
|
}
|
|
334
334
|
#f() {
|
|
335
335
|
let { properties: e } = this.#n;
|
|
336
|
-
for (let [t, { computed: n }] of Object.entries(e)) n && this.#
|
|
336
|
+
for (let [t, { computed: n }] of Object.entries(e)) n && this.#M(t, this.#v(n));
|
|
337
337
|
}
|
|
338
338
|
#p() {
|
|
339
339
|
let { observedAttributes: e, properties: t } = this.#n;
|
|
340
340
|
for (let [n, r] of Object.entries(t)) r.computed || this.#m(n, r, e);
|
|
341
341
|
for (let [n, r] of Object.entries(t)) r.computed && this.#m(n, r, e);
|
|
342
|
+
this.#A();
|
|
342
343
|
}
|
|
343
344
|
#m(t, n, r) {
|
|
344
345
|
if (t === "class" || t === "style") throw new g(`"${t}" is a reserved property`);
|
|
345
346
|
let i = e.getAttrName(t), a = this.hasAttribute(i);
|
|
346
|
-
n.required && !a && this.#
|
|
347
|
+
n.required && !a && this.#F(this, i, "is a required attribute");
|
|
347
348
|
let o = n.value;
|
|
348
349
|
this.hasOwnProperty(t) && (o = this[t], delete this[t]);
|
|
349
|
-
let { type: c } = n, l = c === Boolean ? o || a : r.includes(i) && a ? this.#
|
|
350
|
-
this[u] = l,
|
|
350
|
+
let { type: c } = n, l = c === Boolean ? o || a : r.includes(i) && a ? this.#L(t, i) : o ?? k(n), u = "#" + t;
|
|
351
|
+
this[u] = l, Object.defineProperty(this, t, {
|
|
351
352
|
enumerable: !0,
|
|
352
353
|
get() {
|
|
353
354
|
return this[u];
|
|
354
355
|
},
|
|
355
356
|
set(e) {
|
|
356
|
-
n.computed && !this.#t.has(t) && this.#
|
|
357
|
+
n.computed && !this.#t.has(t) && this.#F(null, t, "is a computed property and cannot be set directly"), c === Number && typeof e == "string" && (e = z(e));
|
|
357
358
|
let r = this[u];
|
|
358
359
|
if (e === r) return;
|
|
359
|
-
this.#
|
|
360
|
+
this.#q(t, c, e), this[u] = e;
|
|
360
361
|
let a = this.#l.get(t);
|
|
361
|
-
a && s(a.state, a.stateProp, e), this.#
|
|
362
|
+
a && s(a.state, a.stateProp, e), this.#z(t, c, e, i), this.#e || (this.#B(t), this.#k(t)), this.#H(t, e);
|
|
362
363
|
let o = this.#i[t];
|
|
363
364
|
o && this.setFormValue(o, String(e)), this.propertyChangedCallback(t, r, e), n.dispatch && this.dispatch("change", {
|
|
364
365
|
tagName: this.localName,
|
|
@@ -392,17 +393,17 @@ var W = class e extends m {
|
|
|
392
393
|
for (let r of t.getAttributeNames()) {
|
|
393
394
|
let i = t.getAttribute(r);
|
|
394
395
|
if (r === "ref") {
|
|
395
|
-
this.#
|
|
396
|
+
this.#P(t, i);
|
|
396
397
|
continue;
|
|
397
398
|
}
|
|
398
399
|
let a = this.#O(t, i);
|
|
399
400
|
if (a) {
|
|
400
401
|
let i = this[a];
|
|
401
|
-
i === void 0 && this.#
|
|
402
|
+
i === void 0 && this.#I(t, r, a);
|
|
402
403
|
let [o, s] = r.split(":"), c = e.getPropName(o), l = this.#T(a);
|
|
403
|
-
n && t.#T(c) || (t[c] = i), o === "value" && (s ? (t["on" + s] === void 0 && this.#
|
|
404
|
+
n && t.#T(c) || (t[c] = i), o === "value" && (s ? (t["on" + s] === void 0 && this.#F(t, r, "refers to an unsupported event name"), t.setAttribute(o, this[a])) : s = "change"), n && !l && t.#c.set(e.getPropName(o), a);
|
|
404
405
|
}
|
|
405
|
-
this.#
|
|
406
|
+
this.#N(i, t, r);
|
|
406
407
|
}
|
|
407
408
|
}
|
|
408
409
|
#_(e) {
|
|
@@ -414,7 +415,7 @@ var W = class e extends m {
|
|
|
414
415
|
r.add(t);
|
|
415
416
|
continue;
|
|
416
417
|
}
|
|
417
|
-
if (t instanceof HTMLElement) this.#
|
|
418
|
+
if (t instanceof HTMLElement) this.#V(t, e);
|
|
418
419
|
else if (!(t instanceof CSSStyleRule)) {
|
|
419
420
|
let { element: n, attrName: r } = t;
|
|
420
421
|
n instanceof CSSStyleRule ? n.style.setProperty(r, e) : V(n, r, e);
|
|
@@ -438,13 +439,13 @@ var W = class e extends m {
|
|
|
438
439
|
let t = Array.from(e.style);
|
|
439
440
|
for (let n of t) if (n.startsWith("--")) {
|
|
440
441
|
let t = e.style.getPropertyValue(n);
|
|
441
|
-
this.#
|
|
442
|
+
this.#N(t, e, n);
|
|
442
443
|
}
|
|
443
444
|
}
|
|
444
445
|
} else {
|
|
445
446
|
let t = "";
|
|
446
447
|
if (F(e)) {
|
|
447
|
-
this.#
|
|
448
|
+
this.#N(e.textContent, e);
|
|
448
449
|
let n = e.textContent?.match(x);
|
|
449
450
|
n && (t = n[1]);
|
|
450
451
|
} else {
|
|
@@ -453,7 +454,7 @@ var W = class e extends m {
|
|
|
453
454
|
}
|
|
454
455
|
if (t) {
|
|
455
456
|
let n = this.#O(e, t);
|
|
456
|
-
n && F(e) ? e.textContent = this[n] : this.#
|
|
457
|
+
n && F(e) ? e.textContent = this[n] : this.#N(t, e);
|
|
457
458
|
}
|
|
458
459
|
}
|
|
459
460
|
}
|
|
@@ -541,7 +542,7 @@ var W = class e extends m {
|
|
|
541
542
|
if (!C.test(r)) return;
|
|
542
543
|
let i = I(e) || F(e), [a, o] = (t ?? "").split(":");
|
|
543
544
|
if (!(i && a === "value" || F(e))) return;
|
|
544
|
-
o ? e["on" + o] === void 0 && this.#
|
|
545
|
+
o ? e["on" + o] === void 0 && this.#F(e, t, "refers to an unsupported event name") : o = "change";
|
|
545
546
|
let s = M(r);
|
|
546
547
|
e.addEventListener(o, (e) => {
|
|
547
548
|
let { target: t } = e;
|
|
@@ -566,7 +567,7 @@ var W = class e extends m {
|
|
|
566
567
|
#D() {
|
|
567
568
|
if (this.#n.formAssociated || this.closest("form") === null) return;
|
|
568
569
|
let e = this.#n.name;
|
|
569
|
-
this.#
|
|
570
|
+
this.#F(this, void 0, `inside form, class ${e} requires "static formAssociated = true;"`);
|
|
570
571
|
}
|
|
571
572
|
static get observedAttributes() {
|
|
572
573
|
let t = Object.entries(this.properties || {}).filter(([e, t]) => !t.computed).map(([t]) => e.getAttrName(t));
|
|
@@ -576,26 +577,32 @@ var W = class e extends m {
|
|
|
576
577
|
#O(e, t) {
|
|
577
578
|
if (!t || !C.test(t)) return;
|
|
578
579
|
let n = M(t);
|
|
579
|
-
return this[n] === void 0 && this.#
|
|
580
|
+
return this[n] === void 0 && this.#I(e, "", n), n;
|
|
580
581
|
}
|
|
581
582
|
#k(e) {
|
|
582
583
|
let t = this.#n.propToExprsMap.get(e) || [];
|
|
583
584
|
this.#_(t);
|
|
584
585
|
}
|
|
585
586
|
ready() {}
|
|
586
|
-
#A(
|
|
587
|
+
#A() {
|
|
588
|
+
let e = this.#n;
|
|
589
|
+
if (!e.computedPropsRegistered) {
|
|
590
|
+
e.computedPropsRegistered = !0, e.computedGraph = null;
|
|
591
|
+
for (let [t, n] of Object.entries(e.properties)) n.computed && this.#j(t, n);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
#j(e, t) {
|
|
587
595
|
let n = this.#n;
|
|
588
|
-
|
|
589
|
-
n.registeredComputedProps.add(e), n.computedGraph = null;
|
|
596
|
+
n.registeredComputedProps.add(e);
|
|
590
597
|
let r = n.propToComputedMap;
|
|
591
598
|
function i(t, n) {
|
|
592
599
|
let i = r.get(t);
|
|
593
600
|
i || (i = [], r.set(t, i)), i.push([e, n]);
|
|
594
601
|
}
|
|
595
|
-
let
|
|
596
|
-
for (let t of
|
|
597
|
-
let n = M(t);
|
|
598
|
-
this[n] === void 0 && this.#
|
|
602
|
+
let a = t.computed;
|
|
603
|
+
for (let t of a.matchAll(w)) {
|
|
604
|
+
let n = M(t[0]);
|
|
605
|
+
this[n] === void 0 && this.#I(null, e, n), typeof this[n] != "function" && i(n, a);
|
|
599
606
|
}
|
|
600
607
|
for (let t of a.matchAll(b)) {
|
|
601
608
|
let r = t[1];
|
|
@@ -603,7 +610,7 @@ var W = class e extends m {
|
|
|
603
610
|
for (let [e, t] of Object.entries(n.properties)) H(t.usedBy)?.includes(r) && i(e, a);
|
|
604
611
|
}
|
|
605
612
|
}
|
|
606
|
-
#
|
|
613
|
+
#M(e, t) {
|
|
607
614
|
this.#t.add(e);
|
|
608
615
|
try {
|
|
609
616
|
this[e] = t;
|
|
@@ -611,9 +618,9 @@ var W = class e extends m {
|
|
|
611
618
|
this.#t.delete(e);
|
|
612
619
|
}
|
|
613
620
|
}
|
|
614
|
-
#
|
|
621
|
+
#N(e, t, n = void 0) {
|
|
615
622
|
if (!e) return;
|
|
616
|
-
let r = this.#
|
|
623
|
+
let r = this.#G(t, n, e);
|
|
617
624
|
if (!r) {
|
|
618
625
|
let r = e.replaceAll("this..", "this.");
|
|
619
626
|
n ? V(t, n, r) : "textContent" in t && (t.textContent = r);
|
|
@@ -636,11 +643,11 @@ var W = class e extends m {
|
|
|
636
643
|
attrName: n
|
|
637
644
|
} : t), t instanceof HTMLElement && this.#S(t, n, r);
|
|
638
645
|
let o = this.#v(e);
|
|
639
|
-
n ? V(t, n, o) : this.#
|
|
646
|
+
n ? V(t, n, o) : this.#V(t, o);
|
|
640
647
|
}
|
|
641
|
-
#
|
|
648
|
+
#P(e, t) {
|
|
642
649
|
let n = t?.trim() ?? "", r = this.#n.properties[n];
|
|
643
|
-
r || this.#
|
|
650
|
+
r || this.#I(e, "ref", n), r.type !== m && this.#F(e, "ref", `refers to property "${n}" whose type is not HTMLElement`), this[n] && this.#F(e, "ref", `is a duplicate reference to the property "${n}"`), this[n] = e, e.removeAttribute("ref");
|
|
644
651
|
}
|
|
645
652
|
setAttributeSafe(e, t) {
|
|
646
653
|
this.hasAttribute(e) || this.setAttribute(e, t);
|
|
@@ -651,24 +658,24 @@ var W = class e extends m {
|
|
|
651
658
|
static ssr(e = {}) {
|
|
652
659
|
throw new g("SSR is not available in the browser build.");
|
|
653
660
|
}
|
|
654
|
-
#
|
|
661
|
+
#F(e, t, n) {
|
|
655
662
|
let r = e instanceof HTMLElement ? e.localName : "CSS rule";
|
|
656
663
|
throw new g(`component ${this.#n.elementName}` + (e ? `, element "${r}"` : "") + (t ? `, attribute "${t}"` : "") + ` ${n}`);
|
|
657
664
|
}
|
|
658
|
-
#
|
|
659
|
-
this.#
|
|
665
|
+
#I(e, t, n) {
|
|
666
|
+
this.#F(e, t, `refers to missing property "${n}"`);
|
|
660
667
|
}
|
|
661
|
-
#
|
|
662
|
-
return this.#
|
|
668
|
+
#L(e, t) {
|
|
669
|
+
return this.#R(e, this.getAttribute(t));
|
|
663
670
|
}
|
|
664
|
-
#
|
|
671
|
+
#R(t, n) {
|
|
665
672
|
if (n?.match(w)) return n;
|
|
666
673
|
let r = this.#n.properties[t], { type: i, values: a } = r;
|
|
667
|
-
if (i || this.#
|
|
674
|
+
if (i || this.#F(null, t, "does not specify its type"), n === null) return i === Boolean ? !1 : k(r);
|
|
668
675
|
if (i === String) {
|
|
669
676
|
if (a && !a.includes(n)) {
|
|
670
677
|
let e = a.map((e) => `"${e}"`).join(", ");
|
|
671
|
-
this.#
|
|
678
|
+
this.#F(null, t, `must be one of ${e}`);
|
|
672
679
|
}
|
|
673
680
|
return n;
|
|
674
681
|
}
|
|
@@ -677,25 +684,25 @@ var W = class e extends m {
|
|
|
677
684
|
if (n === "true") return !0;
|
|
678
685
|
if (n === "false" || n === "null") return !1;
|
|
679
686
|
let r = e.getAttrName(t);
|
|
680
|
-
return n && n !== r && this.#
|
|
687
|
+
return n && n !== r && this.#F(null, t, "is a Boolean attribute, so its value must match attribute name or be missing"), n === "" || n === r;
|
|
681
688
|
}
|
|
682
689
|
}
|
|
683
|
-
#
|
|
684
|
-
P(n) && !this.#T(e) && n !== (t === Boolean ? this.hasAttribute(r) : this.#
|
|
690
|
+
#z(e, t, n, r) {
|
|
691
|
+
P(n) && !this.#T(e) && n !== (t === Boolean ? this.hasAttribute(r) : this.#L(e, r)) && B(this, r || e, n);
|
|
685
692
|
}
|
|
686
|
-
#
|
|
687
|
-
for (let [t, n] of this.#x([e])) this.#
|
|
693
|
+
#B(e) {
|
|
694
|
+
for (let [t, n] of this.#x([e])) this.#M(t, this.#v(n));
|
|
688
695
|
}
|
|
689
|
-
#
|
|
696
|
+
#V(e, t) {
|
|
690
697
|
if (t === void 0) return;
|
|
691
698
|
let n = e instanceof HTMLElement;
|
|
692
699
|
Array.isArray(t) && (t = t.join(""));
|
|
693
700
|
let r = typeof t;
|
|
694
|
-
r !== "string" && r !== "number" && this.#
|
|
701
|
+
r !== "string" && r !== "number" && this.#F(e, void 0, " computed content is not a string or number");
|
|
695
702
|
let i = String(t);
|
|
696
|
-
e instanceof HTMLElement && F(e) ? e.value = i : n && r === "string" && i.trim().startsWith("<") ? (e.innerHTML = d(i), this.#
|
|
703
|
+
e instanceof HTMLElement && F(e) ? e.value = i : n && r === "string" && i.trim().startsWith("<") ? (e.innerHTML = d(i), this.#J(e), this.#E(e)) : n && (e.textContent = i);
|
|
697
704
|
}
|
|
698
|
-
#
|
|
705
|
+
#H(e, t) {
|
|
699
706
|
let n = this.#c.get(e);
|
|
700
707
|
if (!n) return;
|
|
701
708
|
let r = this.getRootNode();
|
|
@@ -705,7 +712,7 @@ var W = class e extends m {
|
|
|
705
712
|
let a = i;
|
|
706
713
|
a[n] = t;
|
|
707
714
|
}
|
|
708
|
-
#
|
|
715
|
+
#U() {
|
|
709
716
|
let e = this.#n;
|
|
710
717
|
function t() {
|
|
711
718
|
let t = f();
|
|
@@ -735,7 +742,7 @@ var W = class e extends m {
|
|
|
735
742
|
t = {};
|
|
736
743
|
for (let n of Object.keys(e)) t[n] = n;
|
|
737
744
|
}
|
|
738
|
-
this.#
|
|
745
|
+
this.#K(e, t);
|
|
739
746
|
for (let [n, r] of Object.entries(t)) if (this.#w(r)) {
|
|
740
747
|
let t = o(e, n);
|
|
741
748
|
t !== void 0 && (this[r] = t), this.#l.set(r, {
|
|
@@ -757,7 +764,7 @@ var W = class e extends m {
|
|
|
757
764
|
unsubscribe: i
|
|
758
765
|
});
|
|
759
766
|
}
|
|
760
|
-
#
|
|
767
|
+
#W() {
|
|
761
768
|
let t = new Set(Object.keys(this.#n.properties));
|
|
762
769
|
for (let n of this.getAttributeNames()) if (!p.has(n) && !n.startsWith("on") && n !== "ref") {
|
|
763
770
|
if (n === "form-assoc") {
|
|
@@ -769,38 +776,38 @@ var W = class e extends m {
|
|
|
769
776
|
this.#D();
|
|
770
777
|
continue;
|
|
771
778
|
}
|
|
772
|
-
this.#
|
|
779
|
+
this.#F(null, n, "is not a supported attribute");
|
|
773
780
|
}
|
|
774
781
|
}
|
|
775
782
|
}
|
|
776
|
-
#
|
|
783
|
+
#G(e, t, n) {
|
|
777
784
|
let r = n.match(w);
|
|
778
785
|
if (r) return r.forEach((n) => {
|
|
779
786
|
let r = M(n);
|
|
780
|
-
this[r] === void 0 && this.#
|
|
787
|
+
this[r] === void 0 && this.#I(e, t, r);
|
|
781
788
|
}), r;
|
|
782
789
|
}
|
|
783
|
-
#
|
|
790
|
+
#K(e, t) {
|
|
784
791
|
for (let [n, r] of Object.entries(t)) {
|
|
785
792
|
let t = o(e, n);
|
|
786
|
-
t === void 0 && this.#
|
|
793
|
+
t === void 0 && this.#F(this, void 0, `invalid state path "${n}"`), t = this[r], this.#w(r) || this.#F(null, r, "refers to missing property in useState map");
|
|
787
794
|
}
|
|
788
795
|
}
|
|
789
|
-
#
|
|
796
|
+
#q(e, t, n) {
|
|
790
797
|
let { values: r } = this.#n.properties[e];
|
|
791
798
|
if (r) {
|
|
792
799
|
let i;
|
|
793
|
-
t === String ? typeof n == "string" ? r.includes(n) || (i = `must be one of ${r.map((e) => `"${e}"`).join(", ")}`) : i = `value is a ${typeof n}, but type is String` : i = "declares allowed values, but its type is not String", i && this.#
|
|
800
|
+
t === String ? typeof n == "string" ? r.includes(n) || (i = `must be one of ${r.map((e) => `"${e}"`).join(", ")}`) : i = `value is a ${typeof n}, but type is String` : i = "declares allowed values, but its type is not String", i && this.#F(null, e, i);
|
|
794
801
|
}
|
|
795
802
|
if (n instanceof t) return;
|
|
796
803
|
let i = typeof n;
|
|
797
804
|
if (i === "object") {
|
|
798
805
|
let { constructor: r } = n;
|
|
799
|
-
i = r.name, r !== t && this.#
|
|
806
|
+
i = r.name, r !== t && this.#F(null, e, `was set to a ${i}, but must be a ${t.name}`);
|
|
800
807
|
}
|
|
801
|
-
i !== t.name.toLowerCase() && this.#
|
|
808
|
+
i !== t.name.toLowerCase() && this.#F(null, e, `was set to a ${i}, but must be a ${t.name}`);
|
|
802
809
|
}
|
|
803
|
-
#
|
|
810
|
+
#J(e) {
|
|
804
811
|
let t = Array.from(e.querySelectorAll("*"));
|
|
805
812
|
for (let e of t) {
|
|
806
813
|
let t = [];
|
|
@@ -810,9 +817,9 @@ var W = class e extends m {
|
|
|
810
817
|
let i = r.slice(2);
|
|
811
818
|
i = i[0].toLowerCase() + i.slice(1).toLowerCase();
|
|
812
819
|
let a = n.value;
|
|
813
|
-
this.#
|
|
820
|
+
this.#G(e, r, a);
|
|
814
821
|
let o;
|
|
815
|
-
typeof this[a] == "function" ? o = (e) => this[a](e) : (this.#
|
|
822
|
+
typeof this[a] == "function" ? o = (e) => this[a](e) : (this.#G(e, r, a), o = () => this.#v(a)), e.addEventListener(i, o), t.push(r);
|
|
816
823
|
}
|
|
817
824
|
}
|
|
818
825
|
for (let n of t) e.removeAttribute(n);
|
package/dist/wrec-ssr.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare abstract class Wrec extends HTMLElementBase {
|
|
|
51
51
|
private static methodToExprsMap;
|
|
52
52
|
private static propToExprsMap;
|
|
53
53
|
private static registeredComputedProps;
|
|
54
|
+
private static computedPropsRegistered;
|
|
54
55
|
private static template;
|
|
55
56
|
[key: string]: any;
|
|
56
57
|
static define(elementName: string): void;
|
package/dist/wrec-ssr.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, i as t, n, r, t as i } from "./wrec-
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "./wrec-DEac2MyH.js";
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var a = Object.defineProperty, o = Object.getOwnPropertyDescriptor, s = Object.getOwnPropertyNames, c = Object.prototype.hasOwnProperty, l = (e, t) => () => (e && (t = e(e = 0)), t), u = (e, t) => () => (t || e((t = { exports: {} }).exports, t), t.exports), d = (e, t) => {
|
|
4
4
|
let n = {};
|
package/dist/wrec.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export declare abstract class Wrec extends HTMLElementBase {
|
|
|
51
51
|
private static methodToExprsMap;
|
|
52
52
|
private static propToExprsMap;
|
|
53
53
|
private static registeredComputedProps;
|
|
54
|
+
private static computedPropsRegistered;
|
|
54
55
|
private static template;
|
|
55
56
|
[key: string]: any;
|
|
56
57
|
static define(elementName: string): void;
|
package/dist/wrec.es.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e, i as t, n, r, t as i } from "./wrec-
|
|
1
|
+
import { a as e, i as t, n, r, t as i } from "./wrec-DEac2MyH.js";
|
|
2
2
|
export { i as Wrec, e as WrecState, n as createElement, r as css, t as html };
|
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.29.
|
|
5
|
+
"version": "0.29.3",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"preview": "vite preview",
|
|
47
47
|
"test": "node port-check.js && playwright test",
|
|
48
48
|
"testui": "playwright test --ui",
|
|
49
|
-
"unittest": "vitest"
|
|
49
|
+
"unittest": "vitest run"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@playwright/test": "^1.59.1",
|
package/scripts/lint.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// This lints a given source file that defines a wrec component.
|
|
4
|
+
// To run this, enter `npx wrec-lint [--verbose] {file-path}`
|
|
5
|
+
//
|
|
6
|
+
// Include --verbose to output lists of the
|
|
7
|
+
// properties and expressions that are found.
|
|
8
|
+
|
|
3
9
|
// This linter checks Wrec components for these issues:
|
|
4
10
|
// - undefined properties accessed in expressions
|
|
5
11
|
// - undefined instance methods called in expressions
|
|
@@ -1380,7 +1386,7 @@ function main() {
|
|
|
1380
1386
|
const positionalArgs = args.filter(arg => arg !== '--verbose');
|
|
1381
1387
|
|
|
1382
1388
|
if (positionalArgs.length > 1) {
|
|
1383
|
-
fail('usage: node scripts/wrec-lint.js [file
|
|
1389
|
+
fail('usage: node scripts/wrec-lint.js [--verbose] {file-path}');
|
|
1384
1390
|
}
|
|
1385
1391
|
|
|
1386
1392
|
const [filePath] = positionalArgs;
|
package/scripts/used-by.js
CHANGED
|
@@ -1,50 +1,62 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// This script inspects a given Wrec component source file and
|
|
3
|
+
// determines the proper values for property config `usedBy` properties.
|
|
4
|
+
// Each value is a list of methods that use the property
|
|
5
|
+
// or a single method name.
|
|
6
|
+
// It uses the TypeScript compiler to parse the file,
|
|
7
|
+
// discover which expressions call which methods,
|
|
8
|
+
// trace property usage through those call chains, and
|
|
9
|
+
// output or update the `usedBy` properties`.
|
|
10
|
+
//
|
|
11
|
+
// To run this, enter `npx wrec-usedby [--dry] [file-path]`
|
|
12
|
+
// If no file-path is specified, the script runs on
|
|
13
|
+
// all .js and .ts files in and below the current directory.
|
|
14
|
+
//
|
|
15
|
+
// Include the --dry flag for a dry run where `usedBy` values are output,
|
|
16
|
+
// but no files are modified.
|
|
2
17
|
|
|
3
18
|
import fs from 'node:fs';
|
|
4
19
|
import path from 'node:path';
|
|
20
|
+
import {fileURLToPath} from 'node:url';
|
|
5
21
|
import ts from 'typescript';
|
|
6
22
|
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (args.includes('--check')) {
|
|
18
|
-
console.error('Use --dry instead of --check.');
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (inputPaths.length !== 1) {
|
|
23
|
-
console.error(
|
|
24
|
-
'Specify a single source file, e.g. npx wrec-usedby src/examples/radio-group.js'
|
|
23
|
+
function buildConfigText(sourceFile, member, methodNames, quote) {
|
|
24
|
+
const {text} = sourceFile;
|
|
25
|
+
const configObject = member.initializer;
|
|
26
|
+
const existingMembers = configObject.properties.filter(
|
|
27
|
+
property =>
|
|
28
|
+
!(
|
|
29
|
+
ts.isPropertyAssignment(property) &&
|
|
30
|
+
getNameText(property.name) === 'usedBy'
|
|
31
|
+
)
|
|
25
32
|
);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
for (const target of targets) {
|
|
33
|
-
if (!fs.existsSync(target)) {
|
|
34
|
-
console.error(`File not found: ${path.relative(cwd, target)}`);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
33
|
+
const existingTexts = existingMembers.map(property =>
|
|
34
|
+
text.slice(property.getStart(sourceFile), property.end).trim()
|
|
35
|
+
);
|
|
36
|
+
if (methodNames.length > 0)
|
|
37
|
+
existingTexts.push(createUsedByProperty(methodNames, quote));
|
|
37
38
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const original = text.slice(
|
|
40
|
+
configObject.getStart(sourceFile),
|
|
41
|
+
configObject.end
|
|
42
|
+
);
|
|
43
|
+
const multiline = original.includes('\n');
|
|
44
|
+
if (!multiline) {
|
|
45
|
+
const openMatch = original.match(/^\{(\s*)/);
|
|
46
|
+
const closeMatch = original.match(/(\s*)\}$/);
|
|
47
|
+
const openSpacing = openMatch ? openMatch[1] : ' ';
|
|
48
|
+
const closeSpacing = closeMatch ? closeMatch[1] : ' ';
|
|
49
|
+
return `{${openSpacing}${existingTexts.join(', ')}${closeSpacing}}`;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
// Preserve the surrounding formatting style so the update feels like a
|
|
53
|
+
// minimal edit instead of reformatting the whole object.
|
|
54
|
+
const memberIndent = getIndent(text, member.getStart(sourceFile));
|
|
55
|
+
const firstExisting = existingMembers[0];
|
|
56
|
+
const innerIndent = firstExisting
|
|
57
|
+
? getIndent(text, firstExisting.getStart(sourceFile))
|
|
58
|
+
: memberIndent + ' ';
|
|
59
|
+
return `{\n${existingTexts.map(part => `${innerIndent}${part}`).join(',\n')}\n${memberIndent}}`;
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
function collectFiles(startPath, files = []) {
|
|
@@ -52,9 +64,7 @@ function collectFiles(startPath, files = []) {
|
|
|
52
64
|
|
|
53
65
|
const stat = fs.statSync(startPath);
|
|
54
66
|
if (stat.isFile()) {
|
|
55
|
-
if (
|
|
56
|
-
files.push(startPath);
|
|
57
|
-
}
|
|
67
|
+
if (isSupportedSourceFile(startPath)) files.push(startPath);
|
|
58
68
|
return files;
|
|
59
69
|
}
|
|
60
70
|
|
|
@@ -63,12 +73,7 @@ function collectFiles(startPath, files = []) {
|
|
|
63
73
|
if (entry.isDirectory()) {
|
|
64
74
|
if (entry.name === 'node_modules' || entry.name === 'dist') continue;
|
|
65
75
|
collectFiles(fullPath, files);
|
|
66
|
-
} else if (
|
|
67
|
-
entry.isFile() &&
|
|
68
|
-
/\.(js|ts)$/.test(entry.name) &&
|
|
69
|
-
!entry.name.endsWith('.d.ts') &&
|
|
70
|
-
!entry.name.includes('.test.')
|
|
71
|
-
) {
|
|
76
|
+
} else if (entry.isFile() && isSupportedSourceFile(entry.name, true)) {
|
|
72
77
|
files.push(fullPath);
|
|
73
78
|
}
|
|
74
79
|
}
|
|
@@ -76,63 +81,11 @@ function collectFiles(startPath, files = []) {
|
|
|
76
81
|
return files;
|
|
77
82
|
}
|
|
78
83
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
ts.isIdentifier(name) ||
|
|
84
|
-
ts.isStringLiteral(name) ||
|
|
85
|
-
ts.isPrivateIdentifier(name)
|
|
86
|
-
) {
|
|
87
|
-
return name.text;
|
|
88
|
-
}
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function hasStaticModifier(node) {
|
|
93
|
-
return Boolean(
|
|
94
|
-
node.modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword)
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function getWrecImportInfo(sourceFile) {
|
|
99
|
-
const names = new Set(['Wrec']);
|
|
100
|
-
let quote = "'";
|
|
101
|
-
|
|
102
|
-
for (const statement of sourceFile.statements) {
|
|
103
|
-
if (
|
|
104
|
-
!ts.isImportDeclaration(statement) ||
|
|
105
|
-
!statement.importClause ||
|
|
106
|
-
!ts.isStringLiteral(statement.moduleSpecifier)
|
|
107
|
-
) {
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const moduleName = statement.moduleSpecifier.text;
|
|
112
|
-
const isWrecModule =
|
|
113
|
-
moduleName === 'wrec' ||
|
|
114
|
-
moduleName === 'wrec/ssr' ||
|
|
115
|
-
moduleName.endsWith('/wrec') ||
|
|
116
|
-
moduleName.endsWith('/wrec-ssr');
|
|
117
|
-
if (!isWrecModule) continue;
|
|
118
|
-
|
|
119
|
-
const namedBindings = statement.importClause.namedBindings;
|
|
120
|
-
if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;
|
|
121
|
-
|
|
122
|
-
for (const element of namedBindings.elements) {
|
|
123
|
-
const importedName = element.propertyName?.text ?? element.name.text;
|
|
124
|
-
if (importedName === 'Wrec') {
|
|
125
|
-
names.add(element.name.text);
|
|
126
|
-
|
|
127
|
-
const moduleText = statement.moduleSpecifier.getText(sourceFile);
|
|
128
|
-
if (moduleText.startsWith('"') || moduleText.startsWith("'")) {
|
|
129
|
-
quote = moduleText[0];
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
84
|
+
function createUsedByProperty(methodNames, quote) {
|
|
85
|
+
if (methodNames.length === 1) {
|
|
86
|
+
return `usedBy: ${quote}${methodNames[0]}${quote}`;
|
|
133
87
|
}
|
|
134
|
-
|
|
135
|
-
return {names, quote};
|
|
88
|
+
return `usedBy: [${methodNames.map(name => `${quote}${name}${quote}`).join(', ')}]`;
|
|
136
89
|
}
|
|
137
90
|
|
|
138
91
|
function extendsWrec(node, wrecNames) {
|
|
@@ -150,51 +103,6 @@ function extendsWrec(node, wrecNames) {
|
|
|
150
103
|
);
|
|
151
104
|
}
|
|
152
105
|
|
|
153
|
-
function getTemplateCalledMethods(classNode) {
|
|
154
|
-
const methodNames = new Set();
|
|
155
|
-
const CALL_RE = /this\.([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/g;
|
|
156
|
-
|
|
157
|
-
function visit(node) {
|
|
158
|
-
if (
|
|
159
|
-
ts.isPropertyAccessExpression(node) &&
|
|
160
|
-
node.expression.kind === ts.SyntaxKind.ThisKeyword &&
|
|
161
|
-
ts.isCallExpression(node.parent) &&
|
|
162
|
-
node.parent.expression === node
|
|
163
|
-
) {
|
|
164
|
-
methodNames.add(node.name.text);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
ts.forEachChild(node, visit);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function addTemplateTextMethods(template) {
|
|
171
|
-
const text = template.getText();
|
|
172
|
-
for (const match of text.matchAll(CALL_RE)) {
|
|
173
|
-
methodNames.add(match[1]);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
for (const member of classNode.members) {
|
|
178
|
-
if (
|
|
179
|
-
ts.isPropertyDeclaration(member) &&
|
|
180
|
-
hasStaticModifier(member) &&
|
|
181
|
-
getNameText(member.name) === 'html' &&
|
|
182
|
-
member.initializer
|
|
183
|
-
) {
|
|
184
|
-
if (
|
|
185
|
-
ts.isTaggedTemplateExpression(member.initializer) &&
|
|
186
|
-
ts.isIdentifier(member.initializer.tag) &&
|
|
187
|
-
member.initializer.tag.text === 'html'
|
|
188
|
-
) {
|
|
189
|
-
addTemplateTextMethods(member.initializer.template);
|
|
190
|
-
}
|
|
191
|
-
ts.forEachChild(member.initializer, visit);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return methodNames;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
106
|
function getComputedCalledMethods(classNode) {
|
|
199
107
|
const methodNames = new Set();
|
|
200
108
|
const CALL_RE = /this\.([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/g;
|
|
@@ -238,6 +146,12 @@ function getComputedCalledMethods(classNode) {
|
|
|
238
146
|
return methodNames;
|
|
239
147
|
}
|
|
240
148
|
|
|
149
|
+
function getIndent(text, pos) {
|
|
150
|
+
const lineStart = text.lastIndexOf('\n', pos - 1) + 1;
|
|
151
|
+
const match = /^[ \t]*/.exec(text.slice(lineStart));
|
|
152
|
+
return match ? match[0] : '';
|
|
153
|
+
}
|
|
154
|
+
|
|
241
155
|
function getMethodUsages(classNode, propertyNames) {
|
|
242
156
|
const methodInfo = new Map();
|
|
243
157
|
for (const member of classNode.members) {
|
|
@@ -283,6 +197,9 @@ function getMethodUsages(classNode, propertyNames) {
|
|
|
283
197
|
}
|
|
284
198
|
|
|
285
199
|
function visit(child) {
|
|
200
|
+
// Record both direct property reads like `this.foo` and method calls
|
|
201
|
+
// like `this.renderFoo()` so we can later propagate property usage
|
|
202
|
+
// through method-to-method call chains.
|
|
286
203
|
if (
|
|
287
204
|
ts.isPropertyAccessExpression(child) &&
|
|
288
205
|
child.expression.kind === ts.SyntaxKind.ThisKeyword
|
|
@@ -354,6 +271,9 @@ function getMethodUsages(classNode, propertyNames) {
|
|
|
354
271
|
const memo = new Map();
|
|
355
272
|
|
|
356
273
|
function getTransitiveProps(methodName, seen = new Set()) {
|
|
274
|
+
// Starting from methods that are reachable from the template/computed
|
|
275
|
+
// properties, walk through nested method calls and accumulate every
|
|
276
|
+
// component property touched along the way.
|
|
357
277
|
if (memo.has(methodName)) return memo.get(methodName);
|
|
358
278
|
if (seen.has(methodName)) return new Set();
|
|
359
279
|
|
|
@@ -395,55 +315,129 @@ function getMethodUsages(classNode, propertyNames) {
|
|
|
395
315
|
return propToMethods;
|
|
396
316
|
}
|
|
397
317
|
|
|
398
|
-
function
|
|
399
|
-
if (
|
|
400
|
-
|
|
318
|
+
function getNameText(name) {
|
|
319
|
+
if (
|
|
320
|
+
ts.isIdentifier(name) ||
|
|
321
|
+
ts.isStringLiteral(name) ||
|
|
322
|
+
ts.isPrivateIdentifier(name)
|
|
323
|
+
) {
|
|
324
|
+
return name.text;
|
|
401
325
|
}
|
|
402
|
-
return
|
|
326
|
+
return null;
|
|
403
327
|
}
|
|
404
328
|
|
|
405
|
-
function
|
|
406
|
-
const
|
|
407
|
-
const
|
|
408
|
-
|
|
329
|
+
function getTemplateCalledMethods(classNode) {
|
|
330
|
+
const methodNames = new Set();
|
|
331
|
+
const CALL_RE = /this\.([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/g;
|
|
332
|
+
|
|
333
|
+
function visit(node) {
|
|
334
|
+
if (
|
|
335
|
+
ts.isPropertyAccessExpression(node) &&
|
|
336
|
+
node.expression.kind === ts.SyntaxKind.ThisKeyword &&
|
|
337
|
+
ts.isCallExpression(node.parent) &&
|
|
338
|
+
node.parent.expression === node
|
|
339
|
+
) {
|
|
340
|
+
methodNames.add(node.name.text);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
ts.forEachChild(node, visit);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function addTemplateTextMethods(template) {
|
|
347
|
+
// Template expressions can hide method calls inside raw template text,
|
|
348
|
+
// so use a regex in addition to AST traversal to catch those names.
|
|
349
|
+
const text = template.getText();
|
|
350
|
+
for (const match of text.matchAll(CALL_RE)) {
|
|
351
|
+
methodNames.add(match[1]);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
for (const member of classNode.members) {
|
|
356
|
+
if (
|
|
357
|
+
ts.isPropertyDeclaration(member) &&
|
|
358
|
+
hasStaticModifier(member) &&
|
|
359
|
+
getNameText(member.name) === 'html' &&
|
|
360
|
+
member.initializer
|
|
361
|
+
) {
|
|
362
|
+
if (
|
|
363
|
+
ts.isTaggedTemplateExpression(member.initializer) &&
|
|
364
|
+
ts.isIdentifier(member.initializer.tag) &&
|
|
365
|
+
member.initializer.tag.text === 'html'
|
|
366
|
+
) {
|
|
367
|
+
addTemplateTextMethods(member.initializer.template);
|
|
368
|
+
}
|
|
369
|
+
ts.forEachChild(member.initializer, visit);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return methodNames;
|
|
409
374
|
}
|
|
410
375
|
|
|
411
|
-
function
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
const existingMembers = configObject.properties.filter(
|
|
415
|
-
property =>
|
|
416
|
-
!(
|
|
417
|
-
ts.isPropertyAssignment(property) &&
|
|
418
|
-
getNameText(property.name) === 'usedBy'
|
|
419
|
-
)
|
|
420
|
-
);
|
|
421
|
-
const existingTexts = existingMembers.map(property =>
|
|
422
|
-
text.slice(property.getStart(sourceFile), property.end).trim()
|
|
423
|
-
);
|
|
424
|
-
if (methodNames.length > 0)
|
|
425
|
-
existingTexts.push(createUsedByProperty(methodNames, quote));
|
|
376
|
+
function getWrecImportInfo(sourceFile) {
|
|
377
|
+
const names = new Set(['Wrec']);
|
|
378
|
+
let quote = "'";
|
|
426
379
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
380
|
+
// Support aliased imports such as `import {Wrec as Base} from 'wrec'`
|
|
381
|
+
// so subclass detection still works and generated text matches the
|
|
382
|
+
// file's existing quote style.
|
|
383
|
+
for (const statement of sourceFile.statements) {
|
|
384
|
+
if (
|
|
385
|
+
!ts.isImportDeclaration(statement) ||
|
|
386
|
+
!statement.importClause ||
|
|
387
|
+
!ts.isStringLiteral(statement.moduleSpecifier)
|
|
388
|
+
) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const moduleName = statement.moduleSpecifier.text;
|
|
393
|
+
const isWrecModule =
|
|
394
|
+
moduleName === 'wrec' ||
|
|
395
|
+
moduleName === 'wrec/ssr' ||
|
|
396
|
+
moduleName.endsWith('/wrec') ||
|
|
397
|
+
moduleName.endsWith('/wrec-ssr');
|
|
398
|
+
if (!isWrecModule) continue;
|
|
399
|
+
|
|
400
|
+
const namedBindings = statement.importClause.namedBindings;
|
|
401
|
+
if (!namedBindings || !ts.isNamedImports(namedBindings)) continue;
|
|
402
|
+
|
|
403
|
+
for (const element of namedBindings.elements) {
|
|
404
|
+
const importedName = element.propertyName?.text ?? element.name.text;
|
|
405
|
+
if (importedName === 'Wrec') {
|
|
406
|
+
names.add(element.name.text);
|
|
407
|
+
|
|
408
|
+
const moduleText = statement.moduleSpecifier.getText(sourceFile);
|
|
409
|
+
if (moduleText.startsWith('"') || moduleText.startsWith("'")) {
|
|
410
|
+
quote = moduleText[0];
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return {names, quote};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function hasStaticModifier(node) {
|
|
420
|
+
return Boolean(
|
|
421
|
+
node.modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword)
|
|
430
422
|
);
|
|
431
|
-
|
|
432
|
-
if (!multiline) return `{ ${existingTexts.join(', ')} }`;
|
|
423
|
+
}
|
|
433
424
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
425
|
+
function isSupportedSourceFile(filePath, excludeTests = false) {
|
|
426
|
+
return (
|
|
427
|
+
/\.(js|ts)$/.test(filePath) &&
|
|
428
|
+
!/\.d\.ts$/.test(filePath) &&
|
|
429
|
+
(!excludeTests || !filePath.includes('.test.'))
|
|
430
|
+
);
|
|
440
431
|
}
|
|
441
432
|
|
|
442
433
|
function transformSourceFile(sourceFile) {
|
|
443
434
|
const edits = [];
|
|
444
435
|
const {names: wrecNames, quote} = getWrecImportInfo(sourceFile);
|
|
436
|
+
const suggestions = [];
|
|
445
437
|
let foundWrecSubclass = false;
|
|
446
438
|
|
|
439
|
+
// Each matching class contributes text replacements for the specific
|
|
440
|
+
// property config objects that need `usedBy` added, updated, or removed.
|
|
447
441
|
for (const node of sourceFile.statements) {
|
|
448
442
|
if (!ts.isClassDeclaration(node) || !extendsWrec(node, wrecNames)) continue;
|
|
449
443
|
foundWrecSubclass = true;
|
|
@@ -492,6 +486,15 @@ function transformSourceFile(sourceFile) {
|
|
|
492
486
|
);
|
|
493
487
|
const hadUsedBy =
|
|
494
488
|
existingMembers.length !== configObject.properties.length;
|
|
489
|
+
if (methodNames.length > 0 || hadUsedBy) {
|
|
490
|
+
suggestions.push({
|
|
491
|
+
propName,
|
|
492
|
+
suggestion:
|
|
493
|
+
methodNames.length > 0
|
|
494
|
+
? createUsedByProperty(methodNames, quote)
|
|
495
|
+
: 'remove usedBy'
|
|
496
|
+
});
|
|
497
|
+
}
|
|
495
498
|
const needsUsedBy = methodNames.length > 0;
|
|
496
499
|
if (!hadUsedBy && !needsUsedBy) continue;
|
|
497
500
|
|
|
@@ -517,6 +520,7 @@ function transformSourceFile(sourceFile) {
|
|
|
517
520
|
changed: false,
|
|
518
521
|
edits: [],
|
|
519
522
|
foundWrecSubclass: false,
|
|
523
|
+
suggestions,
|
|
520
524
|
text: sourceFile.text
|
|
521
525
|
};
|
|
522
526
|
}
|
|
@@ -526,6 +530,7 @@ function transformSourceFile(sourceFile) {
|
|
|
526
530
|
changed: false,
|
|
527
531
|
edits: [],
|
|
528
532
|
foundWrecSubclass: true,
|
|
533
|
+
suggestions,
|
|
529
534
|
text: sourceFile.text
|
|
530
535
|
};
|
|
531
536
|
}
|
|
@@ -537,49 +542,123 @@ function transformSourceFile(sourceFile) {
|
|
|
537
542
|
nextSource.slice(0, edit.start) + edit.text + nextSource.slice(edit.end);
|
|
538
543
|
}
|
|
539
544
|
|
|
540
|
-
return {
|
|
545
|
+
return {
|
|
546
|
+
changed: true,
|
|
547
|
+
edits,
|
|
548
|
+
foundWrecSubclass: true,
|
|
549
|
+
suggestions,
|
|
550
|
+
text: nextSource
|
|
551
|
+
};
|
|
541
552
|
}
|
|
542
553
|
|
|
543
|
-
|
|
554
|
+
function validateTargetFile(target, cwd = process.cwd()) {
|
|
555
|
+
if (!fs.existsSync(target)) {
|
|
556
|
+
throw new Error(`File not found: ${path.relative(cwd, target)}`);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const stat = fs.statSync(target);
|
|
560
|
+
if (!stat.isFile()) {
|
|
561
|
+
throw new Error(`Not a file: ${path.relative(cwd, target)}`);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if (!/\.(js|ts)$/.test(target) || /\.d\.ts$/.test(target)) {
|
|
565
|
+
throw new Error(`Unsupported file type: ${path.relative(cwd, target)}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
544
568
|
|
|
545
|
-
|
|
546
|
-
const
|
|
547
|
-
|
|
569
|
+
export function updateUsedBySource(filePath, text) {
|
|
570
|
+
const scriptKind = filePath.endsWith('.ts')
|
|
571
|
+
? ts.ScriptKind.TS
|
|
572
|
+
: ts.ScriptKind.JS;
|
|
548
573
|
const sourceFile = ts.createSourceFile(
|
|
549
|
-
|
|
574
|
+
filePath,
|
|
550
575
|
text,
|
|
551
576
|
ts.ScriptTarget.Latest,
|
|
552
577
|
true,
|
|
553
578
|
scriptKind
|
|
554
579
|
);
|
|
555
580
|
|
|
581
|
+
return transformSourceFile(sourceFile);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export function updateUsedByFile(filePath, options = {}) {
|
|
585
|
+
const {dry = false, quiet = false} = options;
|
|
586
|
+
const cwd = process.cwd();
|
|
587
|
+
const resolved = path.resolve(cwd, filePath);
|
|
588
|
+
validateTargetFile(resolved, cwd);
|
|
589
|
+
|
|
590
|
+
const text = fs.readFileSync(resolved, 'utf8');
|
|
556
591
|
const {
|
|
557
592
|
changed,
|
|
558
|
-
edits,
|
|
559
593
|
foundWrecSubclass,
|
|
594
|
+
suggestions,
|
|
560
595
|
text: nextText
|
|
561
|
-
} =
|
|
596
|
+
} = updateUsedBySource(resolved, text);
|
|
562
597
|
if (!foundWrecSubclass) {
|
|
563
|
-
|
|
564
|
-
|
|
598
|
+
throw new Error('No class extending Wrec was found.');
|
|
599
|
+
}
|
|
600
|
+
if (dry) {
|
|
601
|
+
return {changed, foundWrecSubclass, suggestions, text: nextText};
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (changed) {
|
|
605
|
+
// Otherwise, apply the rewritten source text back to disk.
|
|
606
|
+
fs.writeFileSync(resolved, nextText);
|
|
607
|
+
}
|
|
608
|
+
return {changed, foundWrecSubclass, suggestions: [], text: nextText, quiet};
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function fail(message) {
|
|
612
|
+
console.error(message);
|
|
613
|
+
process.exit(1);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function main() {
|
|
617
|
+
const args = process.argv.slice(2);
|
|
618
|
+
const dry = args.includes('--dry');
|
|
619
|
+
const quiet = args.includes('--quiet');
|
|
620
|
+
const inputPaths = args.filter(arg => !arg.startsWith('--'));
|
|
621
|
+
|
|
622
|
+
if (args.includes('--check')) {
|
|
623
|
+
throw new Error('Use --dry instead of --check.');
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
if (inputPaths.length !== 1) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
'Specify a single source file, e.g. npx wrec-usedby src/examples/radio-group.js'
|
|
629
|
+
);
|
|
565
630
|
}
|
|
566
|
-
if (!changed) continue;
|
|
567
631
|
|
|
568
|
-
|
|
632
|
+
const result = updateUsedByFile(inputPaths[0], {dry, quiet});
|
|
569
633
|
if (dry) {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
console.log(`${
|
|
634
|
+
// In dry mode, report the inferred change and exit non-zero when at
|
|
635
|
+
// least one update would be needed so the script can be used in checks.
|
|
636
|
+
for (const {propName, suggestion} of result.suggestions) {
|
|
637
|
+
console.log(`${propName} - ${suggestion}`);
|
|
574
638
|
}
|
|
575
|
-
|
|
576
|
-
|
|
639
|
+
if (!result.changed) return;
|
|
640
|
+
process.exit(1);
|
|
577
641
|
}
|
|
578
|
-
|
|
642
|
+
|
|
643
|
+
if (result.changed) console.log('updated');
|
|
579
644
|
}
|
|
580
645
|
|
|
581
|
-
|
|
646
|
+
const isCliEntry = (() => {
|
|
647
|
+
if (!process.argv[1]) return false;
|
|
648
|
+
try {
|
|
649
|
+
return (
|
|
650
|
+
fs.realpathSync(process.argv[1]) ===
|
|
651
|
+
fs.realpathSync(fileURLToPath(import.meta.url))
|
|
652
|
+
);
|
|
653
|
+
} catch {
|
|
654
|
+
return path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
655
|
+
}
|
|
656
|
+
})();
|
|
582
657
|
|
|
583
|
-
if (
|
|
584
|
-
|
|
658
|
+
if (isCliEntry) {
|
|
659
|
+
try {
|
|
660
|
+
main();
|
|
661
|
+
} catch (error) {
|
|
662
|
+
fail(error instanceof Error ? error.message : String(error));
|
|
663
|
+
}
|
|
585
664
|
}
|