Created
January 14, 2026 17:36
-
-
Save Himujjal/97ce4ab99b2482bcfdb7400a2cb74ea6 to your computer and use it in GitHub Desktop.
Yukti is a minimal version of SolidJS in one single TypeScript file without any dependencies.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Yukti Framework v1.0.0 | |
| * Minimal reactive core with proper TypeScript support | |
| */ | |
| // ========================================== | |
| // 1. Core Types & Interfaces | |
| // ========================================== | |
| export type Accessor<T> = () => T; | |
| export type Setter<T> = (value: T | ((prev: T) => T)) => T; | |
| export type Signal<T> = [Accessor<T>, Setter<T>]; | |
| export type EffectFn = () => void | CleanupFn; | |
| export type CleanupFn = () => void; | |
| export type Component<P = {}> = (props: P) => Renderable; | |
| export type Renderable = VNode | VNode[] | string | number | boolean | null | undefined; | |
| interface VNodeBase { | |
| type: string | Component<any>; | |
| props: Record<string, any>; | |
| children?: Renderable[]; | |
| key?: string | number; | |
| } | |
| interface ElementVNode extends VNodeBase { | |
| type: string; | |
| $$typeof: typeof ELEMENT_TYPE; | |
| } | |
| interface ComponentVNode extends VNodeBase { | |
| type: Component<any>; | |
| $$typeof: typeof COMPONENT_TYPE; | |
| result?: Renderable; | |
| owner?: Owner; | |
| } | |
| export type VNode = ElementVNode | ComponentVNode; | |
| interface Effect { | |
| fn: EffectFn; | |
| dependencies: Set<Set<() => void>>; | |
| cleanup?: CleanupFn; | |
| execute: () => void; | |
| } | |
| interface Owner { | |
| owner: Owner | null; | |
| cleanups: Set<CleanupFn>; | |
| mounts: Set<() => void>; | |
| mounted: boolean; | |
| context?: Map<symbol, unknown>; | |
| } | |
| interface Context<T> { | |
| id: symbol; | |
| defaultValue: T; | |
| } | |
| // ========================================== | |
| // 2. Constants | |
| // ========================================== | |
| const ELEMENT_TYPE = Symbol.for('yukti.element'); | |
| const COMPONENT_TYPE = Symbol.for('yukti.component'); | |
| // ========================================== | |
| // 3. Global State | |
| // ========================================== | |
| let currentOwner: Owner | null = null; | |
| let currentEffect: Effect | null = null; | |
| let pendingEffects = new Set<() => void>(); | |
| let isBatching = false; | |
| // ========================================== | |
| // 4. Reactive State Management | |
| // ========================================== | |
| export function createSignal<T>(initialValue: T): Signal<T> { | |
| let value = initialValue; | |
| const subscribers = new Set<() => void>(); | |
| const get: Accessor<T> = () => { | |
| if (currentEffect) { | |
| subscribers.add(currentEffect.execute); | |
| currentEffect.dependencies.add(subscribers); | |
| } | |
| return value; | |
| }; | |
| const set: Setter<T> = (newValue) => { | |
| const nextValue = typeof newValue === 'function' | |
| ? (newValue as (prev: T) => T)(value) | |
| : newValue; | |
| if (!Object.is(nextValue, value)) { | |
| value = nextValue; | |
| if (!isBatching) { | |
| subscribers.forEach(sub => sub()); | |
| } else { | |
| subscribers.forEach(sub => pendingEffects.add(sub)); | |
| } | |
| } | |
| return value; | |
| }; | |
| return [get, set]; | |
| } | |
| export function createMemo<T>(fn: () => T): Accessor<T> { | |
| const [signal, setSignal] = createSignal<T>(undefined as any); | |
| createEffect(() => { | |
| const value = fn(); | |
| setSignal(() => value); | |
| }); | |
| return signal; | |
| } | |
| // ========================================== | |
| // 5. Effects & Lifecycle | |
| // ========================================== | |
| export function createEffect(fn: EffectFn): () => void { | |
| const effect: Effect = { | |
| fn, | |
| dependencies: new Set(), | |
| cleanup: undefined, | |
| execute: () => { | |
| effect.dependencies.forEach(signalSubscribers => { | |
| signalSubscribers.delete(effect.execute); | |
| }); | |
| effect.dependencies.clear(); | |
| if (effect.cleanup) { | |
| effect.cleanup(); | |
| effect.cleanup = undefined; | |
| } | |
| const prevEffect = currentEffect; | |
| const prevOwner = currentOwner; | |
| const effectOwner: Owner = { | |
| owner: prevOwner, | |
| cleanups: new Set(), | |
| mounts: new Set(), | |
| mounted: false | |
| }; | |
| currentEffect = effect; | |
| currentOwner = effectOwner; | |
| try { | |
| const result = fn(); | |
| if (typeof result === 'function') { | |
| effect.cleanup = result; | |
| onCleanup(result); | |
| } | |
| } finally { | |
| currentEffect = prevEffect; | |
| currentOwner = prevOwner; | |
| } | |
| } | |
| }; | |
| effect.execute(); | |
| return () => { | |
| effect.dependencies.forEach(signalSubscribers => { | |
| signalSubscribers.delete(effect.execute); | |
| }); | |
| if (effect.cleanup) effect.cleanup(); | |
| }; | |
| } | |
| export function batch<T>(fn: () => T): T { | |
| isBatching = true; | |
| try { | |
| return fn(); | |
| } finally { | |
| isBatching = false; | |
| flushEffects(); | |
| } | |
| } | |
| function flushEffects(): void { | |
| const effects = Array.from(pendingEffects); | |
| pendingEffects.clear(); | |
| effects.forEach(effect => effect()); | |
| } | |
| export function onCleanup(fn: CleanupFn): void { | |
| if (currentOwner) { | |
| currentOwner.cleanups.add(fn); | |
| } | |
| } | |
| export function onMount(fn: () => void): void { | |
| if (currentOwner) { | |
| if (!currentOwner.mounted) { | |
| currentOwner.mounts.add(fn); | |
| } else { | |
| fn(); | |
| } | |
| } | |
| } | |
| // ========================================== | |
| // 6. Context API | |
| // ========================================== | |
| export function createContext<T>(defaultValue: T): Context<T> { | |
| return { | |
| id: Symbol('yukti.context'), | |
| defaultValue | |
| }; | |
| } | |
| export function createContextProvider<T>( | |
| context: Context<T>, | |
| value: T, | |
| children: Renderable | (() => Renderable) | |
| ): Renderable { | |
| const providerOwner: Owner = { | |
| owner: currentOwner, | |
| cleanups: new Set(), | |
| mounts: new Set(), | |
| mounted: false, | |
| context: new Map(currentOwner?.context) | |
| }; | |
| providerOwner.context!.set(context.id, value); | |
| const prevOwner = currentOwner; | |
| currentOwner = providerOwner; | |
| try { | |
| const result = typeof children === 'function' ? children() : children; | |
| if (!providerOwner.mounted) { | |
| providerOwner.mounted = true; | |
| providerOwner.mounts.forEach(cb => cb()); | |
| providerOwner.mounts.clear(); | |
| } | |
| return result; | |
| } finally { | |
| currentOwner = prevOwner; | |
| } | |
| } | |
| export function useContext<T>(context: Context<T>): T { | |
| let owner = currentOwner; | |
| while (owner) { | |
| if (owner.context && owner.context.has(context.id)) { | |
| return owner.context.get(context.id) as T; | |
| } | |
| owner = owner.owner; | |
| } | |
| return context.defaultValue; | |
| } | |
| // ========================================== | |
| // 7. Component & JSX Factory | |
| // ========================================== | |
| function normalizeChildren(children: unknown[]): Renderable[] { | |
| const result: Renderable[] = []; | |
| for (const child of children) { | |
| if (child == null || child === false || child === true) { | |
| continue; | |
| } | |
| if (Array.isArray(child)) { | |
| result.push(...normalizeChildren(child)); | |
| } else { | |
| result.push(child as Renderable); | |
| } | |
| } | |
| return result; | |
| } | |
| export function h( | |
| type: string | Component<any>, | |
| props?: Record<string, unknown> | null, | |
| ...children: unknown[] | |
| ): VNode { | |
| const finalProps: Record<string, unknown> = props ? { ...props } : {}; | |
| const normalizedChildren = normalizeChildren(children); | |
| if (normalizedChildren.length > 0) { | |
| finalProps.children = normalizedChildren.length === 1 | |
| ? normalizedChildren[0] | |
| : normalizedChildren; | |
| } | |
| if (typeof type === 'function') { | |
| const componentOwner: Owner = { | |
| owner: currentOwner, | |
| cleanups: new Set(), | |
| mounts: new Set(), | |
| mounted: false | |
| }; | |
| const prevOwner = currentOwner; | |
| currentOwner = componentOwner; | |
| let result: Renderable; | |
| try { | |
| result = type(finalProps); | |
| } finally { | |
| currentOwner = prevOwner; | |
| } | |
| const vnode: ComponentVNode = { | |
| $$typeof: COMPONENT_TYPE, | |
| type, | |
| props: finalProps, | |
| result, | |
| owner: componentOwner | |
| }; | |
| return vnode; | |
| } | |
| const vnode: ElementVNode = { | |
| $$typeof: ELEMENT_TYPE, | |
| type, | |
| props: finalProps, | |
| children: normalizedChildren | |
| }; | |
| return vnode; | |
| } | |
| // ========================================== | |
| // 8. Error Boundary Component | |
| // ========================================== | |
| export interface ErrorBoundaryProps { | |
| fallback: Component<{ error: Error }> | Renderable; | |
| children: Renderable | (() => Renderable); | |
| } | |
| export function ErrorBoundary({ fallback, children }: ErrorBoundaryProps): Renderable { | |
| const [error, setError] = createSignal<Error | null>(null); | |
| try { | |
| const content = typeof children === 'function' ? children() : children; | |
| if (error()) { | |
| return typeof fallback === 'function' | |
| ? fallback({ error: error()! }) | |
| : fallback; | |
| } | |
| return content; | |
| } catch (err) { | |
| setError(err as Error); | |
| return typeof fallback === 'function' | |
| ? fallback({ error: err as Error }) | |
| : fallback; | |
| } | |
| } | |
| // ========================================== | |
| // 9. Core Export | |
| // ========================================== | |
| export const Yukti = { | |
| // Core reactivity | |
| createSignal, | |
| createEffect, | |
| createMemo, | |
| batch, | |
| // Lifecycle | |
| onCleanup, | |
| onMount, | |
| // Context | |
| createContext, | |
| createContextProvider, | |
| useContext, | |
| // Components & JSX | |
| h, | |
| ErrorBoundary, | |
| } as const; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment