@unsetsoft/ryunixjs 0.2.27 → 0.2.28-nightly.10

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.
package/dist/Ryunix.js ADDED
@@ -0,0 +1,607 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Ryunix = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ let containerRoot = null;
8
+ let nextUnitOfWork = null;
9
+ let currentRoot = null;
10
+ let wipRoot = null;
11
+ let deletions = null;
12
+ let wipFiber = null;
13
+ let hookIndex = null;
14
+
15
+ const RYUNIX_TYPES = {
16
+ TEXT_ELEMENT: Symbol("text.element"),
17
+ RYUNIX_EFFECT: Symbol("ryunix.effect"),
18
+ RYUNIX_CONTEXT: Symbol("ryunix.context"),
19
+ };
20
+
21
+ const STRINGS = {
22
+ object: "object",
23
+ function: "function",
24
+ style: "style",
25
+ className: "className",
26
+ children: "children",
27
+ };
28
+
29
+ const EFFECT_TAGS = {
30
+ PLACEMENT: Symbol(),
31
+ UPDATE: Symbol(),
32
+ DELETION: Symbol(),
33
+ };
34
+
35
+ const isEvent = (key) => key.startsWith("on");
36
+ const isProperty = (key) => key !== STRINGS.children && !isEvent(key);
37
+ const isNew = (prev, next) => (key) => prev[key] !== next[key];
38
+ const isGone = (next) => (key) => !(key in next);
39
+ const reg = /[A-Z]/g;
40
+ const hasDepsChanged = (prevDeps, nextDeps) =>
41
+ !prevDeps ||
42
+ !nextDeps ||
43
+ prevDeps.length !== nextDeps.length ||
44
+ prevDeps.some((dep, index) => dep !== nextDeps[index]);
45
+
46
+ /**
47
+ * The function creates a new element with the given type, props, and children.
48
+ * @param type - The type of the element to be created, such as "div", "span", "h1", etc.
49
+ * @param props - The `props` parameter is an object that contains the properties or attributes of the
50
+ * element being created. These properties can include things like `className`, `id`, `style`, and any
51
+ * other custom attributes that the user wants to add to the element. The `props` object is spread
52
+ * using the spread
53
+ * @param children - The `children` parameter is a rest parameter that allows the function to accept
54
+ * any number of arguments after the `props` parameter. These arguments will be treated as children
55
+ * elements of the created element. The `map` function is used to iterate over each child and create a
56
+ * new element if it is not
57
+ * @returns A JavaScript object with a `type` property and a `props` property. The `type` property is
58
+ * set to the `type` argument passed into the function, and the `props` property is an object that
59
+ * includes any additional properties passed in the `props` argument, as well as a `children` property
60
+ * that is an array of any child elements passed in the `...children` argument
61
+ */
62
+ function createElement(type, props, ...children) {
63
+ return {
64
+ type,
65
+ props: {
66
+ ...props,
67
+ children: children
68
+ .flat()
69
+ .map((child) =>
70
+ typeof child === STRINGS.object ? child : createTextElement(child)
71
+ ),
72
+ },
73
+ };
74
+ }
75
+
76
+ /**
77
+ * The function creates a text element with a given text value.
78
+ * @param text - The text content that will be used to create a new text element.
79
+ * @returns A JavaScript object with a `type` property set to `"TEXT_ELEMENT"` and a `props` property
80
+ * that contains a `nodeValue` property set to the `text` parameter and an empty `children` array.
81
+ */
82
+ function createTextElement(text) {
83
+ return {
84
+ type: RYUNIX_TYPES.TEXT_ELEMENT,
85
+ props: {
86
+ nodeValue: text,
87
+ children: [],
88
+ },
89
+ };
90
+ }
91
+
92
+ /**
93
+ * The function creates a new DOM element based on the given fiber object and updates its properties.
94
+ * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
95
+ * contains information about the element type, props, and children of the node.
96
+ * @returns The `createDom` function returns a newly created DOM element based on the `fiber` object
97
+ * passed as an argument. If the `fiber` object represents a text element, a text node is created using
98
+ * `document.createTextNode("")`. Otherwise, a new element is created using
99
+ * `document.createElement(fiber.type)`. The function then calls the `updateDom` function to update the
100
+ * properties of the newly created
101
+ */
102
+ function createDom(fiber) {
103
+ const dom =
104
+ fiber.type == RYUNIX_TYPES.TEXT_ELEMENT
105
+ ? document.createTextNode("")
106
+ : document.createElement(fiber.type);
107
+
108
+ updateDom(dom, {}, fiber.props);
109
+
110
+ return dom;
111
+ }
112
+
113
+ /**
114
+ * The function updates the DOM by removing old event listeners and properties, and adding new ones
115
+ * based on the previous and next props.
116
+ * @param dom - The DOM element that needs to be updated with new props.
117
+ * @param prevProps - An object representing the previous props (properties) of a DOM element.
118
+ * @param nextProps - An object containing the new props that need to be updated in the DOM.
119
+ */
120
+ function updateDom(dom, prevProps, nextProps) {
121
+ Object.keys(prevProps)
122
+ .filter(isEvent)
123
+ .filter((key) => isGone(nextProps)(key) || isNew(prevProps, nextProps)(key))
124
+ .forEach((name) => {
125
+ const eventType = name.toLowerCase().substring(2);
126
+ dom.removeEventListener(eventType, prevProps[name]);
127
+ });
128
+
129
+ Object.keys(prevProps)
130
+ .filter(isProperty)
131
+ .filter(isGone(nextProps))
132
+ .forEach((name) => {
133
+ dom[name] = "";
134
+ });
135
+
136
+ Object.keys(nextProps)
137
+ .filter(isProperty)
138
+ .filter(isNew(prevProps, nextProps))
139
+ .forEach((name) => {
140
+ if (name === STRINGS.style) {
141
+ DomStyle(dom, nextProps.style);
142
+ } else if (name === STRINGS.className) {
143
+ if (nextProps.className === "") {
144
+ throw new Error("className cannot be empty.");
145
+ }
146
+ prevProps.className &&
147
+ dom.classList.remove(...prevProps.className.split(/\s+/));
148
+ dom.classList.add(...nextProps.className.split(/\s+/));
149
+ } else {
150
+ dom[name] = nextProps[name];
151
+ }
152
+ });
153
+
154
+ Object.keys(nextProps)
155
+ .filter(isEvent)
156
+ .filter(isNew(prevProps, nextProps))
157
+ .forEach((name) => {
158
+ const eventType = name.toLowerCase().substring(2);
159
+ dom.addEventListener(eventType, nextProps[name]);
160
+ });
161
+ }
162
+
163
+ function DomStyle(dom, style) {
164
+ dom.style = Object.keys(style).reduce((acc, styleName) => {
165
+ const key = styleName.replace(reg, function (v) {
166
+ return "-" + v.toLowerCase();
167
+ });
168
+ acc += `${key}: ${style[styleName]};`;
169
+ return acc;
170
+ }, "");
171
+ }
172
+
173
+ /**
174
+ * The function commits changes made to the virtual DOM to the actual DOM.
175
+ */
176
+ function commitRoot() {
177
+ deletions.forEach(commitWork);
178
+ commitWork(wipRoot.child);
179
+ currentRoot = wipRoot;
180
+ wipRoot = null;
181
+ }
182
+
183
+ /**
184
+ * The function cancels all effect hooks in a given fiber.
185
+ * @param fiber - The "fiber" parameter is likely referring to a data structure used in React.js to
186
+ * represent a component and its state. It contains information about the component's props, state, and
187
+ * children, as well as metadata used by React to manage updates and rendering. The function
188
+ * "cancelEffects" is likely intended
189
+ */
190
+ function cancelEffects(fiber) {
191
+ if (fiber.hooks) {
192
+ fiber.hooks
193
+ .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.cancel)
194
+ .forEach((effectHook) => {
195
+ effectHook.cancel();
196
+ });
197
+ }
198
+ }
199
+
200
+ /**
201
+ * The function runs all effect hooks in a given fiber.
202
+ * @param fiber - The "fiber" parameter is likely referring to a data structure used in the
203
+ * implementation of a fiber-based reconciliation algorithm, such as the one used in React. A fiber
204
+ * represents a unit of work that needs to be performed by the reconciliation algorithm, and it
205
+ * contains information about a component and its children, as
206
+ */
207
+ function runEffects(fiber) {
208
+ if (fiber.hooks) {
209
+ fiber.hooks
210
+ .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.effect)
211
+ .forEach((effectHook) => {
212
+ effectHook.cancel = effectHook.effect();
213
+ });
214
+ }
215
+ }
216
+
217
+ /**
218
+ * The function commits changes made to the DOM based on the effect tag of the fiber.
219
+ * @param fiber - A fiber is a unit of work in Ryunix's reconciliation process. It represents a
220
+ * component and its state at a particular point in time. The `commitWork` function takes a fiber as a
221
+ * parameter to commit the changes made during the reconciliation process to the actual DOM.
222
+ * @returns The function does not return anything, it performs side effects by manipulating the DOM.
223
+ */
224
+ function commitWork(fiber) {
225
+ if (!fiber) {
226
+ return;
227
+ }
228
+
229
+ let domParentFiber = fiber.parent;
230
+ while (!domParentFiber.dom) {
231
+ domParentFiber = domParentFiber.parent;
232
+ }
233
+ const domParent = domParentFiber.dom;
234
+
235
+ if (fiber.effectTag === EFFECT_TAGS.PLACEMENT) {
236
+ if (fiber.dom != null) {
237
+ domParent.appendChild(fiber.dom);
238
+ }
239
+ runEffects(fiber);
240
+ } else if (fiber.effectTag === EFFECT_TAGS.UPDATE) {
241
+ cancelEffects(fiber);
242
+ if (fiber.dom != null) {
243
+ updateDom(fiber.dom, fiber.alternate.props, fiber.props);
244
+ }
245
+ runEffects(fiber);
246
+ } else if (fiber.effectTag === EFFECT_TAGS.DELETION) {
247
+ cancelEffects(fiber);
248
+ commitDeletion(fiber, domParent);
249
+ return;
250
+ }
251
+
252
+ commitWork(fiber.child);
253
+ commitWork(fiber.sibling);
254
+ }
255
+
256
+ /**
257
+ * The function removes a fiber's corresponding DOM node from its parent node or recursively removes
258
+ * its child's DOM node until it finds a node to remove.
259
+ * @param fiber - a fiber node in a fiber tree, which represents a component or an element in the Ryunix
260
+ * application.
261
+ * @param domParent - The parent DOM element from which the fiber's DOM element needs to be removed.
262
+ */
263
+ function commitDeletion(fiber, domParent) {
264
+ if (fiber.dom) {
265
+ domParent.removeChild(fiber.dom);
266
+ } else {
267
+ commitDeletion(fiber.child, domParent);
268
+ }
269
+ }
270
+
271
+ /**
272
+ * @deprecated use Ryunix.init(root) instead.
273
+ *
274
+ * @description The function creates a root container for a web application.
275
+ * @example Ryunix.createRoot(document.getElementById("root")) -> <div id="root" />
276
+ * @param root - The parameter `root` is likely referring to an HTML element that will serve as the
277
+ * root or container for a web application or component. The `createRoot` function takes this element
278
+ * as an argument and assigns it to a variable called `containerRoot`. This variable can then be used
279
+ * to manipulate the contents
280
+ *
281
+ */
282
+ function createRoot(root) {
283
+ containerRoot = root;
284
+ }
285
+
286
+ /**
287
+ * @description The function creates a reference to a DOM element with the specified ID. This will be used to initialize the app.
288
+ * @example Ryunix.init("root") -> <div id="root" />
289
+ * @param root - The parameter "root" is the id of the HTML element that will serve as the container
290
+ * for the root element.
291
+ */
292
+ function init(root) {
293
+ containerRoot = document.getElementById(root);
294
+ }
295
+
296
+ /**
297
+ * The function renders an element into a container using a work-in-progress root.
298
+ * @param element - The element parameter is the component or element that needs to be rendered in the
299
+ * container. It could be a Ryunix component or a DOM element.
300
+ * @param container - The container parameter is the DOM element where the rendered element will be
301
+ * appended to. this parameter is optional if you use createRoot().
302
+ */
303
+ function render(element, container) {
304
+ wipRoot = {
305
+ dom: containerRoot || container,
306
+ props: {
307
+ children: [element],
308
+ },
309
+ alternate: currentRoot,
310
+ };
311
+ deletions = [];
312
+ nextUnitOfWork = wipRoot;
313
+ }
314
+
315
+ /**
316
+ * This function uses requestIdleCallback to perform work on a fiber tree until it is complete or the
317
+ * browser needs to yield to other tasks.
318
+ * @param deadline - The `deadline` parameter is an object that represents the amount of time the
319
+ * browser has to perform work before it needs to handle other tasks. It has a `timeRemaining()` method
320
+ * that returns the amount of time remaining before the deadline is reached. The `shouldYield` variable
321
+ * is used to determine
322
+ */
323
+ function workLoop(deadline) {
324
+ let shouldYield = false;
325
+ while (nextUnitOfWork && !shouldYield) {
326
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
327
+ shouldYield = deadline.timeRemaining() < 1;
328
+ }
329
+
330
+ if (!nextUnitOfWork && wipRoot) {
331
+ commitRoot();
332
+ }
333
+
334
+ requestIdleCallback(workLoop);
335
+ }
336
+
337
+ requestIdleCallback(workLoop);
338
+
339
+ /**
340
+ * The function performs a unit of work by updating either a function component or a host component and
341
+ * returns the next fiber to be processed.
342
+ * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
343
+ * contains information about the component's type, props, and children, as well as pointers to its
344
+ * parent, child, and sibling fibers. The `performUnitOfWork` function takes a fiber as a parameter and
345
+ * performs work
346
+ * @returns The function `performUnitOfWork` returns the next fiber to be processed. If the current
347
+ * fiber has a child, it returns the child. Otherwise, it looks for the next sibling of the current
348
+ * fiber. If there are no more siblings, it goes up the tree to the parent and looks for the next
349
+ * sibling of the parent. The function returns `null` if there are no more fibers to process.
350
+ */
351
+ function performUnitOfWork(fiber) {
352
+ const isFunctionComponent = fiber.type instanceof Function;
353
+ if (isFunctionComponent) {
354
+ updateFunctionComponent(fiber);
355
+ } else {
356
+ updateHostComponent(fiber);
357
+ }
358
+ if (fiber.child) {
359
+ return fiber.child;
360
+ }
361
+ let nextFiber = fiber;
362
+ while (nextFiber) {
363
+ if (nextFiber.sibling) {
364
+ return nextFiber.sibling;
365
+ }
366
+ nextFiber = nextFiber.parent;
367
+ }
368
+ }
369
+
370
+ /**
371
+ * This function updates a function component by setting up a work-in-progress fiber, resetting the
372
+ * hook index, creating an empty hooks array, rendering the component, and reconciling its children.
373
+ * @param fiber - The fiber parameter is an object that represents a node in the fiber tree. It
374
+ * contains information about the component, its props, state, and children. In this function, it is
375
+ * used to update the state of the component and its children.
376
+ */
377
+ function updateFunctionComponent(fiber) {
378
+ wipFiber = fiber;
379
+ hookIndex = 0;
380
+ wipFiber.hooks = [];
381
+ const children = [fiber.type(fiber.props)];
382
+ reconcileChildren(fiber, children);
383
+ }
384
+
385
+ /**
386
+ * This function updates a host component's DOM element and reconciles its children.
387
+ * @param fiber - A fiber is a unit of work in Ryunix that represents a component and its state. It
388
+ * contains information about the component's type, props, and children, as well as pointers to other
389
+ * fibers in the tree.
390
+ */
391
+ function updateHostComponent(fiber) {
392
+ if (!fiber.dom) {
393
+ fiber.dom = createDom(fiber);
394
+ }
395
+ reconcileChildren(fiber, fiber.props.children.flat());
396
+ }
397
+
398
+ /**
399
+ * This function reconciles the children of a fiber node with a new set of elements, creating new
400
+ * fibers for new elements, updating existing fibers for elements with the same type, and marking old
401
+ * fibers for deletion if they are not present in the new set of elements.
402
+ * @param wipFiber - A work-in-progress fiber object representing a component or element in the virtual
403
+ * DOM tree.
404
+ * @param elements - an array of elements representing the new children to be rendered in the current
405
+ * fiber's subtree
406
+ */
407
+ function reconcileChildren(wipFiber, elements) {
408
+ let index = 0;
409
+ let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
410
+ let prevSibling = null;
411
+
412
+ while (index < elements.length || oldFiber != null) {
413
+ const element = elements[index];
414
+ let newFiber = null;
415
+
416
+ const sameType = oldFiber && element && element.type == oldFiber.type;
417
+
418
+ if (sameType) {
419
+ newFiber = {
420
+ type: oldFiber.type,
421
+ props: element.props,
422
+ dom: oldFiber.dom,
423
+ parent: wipFiber,
424
+ alternate: oldFiber,
425
+ effectTag: EFFECT_TAGS.UPDATE,
426
+ };
427
+ }
428
+ if (element && !sameType) {
429
+ newFiber = {
430
+ type: element.type,
431
+ props: element.props,
432
+ dom: null,
433
+ parent: wipFiber,
434
+ alternate: null,
435
+ effectTag: EFFECT_TAGS.PLACEMENT,
436
+ };
437
+ }
438
+ if (oldFiber && !sameType) {
439
+ oldFiber.effectTag = EFFECT_TAGS.DELETION;
440
+ deletions.push(oldFiber);
441
+ }
442
+
443
+ if (oldFiber) {
444
+ oldFiber = oldFiber.sibling;
445
+ }
446
+
447
+ if (index === 0) {
448
+ wipFiber.child = newFiber;
449
+ } else if (element) {
450
+ prevSibling.sibling = newFiber;
451
+ }
452
+
453
+ prevSibling = newFiber;
454
+ index++;
455
+ }
456
+ }
457
+
458
+ /**
459
+ * The function createContext creates a context object with a default value and methods to set and get
460
+ * the context value.
461
+ * @param defaultValue - The `defaultValue` parameter is the initial value that will be assigned to the
462
+ * `contextValue` variable if no value is provided when creating the context.
463
+ * @returns a context object.
464
+ */
465
+ function createContext(defaultValue) {
466
+ let contextValue = defaultValue || null;
467
+
468
+ const context = {
469
+ tag: RYUNIX_TYPES.RYUNIX_CONTEXT,
470
+ Value: contextValue,
471
+ Provider: null,
472
+ };
473
+
474
+ context.Provider = (value) => (context.Value = value);
475
+
476
+ return context;
477
+ }
478
+
479
+ function Fragments(props) {
480
+ if (props.style) {
481
+ throw new Error("The style attribute is not supported");
482
+ }
483
+ if (props.className === "") {
484
+ throw new Error("className cannot be empty.");
485
+ }
486
+ return createElement("div", props, props.children);
487
+ }
488
+
489
+ // Hooks
490
+
491
+ /**
492
+ * The function `useContext` is used to read and subscribe to context from your component.
493
+ * @param ref - The `ref` parameter is a reference to a context object.
494
+ * @returns The `Value` property of the `hook` object is being returned.
495
+ */
496
+ function useContext(ref) {
497
+ hookIndex++;
498
+
499
+ const oldHook =
500
+ wipFiber.alternate &&
501
+ wipFiber.alternate.hooks &&
502
+ wipFiber.alternate.hooks[hookIndex];
503
+
504
+ const hasOld = oldHook ? oldHook : undefined;
505
+ const Context = hasOld ? hasOld : ref;
506
+ const hook = {
507
+ ...Context,
508
+ };
509
+
510
+ wipFiber.hooks.push(hook);
511
+
512
+ return hook.Value;
513
+ }
514
+
515
+ /**
516
+ * @description The function creates a state.
517
+ * @param initial - The initial value of the state for the hook.
518
+ * @returns The `useStore` function returns an array with two elements: the current state value and a
519
+ * `setState` function that can be used to update the state.
520
+ */
521
+ function useStore(initial) {
522
+ const oldHook =
523
+ wipFiber.alternate &&
524
+ wipFiber.alternate.hooks &&
525
+ wipFiber.alternate.hooks[hookIndex];
526
+ const hook = {
527
+ state: oldHook ? oldHook.state : initial,
528
+ queue: [],
529
+ };
530
+
531
+ const actions = oldHook ? oldHook.queue : [];
532
+ actions.forEach((action) => {
533
+ hook.state =
534
+ typeof action === STRINGS.function ? action(hook.state) : action;
535
+ });
536
+
537
+ /**
538
+ * The function `setState` updates the state of a component in Ryunix by adding an action to a queue
539
+ * and setting up a new work-in-progress root.
540
+ * @param action - The `action` parameter is an object that represents a state update to be performed
541
+ * on a component. It contains information about the type of update to be performed and any new data
542
+ * that needs to be applied to the component's state.
543
+ */
544
+ const setState = (action) => {
545
+ hook.queue.push(action);
546
+ wipRoot = {
547
+ dom: currentRoot.dom,
548
+ props: currentRoot.props,
549
+ alternate: currentRoot,
550
+ };
551
+ nextUnitOfWork = wipRoot;
552
+ deletions = [];
553
+ };
554
+
555
+ wipFiber.hooks.push(hook);
556
+ hookIndex++;
557
+ return [hook.state, setState];
558
+ }
559
+
560
+ /**
561
+ * This is a function that creates a hook for managing side effects in Ryunix components.
562
+ * @param effect - The effect function that will be executed after the component has rendered or when
563
+ * the dependencies have changed. It can perform side effects such as fetching data, updating the DOM,
564
+ * or subscribing to events.
565
+ * @param deps - An array of dependencies that the effect depends on. If any of the dependencies change
566
+ * between renders, the effect will be re-run. If the array is empty, the effect will only run once on
567
+ * mount and never again.
568
+ */
569
+ function useEffect(effect, deps) {
570
+ const oldHook =
571
+ wipFiber.alternate &&
572
+ wipFiber.alternate.hooks &&
573
+ wipFiber.alternate.hooks[hookIndex];
574
+
575
+ const hasChanged = hasDepsChanged(oldHook ? oldHook.deps : undefined, deps);
576
+
577
+ const hook = {
578
+ tag: RYUNIX_TYPES.RYUNIX_EFFECT,
579
+ effect: hasChanged ? effect : null,
580
+ cancel: hasChanged && oldHook && oldHook.cancel,
581
+ deps,
582
+ };
583
+
584
+ wipFiber.hooks.push(hook);
585
+ hookIndex++;
586
+ }
587
+
588
+ var Ryunix = {
589
+ createElement,
590
+ render,
591
+ createRoot,
592
+ init,
593
+ Fragments,
594
+ };
595
+
596
+ window.Ryunix = Ryunix;
597
+
598
+ exports.Fragments = Fragments;
599
+ exports.createContext = createContext;
600
+ exports.default = Ryunix;
601
+ exports.useContext = useContext;
602
+ exports.useEffect = useEffect;
603
+ exports.useStore = useStore;
604
+
605
+ Object.defineProperty(exports, '__esModule', { value: true });
606
+
607
+ }));
package/lib/dom.js CHANGED
@@ -1,3 +1,42 @@
1
+ let containerRoot = null;
2
+ let nextUnitOfWork = null;
3
+ let currentRoot = null;
4
+ let wipRoot = null;
5
+ let deletions = null;
6
+ let wipFiber = null;
7
+ let hookIndex = null;
8
+
9
+ const RYUNIX_TYPES = {
10
+ TEXT_ELEMENT: Symbol("text.element"),
11
+ RYUNIX_EFFECT: Symbol("ryunix.effect"),
12
+ RYUNIX_CONTEXT: Symbol("ryunix.context"),
13
+ };
14
+
15
+ const STRINGS = {
16
+ object: "object",
17
+ function: "function",
18
+ style: "style",
19
+ className: "className",
20
+ children: "children",
21
+ };
22
+
23
+ const EFFECT_TAGS = {
24
+ PLACEMENT: Symbol(),
25
+ UPDATE: Symbol(),
26
+ DELETION: Symbol(),
27
+ };
28
+
29
+ const isEvent = (key) => key.startsWith("on");
30
+ const isProperty = (key) => key !== STRINGS.children && !isEvent(key);
31
+ const isNew = (prev, next) => (key) => prev[key] !== next[key];
32
+ const isGone = (next) => (key) => !(key in next);
33
+ const reg = /[A-Z]/g;
34
+ const hasDepsChanged = (prevDeps, nextDeps) =>
35
+ !prevDeps ||
36
+ !nextDeps ||
37
+ prevDeps.length !== nextDeps.length ||
38
+ prevDeps.some((dep, index) => dep !== nextDeps[index]);
39
+
1
40
  /**
2
41
  * The function creates a new element with the given type, props, and children.
3
42
  * @param type - The type of the element to be created, such as "div", "span", "h1", etc.
@@ -22,7 +61,7 @@ function createElement(type, props, ...children) {
22
61
  children: children
23
62
  .flat()
24
63
  .map((child) =>
25
- typeof child === "object" ? child : createTextElement(child)
64
+ typeof child === STRINGS.object ? child : createTextElement(child)
26
65
  ),
27
66
  },
28
67
  };
@@ -36,7 +75,7 @@ function createElement(type, props, ...children) {
36
75
  */
37
76
  function createTextElement(text) {
38
77
  return {
39
- type: "TEXT_ELEMENT",
78
+ type: RYUNIX_TYPES.TEXT_ELEMENT,
40
79
  props: {
41
80
  nodeValue: text,
42
81
  children: [],
@@ -56,7 +95,7 @@ function createTextElement(text) {
56
95
  */
57
96
  function createDom(fiber) {
58
97
  const dom =
59
- fiber.type == "TEXT_ELEMENT"
98
+ fiber.type == RYUNIX_TYPES.TEXT_ELEMENT
60
99
  ? document.createTextNode("")
61
100
  : document.createElement(fiber.type);
62
101
 
@@ -65,11 +104,6 @@ function createDom(fiber) {
65
104
  return dom;
66
105
  }
67
106
 
68
- const isEvent = (key) => key.startsWith("on");
69
- const isProperty = (key) => key !== "children" && !isEvent(key);
70
- const isNew = (prev, next) => (key) => prev[key] !== next[key];
71
- const isGone = (next) => (key) => !(key in next);
72
-
73
107
  /**
74
108
  * The function updates the DOM by removing old event listeners and properties, and adding new ones
75
109
  * based on the previous and next props.
@@ -97,9 +131,9 @@ function updateDom(dom, prevProps, nextProps) {
97
131
  .filter(isProperty)
98
132
  .filter(isNew(prevProps, nextProps))
99
133
  .forEach((name) => {
100
- if (name === "style") {
134
+ if (name === STRINGS.style) {
101
135
  DomStyle(dom, nextProps.style);
102
- } else if (name === "className") {
136
+ } else if (name === STRINGS.className) {
103
137
  if (nextProps.className === "") {
104
138
  throw new Error("className cannot be empty.");
105
139
  }
@@ -120,7 +154,6 @@ function updateDom(dom, prevProps, nextProps) {
120
154
  });
121
155
  }
122
156
 
123
- const reg = /[A-Z]/g;
124
157
  function DomStyle(dom, style) {
125
158
  dom.style = Object.keys(style).reduce((acc, styleName) => {
126
159
  const key = styleName.replace(reg, function (v) {
@@ -151,7 +184,7 @@ function commitRoot() {
151
184
  function cancelEffects(fiber) {
152
185
  if (fiber.hooks) {
153
186
  fiber.hooks
154
- .filter((hook) => hook.tag === "effect" && hook.cancel)
187
+ .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.cancel)
155
188
  .forEach((effectHook) => {
156
189
  effectHook.cancel();
157
190
  });
@@ -168,7 +201,7 @@ function cancelEffects(fiber) {
168
201
  function runEffects(fiber) {
169
202
  if (fiber.hooks) {
170
203
  fiber.hooks
171
- .filter((hook) => hook.tag === "effect" && hook.effect)
204
+ .filter((hook) => hook.tag === RYUNIX_TYPES.RYUNIX_EFFECT && hook.effect)
172
205
  .forEach((effectHook) => {
173
206
  effectHook.cancel = effectHook.effect();
174
207
  });
@@ -193,18 +226,18 @@ function commitWork(fiber) {
193
226
  }
194
227
  const domParent = domParentFiber.dom;
195
228
 
196
- if (fiber.effectTag === "PLACEMENT") {
229
+ if (fiber.effectTag === EFFECT_TAGS.PLACEMENT) {
197
230
  if (fiber.dom != null) {
198
231
  domParent.appendChild(fiber.dom);
199
232
  }
200
233
  runEffects(fiber);
201
- } else if (fiber.effectTag === "UPDATE") {
234
+ } else if (fiber.effectTag === EFFECT_TAGS.UPDATE) {
202
235
  cancelEffects(fiber);
203
236
  if (fiber.dom != null) {
204
237
  updateDom(fiber.dom, fiber.alternate.props, fiber.props);
205
238
  }
206
239
  runEffects(fiber);
207
- } else if (fiber.effectTag === "DELETION") {
240
+ } else if (fiber.effectTag === EFFECT_TAGS.DELETION) {
208
241
  cancelEffects(fiber);
209
242
  commitDeletion(fiber, domParent);
210
243
  return;
@@ -229,8 +262,6 @@ function commitDeletion(fiber, domParent) {
229
262
  }
230
263
  }
231
264
 
232
- let containerRoot = null;
233
-
234
265
  /**
235
266
  * @deprecated use Ryunix.init(root) instead.
236
267
  *
@@ -275,11 +306,6 @@ function render(element, container) {
275
306
  nextUnitOfWork = wipRoot;
276
307
  }
277
308
 
278
- let nextUnitOfWork = null;
279
- let currentRoot = null;
280
- let wipRoot = null;
281
- let deletions = null;
282
-
283
309
  /**
284
310
  * This function uses requestIdleCallback to perform work on a fiber tree until it is complete or the
285
311
  * browser needs to yield to other tasks.
@@ -335,9 +361,6 @@ function performUnitOfWork(fiber) {
335
361
  }
336
362
  }
337
363
 
338
- let wipFiber = null;
339
- let hookIndex = null;
340
-
341
364
  /**
342
365
  * This function updates a function component by setting up a work-in-progress fiber, resetting the
343
366
  * hook index, creating an empty hooks array, rendering the component, and reconciling its children.
@@ -393,7 +416,7 @@ function reconcileChildren(wipFiber, elements) {
393
416
  dom: oldFiber.dom,
394
417
  parent: wipFiber,
395
418
  alternate: oldFiber,
396
- effectTag: "UPDATE",
419
+ effectTag: EFFECT_TAGS.UPDATE,
397
420
  };
398
421
  }
399
422
  if (element && !sameType) {
@@ -403,11 +426,11 @@ function reconcileChildren(wipFiber, elements) {
403
426
  dom: null,
404
427
  parent: wipFiber,
405
428
  alternate: null,
406
- effectTag: "PLACEMENT",
429
+ effectTag: EFFECT_TAGS.PLACEMENT,
407
430
  };
408
431
  }
409
432
  if (oldFiber && !sameType) {
410
- oldFiber.effectTag = "DELETION";
433
+ oldFiber.effectTag = EFFECT_TAGS.DELETION;
411
434
  deletions.push(oldFiber);
412
435
  }
413
436
 
@@ -437,7 +460,7 @@ function createContext(defaultValue) {
437
460
  let contextValue = defaultValue || null;
438
461
 
439
462
  const context = {
440
- tag: "RYUNIX_CONTEXT",
463
+ tag: RYUNIX_TYPES.RYUNIX_CONTEXT,
441
464
  Value: contextValue,
442
465
  Provider: null,
443
466
  };
@@ -501,7 +524,8 @@ function useStore(initial) {
501
524
 
502
525
  const actions = oldHook ? oldHook.queue : [];
503
526
  actions.forEach((action) => {
504
- hook.state = typeof action === "function" ? action(hook.state) : action;
527
+ hook.state =
528
+ typeof action === STRINGS.function ? action(hook.state) : action;
505
529
  });
506
530
 
507
531
  /**
@@ -527,20 +551,6 @@ function useStore(initial) {
527
551
  return [hook.state, setState];
528
552
  }
529
553
 
530
- /**
531
- * The function checks if the previous dependencies are different from the next dependencies.
532
- * @param prevDeps - The previous dependencies, which could be an array of values or objects that a
533
- * function or component depends on.
534
- * @param nextDeps - `nextDeps` is an array of dependencies that are being checked for changes. These
535
- * dependencies are typically used in React's `useEffect` and `useCallback` hooks to determine when a
536
- * component should re-render or when a function should be re-created.
537
- */
538
- const hasDepsChanged = (prevDeps, nextDeps) =>
539
- !prevDeps ||
540
- !nextDeps ||
541
- prevDeps.length !== nextDeps.length ||
542
- prevDeps.some((dep, index) => dep !== nextDeps[index]);
543
-
544
554
  /**
545
555
  * This is a function that creates a hook for managing side effects in Ryunix components.
546
556
  * @param effect - The effect function that will be executed after the component has rendered or when
@@ -559,7 +569,7 @@ function useEffect(effect, deps) {
559
569
  const hasChanged = hasDepsChanged(oldHook ? oldHook.deps : undefined, deps);
560
570
 
561
571
  const hook = {
562
- tag: "effect",
572
+ tag: RYUNIX_TYPES.RYUNIX_EFFECT,
563
573
  effect: hasChanged ? effect : null,
564
574
  cancel: hasChanged && oldHook && oldHook.cancel,
565
575
  deps,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unsetsoft/ryunixjs",
3
- "version": "0.2.27",
3
+ "version": "0.2.28-nightly.10",
4
4
  "license": "MIT",
5
5
  "main": "./dist/Ryunix.js",
6
6
  "private": false,
@@ -10,6 +10,7 @@
10
10
  "homepage": "https://github.com/UnSetSoft/Ryunixjs#readme",
11
11
  "scripts": {
12
12
  "build": "rollup ./lib/main.js --file ./dist/Ryunix.js --format umd --name Ryunix",
13
+ "prepublish": "npm run build",
13
14
  "postinstall": "npm run build",
14
15
  "cli": "node ./bin/index.js",
15
16
  "nightly:release": "npm publish --tag nightly",
package/utils/index.js ADDED
@@ -0,0 +1,29 @@
1
+ function getPackageManager() {
2
+ const agent = process.env.npm_config_user_agent;
3
+
4
+ if (!agent) {
5
+ const parent = process.env._;
6
+
7
+ if (!parent) {
8
+ return "npm";
9
+ }
10
+
11
+ if (parent.endsWith("pnpx") || parent.endsWith("pnpm")) return "pnpm";
12
+ if (parent.endsWith("bunx") || parent.endsWith("bun")) return "bun";
13
+ if (parent.endsWith("yarn")) return "yarn";
14
+
15
+ return "npm";
16
+ }
17
+
18
+ const [program] = agent.split("/");
19
+
20
+ if (program === "yarn") return "yarn";
21
+ if (program === "pnpm") return "pnpm";
22
+ if (program === "bun") return "bun";
23
+
24
+ return "npm";
25
+ }
26
+
27
+ module.exports = {
28
+ getPackageManager,
29
+ };
package/webpack.config.js CHANGED
@@ -1,8 +1,16 @@
1
1
  const path = require("path");
2
2
  const HtmlWebpackPlugin = require("html-webpack-plugin");
3
3
  const ErrorOverlayPlugin = require("error-overlay-webpack-plugin");
4
+ const { getPackageManager } = require("./utils");
5
+
6
+ let dir;
7
+ const manager = getPackageManager();
8
+ if (manager === "yarn" || manager === "npm" || manager === "bun") {
9
+ dir = path.dirname(path.resolve(path.join(__dirname, "..", "..")));
10
+ } else if (manager === "pnpm") {
11
+ throw new Error(`The manager ${manager} is not supported.`);
12
+ }
4
13
 
5
- const dir = path.dirname(path.resolve(path.join(__dirname, "/../", "../")));
6
14
  module.exports = {
7
15
  mode: "production",
8
16
  entry: path.join(dir, "src", "main.ryx"),