Repository: Rezact/Rezact
Pull request #18 (2023-09-30 19:43:25)

Allow mixing of compiled and manual imports
Allow mixing compiled signals and manually created signals in the same file.

Improve Type support for manually created signals (see images below)

![image](https://github.com/Rezact/Rezact/assets/2927894/0049eca1-65eb-4e4e-8bb0-dc2b6ae73c80)

MapSignals:

![image](https://github.com/Rezact/Rezact/assets/2927894/4f7ce80e-78a9-423a-8f45-df1a4c80cb75)




Patch:
diff --git a/package.json b/package.json
index 21191ae..cb23d7f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@rezact/rezact",
-  "version": "1.0.15-beta.15",
+  "version": "1.0.15-beta.16",
   "type": "module",
   "description": "Intuitive Reactivity, Simplified State. Embrace a modern UI framework that encourages direct data mutations, offers module-level reactivity, and simplifies component design. With Rezact, you get the power of reactivity without the boilerplate. Dive into a seamless development experience tailored for today's web.",
   "main": "index.cjs",
diff --git a/src/lib/rezact/mapState.ts b/src/lib/rezact/mapState.ts
index d6d5449..faa8bcb 100644
--- a/src/lib/rezact/mapState.ts
+++ b/src/lib/rezact/mapState.ts
@@ -15,7 +15,7 @@ overrideEffect(_effect);
 
 function _effect(func: (obj: any) => {}, deps: any[]) {
   const NewState = deps[0] instanceof MapSignal ? MapSignal : Signal;
-  const newState: any = new NewState(func(deps));
+  const newState: any = new NewState(func(deps) as any);
   newState.computed = true;
   const depsLen = deps.length;
   for (let i = 0; i < depsLen; i++) {
@@ -96,20 +96,27 @@ function subscribeToNestedStates(item, mapStateObj) {
   });
 }
 
-export class MapSignal extends Signal {
+export class MapSignal<T> extends Signal<T> {
   elmRefCache: any = new Map();
   refreshTimer: any;
   clearCacheTimer: any;
-
-  constructor(st: any) {
-    super(st);
-    ["push", "pop", "splice", "shift", "unshift", "reduce"].forEach((item) => {
-      this[item] = (...args) => {
-        this.value[item](...args);
-        this.updateList(this.func);
-        this.alertSubs(this.value);
-      };
-    });
+  push: (...items: T[]) => {};
+  map: (
+    callbackfn: (value: T, index: number, array: T[]) => unknown,
+    thisArg?: any
+  ) => {};
+
+  constructor(st: T[]) {
+    super(st as T);
+    ["push", "pop", "splice", "shift", "unshift", "reduce", "map"].forEach(
+      (item) => {
+        this[item] = (...args) => {
+          this.value[item](...args);
+          this.updateList(this.func);
+          this.alertSubs(this.value);
+        };
+      }
+    );
   }
 
   removeStaleElmRefCacheItems() {
diff --git a/src/lib/rezact/signals.ts b/src/lib/rezact/signals.ts
index 42efc16..848c2c8 100644
--- a/src/lib/rezact/signals.ts
+++ b/src/lib/rezact/signals.ts
@@ -97,7 +97,7 @@ function attachSubs(elm, subFuncs) {
   batchTime = Date.now();
 }
 
-export class Signal {
+export class Signal<T> {
   state = true;
   value: any = null;
   subs: any = new Map();
@@ -107,7 +107,7 @@ export class Signal {
   idxState: any;
   func: any;
 
-  constructor(st: any) {
+  constructor(st: T) {
     this.value = st;
   }
 
@@ -123,12 +123,12 @@ export class Signal {
     }
   }
 
-  get() {
-    if (this.value instanceof Text) return this.value.textContent;
+  get(): T {
+    if (this.value instanceof Text) return this.value.textContent as T;
     return this.value;
   }
 
-  set(newVal: any) {
+  set(newVal: T) {
     const val = this.get();
 
     if (newVal === val && !isArray(newVal)) return;
@@ -142,7 +142,7 @@ export class Signal {
       val.replaceWith(newVal);
 
     if (this.value instanceof Text) {
-      this.value.textContent = newVal;
+      (this.value.textContent as T) = newVal;
     } else {
       this.value = newVal;
     }
diff --git a/src/lib/rezact/vite-plugin.ts b/src/lib/rezact/vite-plugin.ts
index f79386c..221c734 100644
--- a/src/lib/rezact/vite-plugin.ts
+++ b/src/lib/rezact/vite-plugin.ts
@@ -7,6 +7,7 @@ import MagicString from "magic-string";
 const acorn = Parser.extend(jsx());
 
 let src = "";
+let itemsAlreadyImported = [];
 let magicString: any = null;
 let lastImport: any = null;
 let importsUsed: any = {};
@@ -720,6 +721,9 @@ function compileRezact(ast) {
     },
 
     ImportDeclaration(node: any) {
+      itemsAlreadyImported.push(
+        ...node.specifiers.map((spec) => spec.imported?.name).filter((i) => i)
+      );
       lastImport = node;
     },
 
@@ -795,6 +799,7 @@ function rezact(): PluginOption {
       if (id.includes("signals.ts")) return;
       if (id.includes("mapState.ts")) return;
       src = _src;
+      itemsAlreadyImported = [];
       magicString = null;
       lastImport = { end: 0 };
       importsUsed = {};
@@ -812,19 +817,27 @@ function rezact(): PluginOption {
       compileRezact(ast);
       if (src.includes("xCreateElement")) importsUsed.xCreateElement = true;
       if (src.includes("xFragment")) importsUsed.xFragment = true;
-      const importsUsedArr = Object.keys(importsUsed);
+      const importsUsedArr = Object.keys(importsUsed).filter(
+        (i) => !itemsAlreadyImported.includes(i)
+      );
+
       if (importsUsedArr.length > 0)
         magicString.prepend(
           `import {${importsUsedArr.join(",")}} from "rezact"\n`
         );
 
-      const signalsUsedArr = Object.keys(signalsUsed);
+      const signalsUsedArr = Object.keys(signalsUsed).filter(
+        (i) => !itemsAlreadyImported.includes(i)
+      );
+
       if (signalsUsedArr.length > 0)
         magicString.prepend(
           `import {${signalsUsedArr.join(",")}} from "rezact/signals"\n`
         );
 
-      const mapStateUsedArr = Object.keys(mapStateUsed);
+      const mapStateUsedArr = Object.keys(mapStateUsed).filter(
+        (i) => !itemsAlreadyImported.includes(i)
+      );
       if (mapStateUsedArr.length > 0)
         magicString.prepend(
           `import {${mapStateUsedArr.join(",")}} from "rezact/mapState"\n`


Test patch:
diff --git a/src/examples/EscapeHatches/TestManual.tsx b/src/examples/EscapeHatches/TestManual.tsx
index 711b7e8..5bafa1a 100644
--- a/src/examples/EscapeHatches/TestManual.tsx
+++ b/src/examples/EscapeHatches/TestManual.tsx
@@ -2,6 +2,22 @@ import { Signal, effect } from "rezact/signals";
 import { MapSignal, mapEffect } from "rezact/mapState";
 
 export function TestManual() {
+  let $count = 10;
+  let count = $count as unknown as Signal<typeof $count>;
+  console.log(count, $count);
+
+  let $testArr = [
+    { name: "Jack", age: 20 },
+    { name: "Jill", age: 30 },
+    { name: "John", age: 40 },
+    { name: "Jane", age: 50 },
+  ];
+
+  let testArr = $testArr as unknown as MapSignal<(typeof $testArr)[0]>;
+
+  testArr.push({ name: "Jesen", age: 30 });
+  testArr.map((item) => console.log(item));
+
   let name = new Signal("jesen");
   let width = new Signal(10);
   let height = new Signal(20);