This commit is contained in:
Nicolas Bayet
2025-10-02 15:22:13 +02:00
parent a309ec498b
commit d0cdc489b1
4 changed files with 96 additions and 93 deletions
+12 -7
View File
@@ -1,10 +1,11 @@
export type ExecutionContext = {
onReadAtom: (atom: Atom) => void;
unsubcribe?: (scheduledContexts: Set<ExecutionContext>) => void;
update: Function;
atoms: Set<Atom>;
update?: Function;
atoms?: Set<Atom>;
meta?: any;
// getParent: () => ExecutionContext | undefined;
// getChildren: () => ExecutionContext[];
meta: any;
// schedule: () => void;
};
@@ -15,9 +16,13 @@ export type customDirectives = Record<
export type Atom = {
executionContexts: Set<ExecutionContext>;
// dependents: Set<Atom>;
dependents: Set<DerivedAtom>;
getValue: () => any;
};
// export type DerivedAtom = Atom & {
// dependencies: Set<Atom>;
// };
export type OldValue = any;
export type DerivedAtom = Atom & {
dependencies: Map<Atom, OldValue>;
computed: boolean;
};
+19 -22
View File
@@ -1,13 +1,12 @@
import { OwlError } from "../common/owl_error";
import { ExecutionContext } from "../common/types";
import { Atom, ExecutionContext } from "../common/types";
import type { App, Env } from "./app";
import { BDom, VNode } from "./blockdom";
import { makeTaskContext, TaskContext } from "./cancellableContext";
import { Component, ComponentConstructor, Props } from "./component";
import { fibersInError } from "./error_handling";
import { makeExecutionContext } from "./executionContext";
import { Fiber, makeChildFiber, makeRootFiber, MountFiber, MountOptions } from "./fibers";
import { reactive, targets, withoutReactivity } from "./reactivity";
import { addAtomToContext, reactive, targets, withoutReactivity } from "./reactivity";
import { STATUS } from "./status";
let currentNode: ComponentNode | null = null;
@@ -106,16 +105,14 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
this.props = props;
this.parentKey = parentKey;
this.taskContext = makeTaskContext();
this.executionContext = makeExecutionContext({
this.executionContext = {
meta: this,
update: () => {
this.render(false);
},
getParent: () => this.parent?.executionContext,
getChildren: () => {
return Object.values(this.children).map((c) => c.executionContext);
},
meta: this,
});
onReadAtom: (atom: Atom) => addAtomToContext(atom, this.executionContext),
atoms: new Set<Atom>(),
};
const defaultProps = C.defaultProps;
props = Object.assign({}, props);
if (defaultProps) {
@@ -123,12 +120,12 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
}
const env = (parent && parent.childEnv) || app.env;
this.childEnv = env;
for (const key in props) {
const prop = props[key];
if (prop && typeof prop === "object" && targets.has(prop)) {
props[key] = useState(prop);
}
}
// for (const key in props) {
// const prop = props[key];
// if (prop && typeof prop === "object" && targets.has(prop)) {
// props[key] = useState(prop);
// }
// }
this.component = new C(props, env, this);
const ctx = Object.assign(Object.create(this.component), { this: this.component });
this.renderFn = app.getTemplate(C.template).bind(this.component, ctx, this);
@@ -271,12 +268,12 @@ export class ComponentNode<P extends Props = any, E = any> implements VNode<Comp
}
currentNode = this;
for (const key in props) {
const prop = props[key];
if (prop && typeof prop === "object" && targets.has(prop)) {
props[key] = useState(prop);
}
}
// for (const key in props) {
// const prop = props[key];
// if (prop && typeof prop === "object" && targets.has(prop)) {
// props[key] = useState(prop);
// }
// }
currentNode = null;
let prom: Promise<any[]>;
withoutReactivity(() => {
+8 -29
View File
@@ -8,27 +8,6 @@ export function getExecutionContext() {
return executionContexts[executionContexts.length - 1];
}
export function makeExecutionContext({
update,
// getParent,
// getChildren,
meta,
}: {
update: () => void;
// getParent?: () => ExecutionContext | undefined;
// getChildren?: () => ExecutionContext[];
meta?: any;
}) {
const executionContext: ExecutionContext = {
update,
// getParent: getParent!,
// getChildren: getChildren!,
atoms: new Set(),
meta: meta || {},
};
return executionContext;
}
export function pushExecutionContext(context: ExecutionContext) {
executionContexts.push(context);
}
@@ -37,11 +16,11 @@ export function popExecutionContext() {
executionContexts.pop();
}
export function makeExecutionContext({ update, meta }: { update: () => void; meta?: any }) {
const executionContext: ExecutionContext = {
update,
atoms: new Set(),
meta: meta || {},
};
return executionContext;
}
// export function makeExecutionContext({ update, meta }: { update: () => void; meta?: any }) {
// const executionContext: ExecutionContext = {
// update,
// atoms: new Set(),
// meta: meta || {},
// };
// return executionContext;
// }
+57 -35
View File
@@ -1,5 +1,5 @@
import { OwlError } from "../common/owl_error";
import { ExecutionContext, Atom } from "../common/types";
import { ExecutionContext, Atom, DerivedAtom, OldValue } from "../common/types";
import { getExecutionContext, popExecutionContext, pushExecutionContext } from "./executionContext";
// Special key to subscribe to, to be notified of key creation/deletion
@@ -80,10 +80,11 @@ export function toRaw<T extends Target, U extends Reactive<T>>(value: U | T): T
const targetToKeysToAtomItem = new WeakMap<Target, Map<PropertyKey, Atom>>();
const scheduledAtoms = new Set<Atom>();
function makeAtom() {
function makeAtom(getValue: () => any): Atom {
const atom: Atom = {
executionContexts: new Set<ExecutionContext>(),
// dependents: new Set<Atom>(),
dependents: new Set<Atom>(),
// getValue,
};
return atom;
}
@@ -96,12 +97,17 @@ function getTargetKeyAtom(target: Target, key: PropertyKey): Atom {
}
let atom = keyToAtomItem.get(key)!;
if (!atom) {
atom = makeAtom();
atom = makeAtom(() => Reflect.get(target, key));
keyToAtomItem.set(key, atom);
}
return atom;
}
export function addAtomToContext(atom: Atom, executionContext: ExecutionContext) {
executionContext.atoms.add(atom);
atom.executionContexts.add(executionContext);
}
/**
* Observes a given key on a target with an callback. The callback will be
* called when the given key changes on the target.
@@ -111,15 +117,9 @@ function getTargetKeyAtom(target: Target, key: PropertyKey): Atom {
* or deletion)
* @param callback the function to call when the key changes
*/
function onReadTargetKey(target: Target, key: PropertyKey): void {
function onReadTargetKey(target: Target, key: PropertyKey, receiver: any): void {
const executionContext = getExecutionContext();
if (!executionContext) return;
const atom = getTargetKeyAtom(target, key);
// observerAtoms.add(atom);
executionContext.atoms.add(atom);
atom.executionContexts.add(executionContext);
executionContext?.onReadAtom(getTargetKeyAtom(target, key));
}
let scheduled = false;
@@ -134,7 +134,20 @@ function scheduleAtom(atom: Atom) {
});
}
function processDerivedAtoms() {
const processedAtoms = new Set<Atom>();
for (const atom of scheduledAtoms) {
for (const dep of atom.dependents) {
if (processedAtoms.has(dep)) continue;
dep.computed = false;
processedAtoms.add(dep);
}
}
}
function processAtoms() {
processDerivedAtoms();
const scheduledContexts = new Set(
[...scheduledAtoms.values()].map((s) => [...s.executionContexts]).flat()
);
@@ -154,7 +167,7 @@ function processAtoms() {
for (const context of scheduledContexts) {
pushExecutionContext(context);
try {
context.update();
context.update?.();
} finally {
popExecutionContext();
}
@@ -296,12 +309,7 @@ export function effect(fn: Function) {
unsubscribeChildEffect(executionContext, scheduledContexts);
},
update: fn,
// getParent: () => {
// return executionContext.meta.parent;
// },
// getChildren: () => {
// return executionContext.meta.children || [];
// },
onReadAtom: (atom: Atom) => addAtomToContext(atom, executionContext),
atoms: new Set(),
meta: {
parent: parent,
@@ -320,24 +328,38 @@ export function effect(fn: Function) {
}
}
// const dependentStack: any[][] = [];
export function derived(fn: Function) {
let lastValue: any;
// const derivedDependecies = new Set<Atom>();
// // const derrivedToAtom = new WeakMap<Function, Atom>();
// export function derived(fn: Function) {
// const derivedAtom: DerivedAtom = {
// executionContexts: new Set<ExecutionContext>(),
// // parent: null,
// dependencies: new Set<Atom>(),
// dependents: new Set<Atom>(),
// };
const derivedAtom: DerivedAtom = {
executionContexts: new Set<ExecutionContext>(),
dependents: new Set<Atom>(),
dependencies: new Map<Atom, OldValue>(),
getValue: () => lastValue,
computed: false,
};
// return () => {
// dependentStack.push([]);
// fn();
// dependentStack.pop();
// };
// }
return () => {
const executionContext = getExecutionContext();
executionContext?.onReadAtom(derivedAtom);
if (derivedAtom.computed) return lastValue;
const derivedExecutionContext: ExecutionContext = {
onReadAtom: (atom: Atom) => {
atom.dependents.add(derivedAtom);
// derivedAtom.executionContexts.add(executionContext);
},
};
pushExecutionContext(derivedExecutionContext);
try {
lastValue = fn();
} finally {
popExecutionContext();
}
derivedAtom.computed = true;
return lastValue;
};
}
/**
* Creates a basic proxy handler for regular objects and arrays.