Skip to content

Commit

Permalink
Merge 3ce9959 into 49793e9
Browse files Browse the repository at this point in the history
  • Loading branch information
thetarnav committed Jan 13, 2023
2 parents 49793e9 + 3ce9959 commit 9568266
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 176 deletions.
8 changes: 2 additions & 6 deletions packages/solid/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,12 @@ type JSXElement = JSX.Element;
export type { JSXElement, JSX };

// dev
import { hashValue, registerGraph, serializeGraph, writeSignal } from "./reactive/signal.js";
import { registerGraph, writeSignal } from "./reactive/signal.js";
let DEV: {
writeSignal: typeof writeSignal;
serializeGraph: typeof serializeGraph;
registerGraph: typeof registerGraph;
hashValue: typeof hashValue;
};
if ("_SOLID_DEV_") {
DEV = { writeSignal, serializeGraph, registerGraph, hashValue };
}
if ("_SOLID_DEV_") DEV = { writeSignal, registerGraph };
export { DEV };

// handle multiple instance check
Expand Down
114 changes: 16 additions & 98 deletions packages/solid/src/reactive/signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ let Listener: Computation<any> | null = null;
let Updates: Computation<any>[] | null = null;
let Effects: Computation<any>[] | null = null;
let ExecCount = 0;
let rootCount = 0;

