@esportsplus/reactivity 0.0.11 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,29 +1,50 @@
1
1
  import Reactive from '../reactive';
2
+ function factory(value) {
3
+ if (typeof value === 'function') {
4
+ return fn(value);
5
+ }
6
+ if (typeof value === 'object' && value !== null && (value.constructor === Object)) {
7
+ return obj(value);
8
+ }
9
+ return new Reactive(value);
10
+ }
11
+ function fn(value) {
12
+ let fn = new Reactive(value);
13
+ return (...args) => {
14
+ let value = fn.get();
15
+ if (args.length && typeof value === 'function') {
16
+ value = value(...args);
17
+ }
18
+ return value;
19
+ };
20
+ }
2
21
  function obj(values) {
3
22
  let lazy = {}, properties = {};
4
23
  for (let key in values) {
5
24
  properties[key] = {
6
25
  get() {
7
26
  if (!lazy[key]) {
8
- lazy[key] = reactive(values[key]);
27
+ lazy[key] = factory(values[key]);
28
+ }
29
+ if (lazy[key] instanceof Reactive) {
30
+ return lazy[key].get();
9
31
  }
10
- return lazy[key].get();
32
+ return lazy[key];
11
33
  },
12
34
  set(value) {
13
35
  if (!lazy[key]) {
14
- lazy[key] = reactive(values[key]);
36
+ lazy[key] = factory(values[key]);
37
+ }
38
+ if (lazy[key] instanceof Reactive) {
39
+ lazy[key].set(value);
40
+ }
41
+ else {
42
+ lazy[key] = factory(value);
15
43
  }
16
- lazy[key].set(value);
17
44
  }
18
45
  };
19
46
  }
20
47
  return Object.defineProperties({}, properties);
21
48
  }
22
49
  ;
