valyrian.js 7.1.2 → 7.2.0

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,182 +1,102 @@
1
- import { update } from "valyrian.js";
2
-
3
- /* eslint-disable no-use-before-define */
4
- interface Cleanup {
5
- (): void;
6
- }
7
-
8
- interface Subscription {
9
- // eslint-disable-next-line no-unused-vars
10
- (value: Signal["value"]): void | Cleanup;
11
- }
1
+ import { VnodeWithDom, current, update, updateVnode, v } from "valyrian.js";
2
+
3
+ export function Signal(initialValue) {
4
+ // Create a copy of the current context object
5
+ const context = { ...current };
6
+
7
+ // Check if the context object has a vnode property
8
+ if (context.vnode) {
9
+ // Is first call
10
+ if (!context.vnode.signals) {
11
+ // Set the signals property to the signals property of the oldVnode object, or an empty array if that doesn't exist
12
+ context.vnode.signals = context.oldVnode?.signals || [];
13
+ // Set the calls property to -1
14
+ context.vnode.calls = -1;
15
+ // Set the subscribers property to the subscribers property of the oldVnode object, or an empty array if that doesn't exist
16
+ context.vnode.subscribers = context.oldVnode?.subscribers || [];
17
+
18
+ // Set the initialChildren property of the vnode object to a copy of the children array of the vnode object
19
+ context.vnode.initialChildren = [...context.vnode.children];
20
+ }
12
21
 
13
- interface Subscriptions extends Map<Subscription, Cleanup> {}
22
+ // Assign the signal variable to the signal stored at the index of the vnode object's calls property in the vnode's signals array
23
+ let signal = context.vnode.signals[++context.vnode.calls];
14
24
 
15
- interface Getter {
16
- // eslint-disable-next-line no-unused-vars
17
- (value: Signal["value"]): any;
18
- }
25
+ // If a signal has already been assigned to the signal variable, return it
26
+ if (signal) {
27
+ return signal;
28
+ }
29
+ }
19
30
 
20
- interface Getters {
21
- [key: string | symbol]: Getter;
22
- }
31
+ // Declare a variable to store the current value of the Signal
32
+ let value = initialValue;
23
33
 
24
- interface Signal {
25
- // Works as a getter of the value
26
- (): Signal["value"];
27
- // Works as a subscription to the value
28
- // eslint-disable-next-line no-unused-vars
29
- (value: Subscription): Signal;
30
- // Works as a setter with a path and a handler
31
- // eslint-disable-next-line no-unused-vars
32
- (path: string, handler: (valueAtPathPosition: any) => any): Signal["value"];
33
- // Works as a setter with a path and a value
34
- // eslint-disable-next-line no-unused-vars
35
- (path: string, value: any): Signal["value"];
36
- // Works as a setter with a value
37
- // eslint-disable-next-line no-unused-vars
38
- (value: any): Signal["value"];
39
- // Gets the current value of the signal.
40
- value: any;
41
- // Cleanup function to be called to remove all subscriptions.
42
- cleanup: () => void;
43
- // Creates a getter on the signal.
44
- // eslint-disable-next-line no-unused-vars
45
- getter: (name: string, handler: Getter) => any;
46
- // To access the getters on the signal.
47
- [key: string | number | symbol]: any;
48
- }
34
+ // Create an array to store functions that have subscribed to changes to the Signal's value
35
+ const subscribers = [];
49
36
 
50
- function makeUnsubscribe(subscriptions: Subscriptions, computed: Signal, handler: Subscription, cleanup?: Cleanup) {
51
- if (typeof cleanup === "function") {
52
- computed.cleanup = cleanup;
53
- }
54
- computed.unsubscribe = () => {
55
- subscriptions.delete(handler);
56
- computed?.cleanup();
37
+ // Define a function that allows other parts of the code to subscribe to changes to the Signal's value
38
+ const subscribe = (callback) => {
39
+ // Add the callback function to the subscribers array
40
+ if (subscribers.indexOf(callback) === -1) {
41
+ subscribers.push(callback);
42
+ }
57
43
  };
58
- }
59
44
 
60
- function createSubscription(signal: Signal, subscriptions: Subscriptions, handler: Subscription) {
61
- if (subscriptions.has(handler) === false) {
62
- // eslint-disable-next-line no-use-before-define
63
- let computed = Signal(() => handler(signal.value));
64
- let cleanup = computed(); // Execute to register itself
65
- makeUnsubscribe(subscriptions, computed, handler, cleanup);
66
- subscriptions.set(handler, computed);
45
+ // Define a function that returns the current value of the Signal
46
+ function get() {
47
+ return value;
67
48
  }
49
+ // Add value, toJSON, valueOf, and toString properties to the get function
50
+ get.value = value;
51
+ get.toJSON = get.valueOf = get;
52
+ get.toString = () => `${value}`;
53
+
54
+ // Define a function that allows the value of the Signal to be updated and notifies any subscribed functions of the change
55
+ const set = (newValue) => {
56
+ // Update the value of the Signal
57
+ value = newValue;
58
+ // Update the value property of the get function
59
+ get.value = value;
60
+ // Call each subscribed function with the new value of the Signal as an argument
61
+ for (let i = 0, l = subscribers.length; i < l; i++) {
62
+ subscribers[i](value);
63
+ }
68
64
 
69
- return subscriptions.get(handler);
70
- }
65
+ // Check if the context object has a vnode property
66
+ if (context.vnode) {
67
+ // If it does, create a new vnode object based on the original vnode, its children, and its DOM and SVG properties
68
+ let newVnode = v(context.vnode.tag, context.vnode.props, ...context.vnode.initialChildren) as VnodeWithDom;
69
+ newVnode.dom = context.vnode.dom;
70
+ newVnode.isSVG = context.vnode.isSVG;
71
+
72
+ // Clear the subscribers array by setting the length property to 0
73
+ context.vnode.subscribers.forEach(
74
+ (subscribers) =>
75
+ // Setting the length property to 0 is faster than clearing the array with a loop
76
+ (subscribers.length = 0)
77
+ );
78
+
79
+ // Clear the subscribers array by setting it to an empty array
80
+ context.vnode.subscribers = [];
81
+
82
+ // Return the result of updating the original vnode with the new vnode
83
+ return updateVnode(newVnode, context.vnode);
84
+ }
71
85
 
72
- let updateTimeout: any;
73
- function delayedUpdate() {
74
- clearTimeout(updateTimeout);
75
- updateTimeout = setTimeout(update);
76
- }
86
+ // If the context object doesn't have a vnode property, return the result of calling the update function
87
+ return update();
88
+ };
77
89
 
78
- // eslint-disable-next-line sonarjs/cognitive-complexity
79
- export function Signal(value: any): Signal {
80
- let subscriptions = new Map();
81
- let getters: Getters = {};
82
-
83
- let forceUpdate = false;
84
-
85
- let signal: Signal = new Proxy(
86
- // eslint-disable-next-line no-unused-vars
87
- function (valOrPath?: any | Subscription, handler?: (valueAtPathPosition: any) => any) {
88
- // Works as a getter
89
- if (typeof valOrPath === "undefined") {
90
- return signal.value;
91
- }
92
-
93
- // Works as a subscription
94
- if (typeof valOrPath === "function") {
95
- return createSubscription(signal, subscriptions, valOrPath);
96
- }
97
-
98
- // Works as a setter with a path
99
- if (typeof valOrPath === "string" && typeof handler !== "undefined") {
100
- let parsed = valOrPath.split(".");
101
- let result = signal.value;
102
- let next;
103
- while (parsed.length) {
104
- next = parsed.shift() as string;
105
- if (parsed.length > 0) {
106
- if (typeof result[next] !== "object") {
107
- result[next] = {};
108
- }
109
- result = result[next];
110
- } else {
111
- result[next] = typeof handler === "function" ? handler(result[next]) : handler;
112
- }
113
- }
114
- forceUpdate = true;
115
- signal.value = signal.value;
116
- return signal.value;
117
- }
118
-
119
- // Works as a setter with a value
120
- signal.value = valOrPath;
121
- return signal.value;
122
- } as Signal,
123
- {
124
- set(state, prop, val) {
125
- if (prop === "value" || prop === "unsubscribe" || prop === "cleanup") {
126
- let old = state[prop];
127
- state[prop] = val;
128
- if (prop === "value" && (forceUpdate || val !== old)) {
129
- forceUpdate = false;
130
- for (let [handler, computed] of subscriptions) {
131
- computed.cleanup();
132
- let cleanup = handler(val);
133
- makeUnsubscribe(subscriptions, computed, handler, cleanup);
134
- }
135
- delayedUpdate();
136
- }
137
- return true;
138
- }
139
- return false;
140
- },
141
- get(state, prop) {
142
- if (prop === "value") {
143
- return typeof state.value === "function" ? state.value() : state.value;
144
- }
145
-
146
- if (prop === "cleanup" || prop === "unsubscribe" || prop === "getter") {
147
- return state[prop];
148
- }
149
-
150
- if (prop in getters) {
151
- return getters[prop](state.value);
152
- }
153
- }
154
- }
155
- );
156
-
157
- Object.defineProperties(signal, {
158
- value: { value, writable: true, enumerable: true },
159
- cleanup: {
160
- value() {
161
- // eslint-disable-next-line no-unused-vars
162
- for (let [handler, computed] of subscriptions) {
163
- computed.unsubscribe();
164
- }
165
- },
166
- writable: true,
167
- enumerable: true
168
- },
169
- getter: {
170
- value(name: string, handler: Getter) {
171
- if (name in getters) {
172
- throw new Error("Named computed already exists.");
173
- }
174
-
175
- getters[name] = handler;
176
- },
177
- enumerable: true
178
- }
179
- });
90
+ // Assign the signal variable an array containing the get, set, and subscribe functions
91
+ let signal = [get, set, subscribe];
92
+
93
+ // If the context object has a vnode property, add the signal to the vnode's signals array
94
+ // and add the subscribers array to the vnode's subscribers array
95
+ if (context.vnode) {
96
+ context.vnode.signals.push(signal);
97
+ context.vnode.subscribers.push(subscribers);
98
+ }
180
99
 
100
+ // Return the signal
181
101
  return signal;
182
102
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "valyrian.js",
3
- "version": "7.1.2",
3
+ "version": "7.2.0",
4
4
  "description": "Lightweight steel to forge PWAs. (Minimal Frontend Framework with server side rendering and other capabilities)",
5
5
  "repository": "git@github.com:Masquerade-Circus/valyrian.js.git",
6
6
  "author": "Masquerade <christian@masquerade-circus.net>",
@@ -83,7 +83,7 @@
83
83
  "typescript": "^4.8.3"
84
84
  },
85
85
  "devDependencies": {
86
- "@release-it/conventional-changelog": "^5.1.0",
86
+ "@release-it/conventional-changelog": "^5.1.1",
87
87
  "@types/clean-css": "^4.2.5",
88
88
  "@types/node": "^18.7.16",
89
89
  "@types/node-fetch": "^2.6.2",
@@ -91,7 +91,6 @@
91
91
  "@types/source-map": "^0.5.7",
92
92
  "@typescript-eslint/eslint-plugin": "^5.36.2",
93
93
  "@typescript-eslint/parser": "^5.36.2",
94
- "browser-sync": "^2.27.10",
95
94
  "buffalo-test": "^2.0.0",
96
95
  "compression": "^1.7.4",
97
96
  "cross-env": "^7.0.3",
@@ -100,7 +99,7 @@
100
99
  "eslint": "^8.23.1",
101
100
  "eslint-plugin-sonarjs": "^0.15.0",
102
101
  "expect": "^29.0.3",
103
- "fastify": "^4.5.3",
102
+ "fastify": "^4.10.2",
104
103
  "gzip-size": "^7.0.0",
105
104
  "mocha": "^10.0.0",
106
105
  "nodemon": "^2.0.19",