// keep immediately evaluated module code, below its indirect declared let dependencies like Listener
const [transPending, setTransPending] = /*@__PURE__*/ createSignal(false);
Expand All @@ -41,28 +40,27 @@ declare global {

export type ComputationState = 0 | 1 | 2;

export interface SourceMapValue {
export interface SignalMapValue {
value: unknown;
name?: string;
graph?: Owner;
}

export interface SignalState<T> extends SourceMapValue {
export interface SignalState<T> extends SignalMapValue {
value: T;
observers: Computation<any>[] | null;
observerSlots: number[] | null;
tValue?: T;
comparator?: (prev: T, next: T) => boolean;
name?: string;
}

export interface Owner {
owned: Computation<any>[] | null;
cleanups: (() => void)[] | null;
owner: Owner | null;
context: any | null;
sourceMap?: Record<string, SourceMapValue>;
sourceMap?: SignalMapValue[];
name?: string;
componentName?: string;
}

export interface Computation<Init, Next extends Init = Init> extends Owner {
Expand Down Expand Up @@ -129,10 +127,7 @@ export function createRoot<T>(fn: RootFunction<T>, detachedOwner?: Owner): T {
: fn
: () => fn(() => untrack(() => cleanNode(root)));

if ("_SOLID_DEV_") {
if (owner) root.name = `${owner.name}-r${rootCount++}`;
globalThis._$afterCreateRoot && globalThis._$afterCreateRoot(root);
}
if ("_SOLID_DEV_") globalThis._$afterCreateRoot && globalThis._$afterCreateRoot(root);

Owner = root;
Listener = null;
Expand Down Expand Up @@ -196,8 +191,10 @@ export function createSignal<T>(
comparator: options.equals || undefined
};

if ("_SOLID_DEV_" && !options.internal)
s.name = registerGraph(options.name || hashValue(value), s as { value: unknown });
if ("_SOLID_DEV_" && !options.internal) {
if (options.name) s.name = options.name;
registerGraph(s);
}

const setter: Setter<T | undefined> = (value?: unknown) => {
if (typeof value === "function") {
Expand Down Expand Up @@ -1058,59 +1055,11 @@ export function devComponent<T>(Comp: (props: T) => JSX.Element, props: T) {
return c.tValue !== undefined ? c.tValue : c.value;
}

export function hashValue(v: any): string {
const s = new Set();
return `s${
typeof v === "string"
? hash(v)
: hash(
untrack(
() =>
JSON.stringify(v, (k, v) => {
if (typeof v === "object" && v != null) {
if (s.has(v)) return;
s.add(v);
const keys = Object.keys(v);
const desc = Object.getOwnPropertyDescriptors(v);
const newDesc = keys.reduce((memo, key) => {
const value = desc[key];
// skip getters
if (!value.get) memo[key] = value;
return memo;
}, {} as any);
v = Object.create({}, newDesc);
}
if (typeof v === "bigint") {
return `${v.toString()}n`;
}
return v;
}) || ""
)
)
}`;
}

export function registerGraph(name: string, value: SourceMapValue): string {
let tryName = name;
if (Owner) {
let i = 0;
Owner.sourceMap || (Owner.sourceMap = {});
while (Owner.sourceMap[tryName]) tryName = `${name}-${++i}`;
Owner.sourceMap[tryName] = value;
value.graph = Owner;
}
return tryName;
}
interface GraphRecord {
[k: string]: GraphRecord | unknown;
}
export function serializeGraph(owner?: Owner | null): GraphRecord {
owner || (owner = Owner);
if (!"_SOLID_DEV_" || !owner) return {};
return {
...serializeValues(owner.sourceMap),
...(owner.owned ? serializeChildren(owner) : {})
};
export function registerGraph(value: SignalMapValue): void {
if (!Owner) return;
if (Owner.sourceMap) Owner.sourceMap.push(value);
else Owner.sourceMap = [value];
value.graph = Owner;
}

export type ContextProviderComponent<T> = FlowComponent<{ value: T }>;
Expand Down Expand Up @@ -1407,14 +1356,10 @@ function createComputation<Next, Init = unknown>(
if (!Owner.owned) Owner.owned = [c];
else Owner.owned.push(c);
}
if ("_SOLID_DEV_")
c.name =
(options && options.name) ||
`${(Owner as Computation<any>).name || "c"}-${
(Owner.owned || (Owner as Memo<Init, Next>).tOwned!).length
}`;
}

if ("_SOLID_DEV_" && options && options.name) c.name = options.name;

if (ExternalSourceFactory) {
const [track, trigger] = createSignal<void>(undefined, { equals: false });
const ordinary = ExternalSourceFactory(c.fn, trigger);
Expand Down Expand Up @@ -1712,31 +1657,4 @@ function createProvider(id: symbol, options?: EffectOptions) {
};
}

function hash(s: string) {
for (var i = 0, h = 9; i < s.length; ) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9);
return `${h ^ (h >>> 9)}`;
}

function serializeValues(sources: Record<string, { value: unknown }> = {}) {
const k = Object.keys(sources);
const result: Record<string, unknown> = {};
for (let i = 0; i < k.length; i++) {
const key = k[i];
result[key] = sources[key].value;
}
return result;
}

function serializeChildren(root: Owner): GraphRecord {
const result: GraphRecord = {};
for (let i = 0, len = root.owned!.length; i < len; i++) {
const node = root.owned![i];
result[node.componentName ? `${node.componentName}:${node.name}` : node.name!] = {
...serializeValues(node.sourceMap),
...(node.owned ? serializeChildren(node) : {})
};
}
return result;
}

type TODO = any;
4 changes: 2 additions & 2 deletions packages/solid/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export * from "./mutable.js";
export * from "./modifiers.js";

// dev
import { $NAME, $NODE, isWrappable } from "./store.js";
export const DEV = "_SOLID_DEV_" ? ({ $NAME, $NODE, isWrappable } as const) : undefined;
import { $NODE, isWrappable } from "./store.js";
export const DEV = "_SOLID_DEV_" ? ({ $NODE, isWrappable } as const) : undefined;
21 changes: 5 additions & 16 deletions packages/solid/store/src/mutable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
getDataNode,
$RAW,
$NODE,
$NAME,
StoreNode,
setProperty,
ownKeys
Expand All @@ -21,8 +20,7 @@ function proxyDescriptor(target: StoreNode, property: PropertyKey) {
desc.set ||
!desc.configurable ||
property === $PROXY ||
property === $NODE ||
property === $NAME
property === $NODE
)
return desc;

Expand Down Expand Up @@ -57,9 +55,7 @@ const proxyTraps: ProxyHandler<StoreNode> = {
batch(() => Array.prototype[property as any].apply(receiver, args));
}
}
return isWrappable(value)
? wrap(value, "_SOLID_DEV_" && target[$NAME] && `${target[$NAME]}:${property.toString()}`)
: value;
return isWrappable(value) ? wrap(value) : value;
},

has(target, property) {
Expand Down Expand Up @@ -90,7 +86,7 @@ const proxyTraps: ProxyHandler<StoreNode> = {
getOwnPropertyDescriptor: proxyDescriptor
};

function wrap<T extends StoreNode>(value: T, name?: string): T {
function wrap<T extends StoreNode>(value: T): T {
let p = value[$PROXY];
if (!p) {
Object.defineProperty(value, $PROXY, { value: (p = new Proxy(value, proxyTraps)) });
Expand All @@ -112,7 +108,6 @@ function wrap<T extends StoreNode>(value: T, name?: string): T {
});
}
}
if ("_SOLID_DEV_" && name) Object.defineProperty(value, $NAME, { value: name });
}
return p;
}
Expand All @@ -123,14 +118,8 @@ export function createMutable<T extends StoreNode>(state: T, options?: { name?:
throw new Error(
`Unexpected type ${typeof unwrappedStore} received when initializing 'createMutable'. Expected an object.`
);
const wrappedStore = wrap(
unwrappedStore,
"_SOLID_DEV_" && ((options && options.name) || DEV.hashValue(unwrappedStore))
);
if ("_SOLID_DEV_") {
const name = (options && options.name) || DEV.hashValue(unwrappedStore);
DEV.registerGraph(name, { value: unwrappedStore });
}
const wrappedStore = wrap(unwrappedStore);
if ("_SOLID_DEV_") DEV.registerGraph({ value: unwrappedStore, name: options && options.name });
return wrappedStore;
}

Expand Down
32 changes: 7 additions & 25 deletions packages/solid/store/src/store.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { getListener, batch, DEV, $PROXY, $TRACK, createSignal } from "solid-js";

export const $RAW = Symbol("store-raw"),
$NODE = Symbol("store-node"),
$NAME = Symbol("store-name");
$NODE = Symbol("store-node");

// dev
declare global {
Expand All @@ -23,7 +22,6 @@ export type OnStoreNodeUpdate = (
) => void;

export interface StoreNode {
[$NAME]?: string;
[$NODE]?: DataNodes;
[key: PropertyKey]: any;
}
Expand All @@ -43,7 +41,7 @@ export type NotWrappable =
| SolidStore.Unwrappable[keyof SolidStore.Unwrappable];
export type Store<T> = T;

function wrap<T extends StoreNode>(value: T, name?: string): T {
function wrap<T extends StoreNode>(value: T): T {
let p = value[$PROXY];
if (!p) {
Object.defineProperty(value, $PROXY, { value: (p = new Proxy(value, proxyTraps)) });
Expand All @@ -60,7 +58,6 @@ function wrap<T extends StoreNode>(value: T, name?: string): T {
}
}
}
if ("_SOLID_DEV_" && name) Object.defineProperty(value, $NAME, { value: name });
}
return p;
}
Expand Down Expand Up @@ -129,14 +126,7 @@ export function getDataNode(nodes: DataNodes, property: PropertyKey, value: any)

export function proxyDescriptor(target: StoreNode, property: PropertyKey) {
const desc = Reflect.getOwnPropertyDescriptor(target, property);
if (
!desc ||
desc.get ||
!desc.configurable ||
property === $PROXY ||
property === $NODE ||
property === $NAME
)
if (!desc || desc.get || !desc.configurable || property === $PROXY || property === $NODE)
return desc;
delete desc.value;
delete desc.writable;
Expand Down Expand Up @@ -187,9 +177,7 @@ const proxyTraps: ProxyHandler<StoreNode> = {
)
value = getDataNode(nodes, property, value)();
}
return isWrappable(value)
? wrap(value, "_SOLID_DEV_" && target[$NAME] && `${target[$NAME]}:${property.toString()}`)
: value;
return isWrappable(value) ? wrap(value) : value;
},

has(target, property) {
Expand Down Expand Up @@ -383,7 +371,7 @@ type MutableKeyOf<T> = KeyOf<T> & keyof PickMutable<T>;
type Rest<
T,
U extends PropertyKey[],
K extends KeyOf<T> = KeyOf<T>
K extends KeyOf<T> = KeyOf<T>
> = K extends keyof PickMutable<T>
? [Part<T, K>, ...RestSetterOrContinue<T[K], [K, ...U]>]
: K extends KeyOf<K>
Expand Down Expand Up @@ -514,14 +502,8 @@ export function createStore<T extends object = {}>(
throw new Error(
`Unexpected type ${typeof unwrappedStore} received when initializing 'createStore'. Expected an object.`
);
const wrappedStore = wrap(
unwrappedStore,
"_SOLID_DEV_" && ((options && options.name) || DEV.hashValue(unwrappedStore))
);
if ("_SOLID_DEV_") {
const name = (options && options.name) || DEV.hashValue(unwrappedStore);
DEV.registerGraph(name, { value: unwrappedStore });
}
const wrappedStore = wrap(unwrappedStore);
if ("_SOLID_DEV_") DEV.registerGraph({ value: unwrappedStore, name: options && options.name });
function setStore(...args: any[]): void {
batch(() => {
isArray && args.length === 1
Expand Down
Loading

0 comments on commit 9568266

Please sign in to comment.