23
- function reactive(value) {
24
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
25
- return obj(value);
26
- }
27
- return new Reactive(value);
28
- }
29
- export default (value) => reactive(value);
50
+ export default (value) => factory(value);
@@ -1,17 +1,15 @@
1
- import { Scheduler } from './types';
2
- type Fn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
1
+ import { ReactiveFn, Scheduler, State } from './types';
3
2
  declare class Reactive<T> {
4
- private effect;
5
3
  private fn?;
6
- private observers;
7
- private sources;
8
- private state;
9
4
  private value;
5
+ effect: boolean;
10
6
  cleanup: ((old: T) => void)[] | null;
11
- constructor(data: Fn<T> | T, effect?: boolean);
7
+ observers: Reactive<any>[] | null;
8
+ sources: Reactive<any>[] | null;
9
+ state: State;
10
+ constructor(data: ReactiveFn<T> | T, effect?: boolean);
12
11
  get(): T extends (...args: any[]) => any ? ReturnType<T> : T;
13
12
  set(value: T): void;
14
- private mark;
15
13
  private removeParentObservers;
16
14
  private sync;
17
15
  private update;
package/build/reactive.js CHANGED
@@ -1,19 +1,43 @@
1
1
  import { CLEAN, CHECK, DIRTY } from './symbols';
2
- let index = 0, reaction = null, queue = [], schedulers = new Set, stack = null;
2
+ let index = 0, reaction = null, queue = [], scheduled = false, schedulers = new Set, stack = null;
3
+ function mark(instances, state) {
4
+ if (!instances) {
5
+ return;
6
+ }
7
+ for (let i = 0, n = instances.length; i < n; i++) {
8
+ let instance = instances[i];
9
+ if (instance.state < state) {
10
+ if (instance.effect && instance.state === CLEAN) {
11
+ queue.push(instance);
12
+ if (!scheduled) {
13
+ for (let scheduler of schedulers) {
14
+ scheduler.schedule();
15
+ }
16
+ scheduled = true;
17
+ }
18
+ }
19
+ instance.state = state;
20
+ if (instance.observers) {
21
+ mark(instance.observers, CHECK);
22
+ }
23
+ }
24
+ }
25
+ }
3
26
  async function task() {
4
27
  for (let i = 0, n = queue.length; i < n; i++) {
5
28
  await queue[i].get();
6
29
  }
7
30
  queue.length = 0;
31
+ scheduled = false;
8
32
  }
9
33
  class Reactive {
10
- effect;
11
34
  fn;
35
+ value;
36
+ effect;
37
+ cleanup = null;
12
38
  observers = null;
13
39
  sources = null;
14
40
  state;
15
- value;
16
- cleanup = null;
17
41
  constructor(data, effect = false) {
18
42
  this.effect = effect;
19
43
  if (typeof data === 'function') {
@@ -49,30 +73,11 @@ class Reactive {
49
73
  return this.value;
50
74
  }
51
75
  set(value) {
52
- if (this.observers && this.value !== value) {
53
- for (let i = 0; i < this.observers.length; i++) {
54
- this.observers[i].mark(DIRTY);
55
- }
76
+ if (this.value !== value) {
77
+ mark(this.observers, DIRTY);
56
78
  }
57
79
  this.value = value;
58
80
  }
59
- mark(state) {
60
- if (this.state < state) {
61
- if (this.effect && this.state === CLEAN) {
62
- queue.push(this);
63
- for (let scheduler of schedulers) {
64
- scheduler.schedule();
65
- }
66
- }
67
- this.state = state;
68
- if (!this.observers) {
69
- return;
70
- }
71
- for (let i = 0; i < this.observers.length; i++) {
72
- this.observers[i].mark(CHECK);
73
- }
74
- }
75
- }
76
81
  removeParentObservers() {
77
82
  if (!this.sources) {
78
83
  return;
package/build/types.d.ts CHANGED
@@ -6,6 +6,7 @@ type Primitives = any[] | boolean | number | string | null | undefined | ((...ar
6
6
  type InferNested<T> = T extends (...args: any[]) => any ? ReturnType<T> : T extends Record<string, Primitives> ? {
7
7
  [K in keyof T]: InferNested<T[K]>;
8
8
  } : T;
9
+ type ReactiveFn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
9
10
  type Scheduler = {
10
11
  schedule(): void;
11
12
  tasks: {
@@ -14,4 +15,4 @@ type Scheduler = {
14
15
  };
15
16
  };
16
17
  type State = typeof CHECK | typeof CLEAN | typeof DIRTY;
17
- export { Infer, Scheduler, State };
18
+ export { Infer, ReactiveFn, Scheduler, State };
package/package.json CHANGED
@@ -15,5 +15,5 @@
15
15
  "prepublishOnly": "npm run build"
16
16
  },
17
17
  "types": "./build/index.d.ts",
18
- "version": "0.0.11"
18
+ "version": "0.0.12"
19
19
  }
@@ -2,7 +2,35 @@ import { Infer } from '~/types';
2
2
  import Reactive from '~/reactive';
3
3
 
4
4
 
5
- // TODO: Typecheck on `values` to get rid of lazy var
5
+ function factory<T>(value: T) {
6
+ if (typeof value === 'function') {
7
+ return fn(value);
8
+ }
9
+
10
+ if (typeof value === 'object' && value !== null && (value.constructor === Object)) {
11
+ return obj(value);
12
+ }
13
+
14
+ return new Reactive(value) as T;
15
+ }
16
+
17
+ function fn<T>(value: T) {
18
+ let fn = new Reactive(value);
19
+
20
+ return (...args: any[]) => {
21
+ let value = fn.get();
22
+
23
+ if (args.length && typeof value === 'function') {
24
+ value = value(...args);
25
+ }
26
+
27
+ return value as typeof value extends (...args: any[]) => any
28
+ ? ReturnType<typeof value>
29
+ : typeof value;
30
+ };
31
+ }
32
+
33
+ // TODO: Typecheck on `values` to get rid of lazy var?
6
34
  function obj<T>(values: T) {
7
35
  let lazy: Record<string, any> = {},
8
36
  properties: PropertyDescriptorMap = {};
@@ -11,17 +39,26 @@ function obj<T>(values: T) {
11
39
  properties[key] = {
12
40
  get() {
13
41
  if (!lazy[key]) {
14
- lazy[key] = reactive(values[key]);
42
+ lazy[key] = factory(values[key]);
43
+ }
44
+
45
+ if (lazy[key] instanceof Reactive) {
46
+ return lazy[key].get();
15
47
  }
16
48
 
17
- return lazy[key].get();
49
+ return lazy[key];
18
50
  },
19
51
  set(value: unknown) {
20
52
  if (!lazy[key]) {
21
- lazy[key] = reactive(values[key]);
53
+ lazy[key] = factory(values[key]);
22
54
  }
23
55
 
24
- lazy[key].set(value);
56
+ if (lazy[key] instanceof Reactive) {
57
+ lazy[key].set(value);
58
+ }
59
+ else {
60
+ lazy[key] = factory(value);
61
+ }
25
62
  }
26
63
  };
27
64
  }
@@ -29,17 +66,5 @@ function obj<T>(values: T) {
29
66
  return Object.defineProperties({}, properties) as T;
30
67
  };
31
68
 
32
- function reactive<T>(value: T) {
33
- // if (Array.isArray(value)) {
34
- // TODO
35
- // }
36
- // TODO: Can remove isArray implementation is created
37
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
38
- return obj(value);
39
- }
40
-
41
- return new Reactive(value) as T;
42
- }
43
-
44
69
 
45
- export default <T>(value: T) => reactive(value) as Infer<T>;
70
+ export default <T>(value: T) => factory(value) as Infer<T>;
package/src/reactive.ts CHANGED
@@ -1,43 +1,73 @@
1
1
  import { CLEAN, CHECK, DIRTY } from './symbols';
2
- import { Scheduler, State } from './types';
2
+ import { ReactiveFn, Scheduler, State } from './types';
3
3
 
4
4
 
5
5
  let index = 0,
6
6
  reaction: Reactive<any> | null = null,
7
7
  queue: Reactive<any>[] = [],
8
+ scheduled = false,
8
9
  schedulers = new Set<Scheduler>,
9
10
  stack: Reactive<any>[] | null = null;
10
11
 
11
12
 
13
+ function mark(instances: Reactive<any>[] | null, state: typeof CHECK | typeof DIRTY) {
14
+ if (!instances) {
15
+ return;
16
+ }
17
+
18
+ for (let i = 0, n = instances.length; i < n; i++) {
19
+ let instance = instances[i];
20
+
21
+ if (instance.state < state) {
22
+ // If previous state was clean we need to update effects
23
+ if (instance.effect && instance.state === CLEAN) {
24
+ queue.push(instance);
25
+
26
+ if (!scheduled) {
27
+ for (let scheduler of schedulers) {
28
+ scheduler.schedule();
29
+ }
30
+
31
+ scheduled = true;
32
+ }
33
+ }
34
+
35
+ instance.state = state;
36
+
37
+ if (instance.observers) {
38
+ mark(instance.observers, CHECK);
39
+ }
40
+ }
41
+ }
42
+ }
43
+
12
44
  async function task() {
13
45
  for (let i = 0, n = queue.length; i < n; i++) {
14
46
  await queue[i].get();
15
47
  }
16
48
 
17
49
  queue.length = 0;
50
+ scheduled = false;
18
51
  }
19
52
 
20
53
 
21
- type Fn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
22
-
23
-
24
54
  class Reactive<T> {
25
- private effect: boolean;
26
- private fn?: Fn<T>;
27
- private observers: Reactive<any>[] | null = null;
28
- private sources: Reactive<any>[] | null = null;
29
- private state: State;
55
+ private fn?: ReactiveFn<T>;
30
56
  private value: T;
31
57
 
32
58
 
59
+ effect: boolean;
33
60
  cleanup: ((old: T) => void)[] | null = null;
61
+ observers: Reactive<any>[] | null = null;
62
+ sources: Reactive<any>[] | null = null;
63
+ state: State;
34
64
 
35
65
 
36
- constructor(data: Fn<T> | T, effect: boolean = false) {
66
+ constructor(data: ReactiveFn<T> | T, effect: boolean = false) {
37
67
  this.effect = effect;
38
68
 
39
69
  if (typeof data === 'function') {
40
- this.fn = data as Fn<T>;
70
+ this.fn = data as ReactiveFn<T>;
41
71
  this.state = DIRTY;
42
72
  this.value = undefined as any;
43
73
 
@@ -75,39 +105,13 @@ class Reactive<T> {
75
105
  }
76
106
 
77
107
  set(value: T) {
78
- if (this.observers && this.value !== value) {
79
- for (let i = 0; i < this.observers.length; i++) {
80
- this.observers[i].mark(DIRTY);
81
- }
108
+ if (this.value !== value) {
109
+ mark(this.observers, DIRTY);
82
110
  }
83
111
 
84
112
  this.value = value;
85
113
  }
86
114
 
87
-
88
- private mark(state: typeof CHECK | typeof DIRTY) {
89
- if (this.state < state) {
90
- // If previous state was clean we need to update effects
91
- if (this.effect && this.state === CLEAN) {
92
- queue.push(this);
93
-
94
- for (let scheduler of schedulers) {
95
- scheduler.schedule();
96
- }
97
- }
98
-
99
- this.state = state;
100
-
101
- if (!this.observers) {
102
- return;
103
- }
104
-
105
- for (let i = 0; i < this.observers.length; i++) {
106
- this.observers[i].mark(CHECK);
107
- }
108
- }
109
- }
110
-
111
115
  // We don't actually delete sources here because we're replacing the entire array soon
112
116
  private removeParentObservers() {
113
117
  if (!this.sources) {
package/src/types.ts CHANGED
@@ -20,6 +20,8 @@ type InferNested<T> =
20
20
  ? { [K in keyof T]: InferNested<T[K]> }
21
21
  : T;
22
22
 
23
+ type ReactiveFn<T> = (onCleanup?: (fn: VoidFunction) => void) => T;
24
+
23
25
  type Scheduler = {
24
26
  schedule(): void;
25
27
  tasks: {
@@ -31,4 +33,4 @@ type Scheduler = {
31
33
  type State = typeof CHECK | typeof CLEAN | typeof DIRTY;
32
34
 
33
35
 
34
- export { Infer, Scheduler, State };
36
+ export { Infer, ReactiveFn, Scheduler, State };