@sprlab/wccompiler 0.2.1 → 0.3.0

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Template string containing the mini reactive runtime (~40 lines).
2
+ * Template string containing the mini reactive runtime.
3
3
  * This gets inlined at the top of each compiled component so the output
4
4
  * is fully self-contained with zero imports.
5
5
  *
@@ -7,12 +7,16 @@
7
7
  * - __signal(initialValue): getter/setter function with subscriber tracking
8
8
  * - __computed(fn): cached derived value that auto-invalidates
9
9
  * - __effect(fn): runs fn immediately and re-runs when dependencies change
10
+ * - __batch(fn): batch multiple signal writes, flush effects once at the end
10
11
  *
11
12
  * Dependency tracking uses a global stack (__currentEffect).
13
+ * Batching uses a depth counter — nested batches are supported.
12
14
  */
13
15
  /** @type {string} */
14
16
  export const reactiveRuntime = `
15
17
  let __currentEffect = null;
18
+ let __batchDepth = 0;
19
+ const __pendingEffects = [];
16
20
 
17
21
  function __signal(initial) {
18
22
  let _value = initial;
@@ -25,7 +29,13 @@ function __signal(initial) {
25
29
  const old = _value;
26
30
  _value = args[0];
27
31
  if (old !== _value) {
28
- for (const fn of [..._subs]) fn();
32
+ if (__batchDepth > 0) {
33
+ for (const fn of _subs) {
34
+ if (!__pendingEffects.includes(fn)) __pendingEffects.push(fn);
35
+ }
36
+ } else {
37
+ for (const fn of [..._subs]) fn();
38
+ }
29
39
  }
30
40
  };
31
41
  }
@@ -35,7 +45,13 @@ function __computed(fn) {
35
45
  const _subs = new Set();
36
46
  const recompute = () => {
37
47
  _dirty = true;
38
- for (const fn of [..._subs]) fn();
48
+ if (__batchDepth > 0) {
49
+ for (const fn of _subs) {
50
+ if (!__pendingEffects.includes(fn)) __pendingEffects.push(fn);
51
+ }
52
+ } else {
53
+ for (const fn of [..._subs]) fn();
54
+ }
39
55
  };
40
56
  return () => {
41
57
  if (__currentEffect) _subs.add(__currentEffect);
@@ -51,12 +67,27 @@ function __computed(fn) {
51
67
  }
52
68
 
53
69
  function __effect(fn) {
70
+ let _cleanup = null;
54
71
  const run = () => {
72
+ if (typeof _cleanup === 'function') _cleanup();
55
73
  const prev = __currentEffect;
56
74
  __currentEffect = run;
57
- fn();
75
+ _cleanup = fn();
58
76
  __currentEffect = prev;
59
77
  };
60
78
  run();
61
79
  }
80
+
81
+ function __batch(fn) {
82
+ __batchDepth++;
83
+ try {
84
+ fn();
85
+ } finally {
86
+ __batchDepth--;
87
+ if (__batchDepth === 0) {
88
+ const pending = __pendingEffects.splice(0);
89
+ for (const f of pending) f();
90
+ }
91
+ }
92
+ }
62
93
  `;
package/lib/types.js CHANGED
@@ -26,6 +26,15 @@
26
26
  /**
27
27
  * @typedef {Object} LifecycleHook
28
28
  * @property {string} body — The callback body (JavaScript code)
29
+ * @property {boolean} async — Whether the callback is async
30
+ */
31
+
32
+ /**
33
+ * @typedef {Object} WatcherDef
34
+ * @property {string} target — Signal/prop/computed name to watch (e.g., 'count')
35
+ * @property {string} newParam — Parameter name for new value (e.g., 'newVal')
36
+ * @property {string} oldParam — Parameter name for old value (e.g., 'oldVal')
37
+ * @property {string} body — Callback body
29
38
  */
30
39
 
31
40
  /**
@@ -68,6 +77,7 @@
68
77
  * @property {ComputedDef[]} computeds — computed() declarations
69
78
  * @property {EffectDef[]} effects — effect() declarations
70
79
  * @property {ConstantVar[]} constantVars — Plain const declarations (non-reactive)
80
+ * @property {WatcherDef[]} watchers — watch() declarations
71
81
  * @property {MethodDef[]} methods — function declarations
72
82
  * @property {PropDef[]} propDefs — Prop definitions with names and defaults
73
83
  * @property {string|null} propsObjectName — Variable name from `const X = defineProps(...)`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sprlab/wccompiler",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Zero-runtime compiler that transforms .ts/.js component files into native web components with signals-based reactivity",
5
5
  "type": "module",
6
6
  "bin": {
package/types/wcc.d.ts CHANGED
@@ -7,6 +7,7 @@ declare module 'wcc' {
7
7
  export function signal<T>(value: T): Signal<T>;
8
8
  export function computed<T>(fn: () => T): () => T;
9
9
  export function effect(fn: () => void): void;
10
+ export function watch<T>(target: string, fn: (newVal: T, oldVal: T) => void): void;
10
11
  export function defineComponent(options: {
11
12
  tag: string;
12
13
  template: string;
@@ -21,7 +22,7 @@ declare module 'wcc' {
21
22
 
22
23
  export function templateRef(name: string): { value: HTMLElement | null };
23
24
 
24
- export function onMount(fn: () => void): void;
25
- export function onDestroy(fn: () => void): void;
25
+ export function onMount(fn: () => void | Promise<void>): void;
26
+ export function onDestroy(fn: () => void | Promise<void>): void;
26
27
  export function templateBindings(bindings: Record<string, any>): void;
27
28
  }