Skip to content

Commit 368bcd4

Browse files
committed
refactor: better signal registry with global
1 parent 60a0ed9 commit 368bcd4

15 files changed

Lines changed: 169 additions & 147 deletions

File tree

packages/async-framework/component/context-registry.ts

Lines changed: 0 additions & 51 deletions
This file was deleted.

packages/async-framework/component/context.ts

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,6 @@
1-
import type { Signal } from "../signals/signals.ts";
2-
import { signalRegistry } from "../signals/registry.ts";
3-
import { contextRegistry } from "./context-registry.ts";
4-
5-
// Why: Manages component context and hook state
6-
export interface ComponentContext {
7-
id: string;
8-
hooks: any[];
9-
hookIndex: number;
10-
signals: Set<Signal<any>>;
11-
cleanup: Set<() => void>;
12-
mounted: boolean;
13-
element: HTMLElement | null;
14-
parent?: ComponentContext | null;
15-
}
16-
17-
const contextStack: ComponentContext[] = [];
18-
let currentContext: ComponentContext | null = null;
1+
import { signalRegistry } from "../signals/instance.ts";
2+
import { contextRegistry, contextStack } from "../context/instance.ts";
3+
import type { ComponentContext } from "../context/types.ts";
194

205
// Why: Generates unique IDs for components and signals
216
export function generateId(type: string, parentId?: string): string {
@@ -24,7 +9,7 @@ export function generateId(type: string, parentId?: string): string {
249

2510
// Why: Manages the current component context
2611
export function getCurrentContext(): ComponentContext | null {
27-
return currentContext;
12+
return contextStack.peek() as ComponentContext | null;
2813
}
2914

3015
// Why: Creates and pushes a new component context
@@ -33,6 +18,7 @@ export function pushContext(
3318
parentContext?: ComponentContext,
3419
): ComponentContext {
3520
const context: ComponentContext = {
21+
type: "component",
3622
id: generateId("component", parentContext?.id),
3723
hooks: [],
3824
hookIndex: 0,
@@ -44,16 +30,12 @@ export function pushContext(
4430
};
4531

4632
contextStack.push(context);
47-
currentContext = context;
4833
return context;
4934
}
5035

5136
// Why: Pops and cleans up the current context
5237
export function popContext(): void {
53-
if (currentContext) {
54-
contextStack.pop();
55-
currentContext = contextStack[contextStack.length - 1] || null;
56-
}
38+
contextStack.pop();
5739
}
5840

5941
// Why: Runs cleanup functions for a context
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from "./registry.ts";
2+
export * from "./instance.ts";
3+
4+
export * from "./stack.ts";
5+
export * from "./types.ts";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { ContextRegistry } from "./registry.ts";
2+
import { ContextStack } from "./stack.ts";
3+
export const contextStack = ContextStack.getInstance();
4+
export const contextRegistry = ContextRegistry.getInstance();

packages/async-framework/context/registry.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,17 @@ export class ContextRegistry {
4343

4444
// Build the full context path
4545
const parts: string[] = [];
46-
46+
4747
// Add parent path if it exists
4848
if (parentId) {
4949
parts.push(parentId);
5050
}
51-
51+
5252
// Add this context's ID
5353
parts.push(`${type}-${count}`);
5454

5555
// Join with dots to create hierarchical ID
56-
return parts.join('.');
56+
return parts.join(".");
5757
}
5858

5959
// Why: Store and retrieve contexts
@@ -72,5 +72,3 @@ export class ContextRegistry {
7272
this.contextTypes.forEach((type) => this.counters.set(type, 0));
7373
}
7474
}
75-
76-
export const contextRegistry = ContextRegistry.getInstance();

packages/async-framework/context/stack.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,3 @@ export class ContextStack {
2828
this.stack = [];
2929
}
3030
}
31-
32-
export const contextStack = ContextStack.getInstance();

packages/async-framework/context/types.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,16 @@ export interface ComponentContext extends BaseContext {
1414
element: HTMLElement | null;
1515
}
1616

17-
export interface SignalContext extends BaseContext {
18-
type: "signal";
17+
// Create a base value context type
18+
export interface ValueContext extends BaseContext {
1919
value: any;
2020
}
2121

22-
export interface ComputedContext extends SignalContext {
22+
export interface SignalContext extends ValueContext {
23+
type: "signal";
24+
}
25+
26+
export interface ComputedContext extends ValueContext {
2327
type: "computed";
2428
dependencies: Set<string>;
2529
}

packages/async-framework/hooks/index.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
import { signal } from "../signals/signals.ts";
22
import { getCurrentContext } from "../component/context.ts";
3+
import { contextRegistry } from "../context/registry.ts";
4+
import type { ComponentContext } from "../context/types.ts";
35

46
// Why: Implements useState hook with signal integration
57
export function useState<T>(initialValue: T): [T, (value: T) => void] {
68
const context = getCurrentContext();
79
if (!context) throw new Error("useState must be called within a component");
810

11+
// Register hook in context registry
12+
const hookId = contextRegistry.generateId("hook", context.id);
913
const hookIndex = context.hookIndex++;
1014

1115
if (!context.hooks[hookIndex]) {
1216
const sig = signal(initialValue);
13-
context.hooks[hookIndex] = sig;
17+
context.hooks[hookIndex] = { id: hookId, signal: sig };
1418
context.signals.add(sig);
19+
20+
// Register cleanup in context
21+
context.cleanup.add(() => {
22+
context.signals.delete(sig);
23+
});
1524
}
1625

17-
const sig = context.hooks[hookIndex];
26+
const { signal: sig } = context.hooks[hookIndex];
1827
return [sig.value, (value: T) => sig.set(value)];
1928
}
2029

@@ -23,9 +32,10 @@ export function useEffect(
2332
effect: () => void | (() => void),
2433
deps?: any[],
2534
): void {
26-
const context = getCurrentContext();
35+
const context = getCurrentContext() as ComponentContext;
2736
if (!context) throw new Error("useEffect must be called within a component");
2837

38+
const hookId = contextRegistry.generateId("hook", context.id);
2939
const hookIndex = context.hookIndex++;
3040
const oldDeps = context.hooks[hookIndex];
3141

@@ -43,9 +53,17 @@ export function useEffect(
4353
const cleanup = effect();
4454
if (typeof cleanup === "function") {
4555
context.cleanup.add(cleanup);
56+
57+
// Register in context registry
58+
contextRegistry.setContext(hookId, {
59+
type: "hook",
60+
id: hookId,
61+
cleanup: new Set([cleanup]),
62+
parent: context,
63+
});
4664
}
4765

48-
context.hooks[hookIndex] = { deps, cleanup };
66+
context.hooks[hookIndex] = { id: hookId, deps, cleanup };
4967
}
5068
}
5169

packages/async-framework/jsx-runtime.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
type JSXChild,
55
renderComponent,
66
} from "./component/render.ts";
7+
import { contextRegistry } from "../context/registry.ts";
8+
import { GlobalContext } from "../context/types.ts";
79

810
// Define types for JSX elements and children
911
type Signal<T> = {
@@ -25,7 +27,7 @@ type JSXAttributes = Record<
2527
string,
2628
string | number | boolean | Signal<any> | ReadSignal<any> | undefined
2729
>;
28-
type JSXElement = HTMLElement | DocumentFragment;
30+
type JSXElement = HTMLElement | Element | DocumentFragment;
2931
type Component = (props: any) => JSXElement | Signal<any>;
3032

3133
// Why: Provides JSX runtime support with context awareness

packages/async-framework/loader/loader.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// deno-lint-ignore-file no-explicit-any
22
import { escapeSelector, isPromise } from "../utils.ts";
33
import type { Signal } from "../signals/signals.ts";
4+
import { SignalRegistry } from "../signals/registry.ts";
45

56
export interface AsyncLoaderContext<T = any, M = any> {
67
value: T | undefined | null | Promise<T>;
@@ -111,7 +112,7 @@ export class AsyncLoader {
111112
this.config = config;
112113
this.context = config.context || {};
113114
this.handlerRegistry = config.handlerRegistry;
114-
this.signalRegistry = config.signalRegistry;
115+
this.signalRegistry = config.signalRegistry || SignalRegistry.getInstance();
115116
this.templateRegistry = config.templateRegistry;
116117
this.eventPrefix = config.eventPrefix || "on:";
117118
this.containerAttribute = config.containerAttribute || "data-container";

0 commit comments

Comments
 (0)