Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions mangle.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,7 @@
},
"minify": {
"mangle": {
"reserved": [
"useSignal",
"useComputed",
"useSignalEffect"
],
"reserved": ["useSignal", "useComputed", "useSignalEffect"],
"keep_classnames": true,
"properties": {
"regex": "^_[^_]",
Expand All @@ -25,7 +21,8 @@
"compress": {
"conditionals": false,
"loops": false,
"sequences": false
"sequences": false,
"unsafe": true
}
},
"props": {
Expand Down
71 changes: 29 additions & 42 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
function cycleDetected(): never {
throw new Error("Cycle detected");
}
function mutationDetected(): never {
throw new Error("Computed cannot have side-effects");
}

const identifier = Symbol.for("preact-signals");

// Flags for Computed and Effect.
Expand Down Expand Up @@ -259,13 +252,15 @@ function Signal(this: Signal, value?: unknown) {
this._targets = undefined;
}

Signal.prototype.brand = identifier;
const signalProto = Signal.prototype;

Signal.prototype._refresh = function () {
signalProto.brand = identifier;

signalProto._refresh = function () {
return true;
};

Signal.prototype._subscribe = function (node) {
signalProto._subscribe = function (node) {
if (this._targets !== node && node._prevTarget === undefined) {
node._nextTarget = this._targets;
if (this._targets !== undefined) {
Expand All @@ -275,7 +270,7 @@ Signal.prototype._subscribe = function (node) {
}
};

Signal.prototype._unsubscribe = function (node) {
signalProto._unsubscribe = function (node) {
// Only run the unsubscribe step if the signal has any subscribers to begin with.
if (this._targets !== undefined) {
const prev = node._prevTarget;
Expand All @@ -294,7 +289,7 @@ Signal.prototype._unsubscribe = function (node) {
}
};

Signal.prototype.subscribe = function (fn) {
signalProto.subscribe = function (fn) {
const signal = this;
return effect(function (this: Effect) {
const value = signal.value;
Expand All @@ -308,23 +303,23 @@ Signal.prototype.subscribe = function (fn) {
});
};

Signal.prototype.valueOf = function () {
signalProto.valueOf = function () {
return this.value;
};

Signal.prototype.toString = function () {
return this.value + "";
signalProto.toString = function () {
return "" + this.value;
};

Signal.prototype.toJSON = function () {
signalProto.toJSON = function () {
return this.value;
};

Signal.prototype.peek = function () {
signalProto.peek = function () {
return this._value;
};

Object.defineProperty(Signal.prototype, "value", {
Object.defineProperty(signalProto, "value", {
get() {
const node = addDependency(this);
if (node !== undefined) {
Expand All @@ -334,12 +329,12 @@ Object.defineProperty(Signal.prototype, "value", {
},
set(this: Signal, value) {
if (evalContext instanceof Computed) {
mutationDetected();
throw Error("Computed side effect detected");
}

if (value !== this._value) {
if (batchIteration > 100) {
cycleDetected();
throw Error("Cycle detected");
}

this._value = value;
Expand Down Expand Up @@ -490,17 +485,17 @@ declare class Computed<T = any> extends Signal<T> {
}

function Computed(this: Computed, compute: () => unknown) {
Signal.call(this, undefined);
Signal.call(this);

this._compute = compute;
this._sources = undefined;
this._globalVersion = globalVersion - 1;
this._flags = OUTDATED;
}

Computed.prototype = new Signal() as Computed;
const computedProto = (Computed.prototype = new Signal() as Computed);

Computed.prototype._refresh = function () {
computedProto._refresh = function () {
this._flags &= ~NOTIFIED;

if (this._flags & RUNNING) {
Expand Down Expand Up @@ -553,7 +548,7 @@ Computed.prototype._refresh = function () {
return true;
};

Computed.prototype._subscribe = function (node) {
computedProto._subscribe = function (node) {
if (this._targets === undefined) {
this._flags |= OUTDATED | TRACKING;

Expand All @@ -567,13 +562,13 @@ Computed.prototype._subscribe = function (node) {
node._source._subscribe(node);
}
}
Signal.prototype._subscribe.call(this, node);
signalProto._subscribe.call(this, node);
};

Computed.prototype._unsubscribe = function (node) {
computedProto._unsubscribe = function (node) {
// Only run the unsubscribe step if the computed signal has any subscribers.
if (this._targets !== undefined) {
Signal.prototype._unsubscribe.call(this, node);
signalProto._unsubscribe.call(this, node);

// Computed signal unsubscribes from its dependencies when it loses its last subscriber.
// This makes it possible for unreferences subgraphs of computed signals to get garbage collected.
Expand All @@ -591,7 +586,7 @@ Computed.prototype._unsubscribe = function (node) {
}
};

Computed.prototype._notify = function () {
computedProto._notify = function () {
if (!(this._flags & NOTIFIED)) {
this._flags |= OUTDATED | NOTIFIED;

Expand All @@ -605,20 +600,20 @@ Computed.prototype._notify = function () {
}
};

Computed.prototype.peek = function () {
computedProto.peek = function () {
if (!this._refresh()) {
cycleDetected();
throw Error("Cycle detected");
}
if (this._flags & HAS_ERROR) {
throw this._value;
}
return this._value;
};

Object.defineProperty(Computed.prototype, "value", {
Object.defineProperty(computedProto, "value", {
get() {
if (this._flags & RUNNING) {
cycleDetected();
throw Error("Cycle detected");
}
const node = addDependency(this);
this._refresh();
Expand Down Expand Up @@ -733,7 +728,7 @@ Effect.prototype._callback = function () {

Effect.prototype._start = function () {
if (this._flags & RUNNING) {
cycleDetected();
throw Error("Cycle detected");
}
this._flags |= RUNNING;
this._flags &= ~DISPOSED;
Expand Down Expand Up @@ -775,12 +770,4 @@ function effect(compute: () => unknown | EffectCleanup): () => void {
return effect._dispose.bind(effect);
}

export {
signal,
computed,
effect,
batch,
Signal,
ReadonlySignal,
untracked,
};
export { signal, computed, effect, batch, Signal, ReadonlySignal, untracked };