@unsetsoft/ryunixjs 1.2.3-canary.9 → 1.2.4

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.
@@ -70,7 +70,12 @@ const is = {
70
70
  };
71
71
 
72
72
  /**
73
- * Create text element
73
+ * The `createTextElement` function creates a text element with the specified text content.
74
+ * @param text - The `text` parameter in the `createTextElement` function is the text content that you
75
+ * want to create a text element for. This text will be set as the `nodeValue` of the text element in
76
+ * the returned object.
77
+ * @returns A text element object is being returned with a type of RYUNIX_TYPES.TEXT_ELEMENT and props
78
+ * containing the text value provided in the function argument.
74
79
  */
75
80
  const createTextElement = (text) => {
76
81
  return {
@@ -83,7 +88,21 @@ const createTextElement = (text) => {
83
88
  };
84
89
 
85
90
  /**
86
- * Create element
91
+ * The `createElement` function creates a virtual DOM element with specified type, properties, and
92
+ * children.
93
+ * @param type - The `type` parameter in the `createElement` function represents the type of element
94
+ * you want to create, such as a HTML tag like 'div', 'span', 'p', etc.
95
+ * @param props - The `props` parameter in the `createElement` function is an object that contains the
96
+ * properties or attributes for the element being created. These properties can include things like
97
+ * class names, styles, event handlers, and any other custom attributes you want to assign to the
98
+ * element. In the code snippet you provided,
99
+ * @param children - The `children` parameter in the `createElement` function represents the child
100
+ * elements or text content that will be nested within the created element. These children can be
101
+ * passed as arguments to the `createElement` function and will be rendered as part of the element's
102
+ * content.
103
+ * @returns An object is being returned with a `type` property representing the type of element, and a
104
+ * `props` property containing the element's properties. The `props` object includes the children of
105
+ * the element, which are processed to ensure they are in the correct format.
87
106
  */
88
107
  const createElement = (type, props, ...children) => {
89
108
  const safeProps = props || {};
@@ -102,7 +121,14 @@ const createElement = (type, props, ...children) => {
102
121
  };
103
122
 
104
123
  /**
105
- * Fragment component
124
+ * The `Fragment` function in JavaScript creates a fragment element with the given children.
125
+ * @param props - The `props` parameter in the `Fragment` function is an object that contains the
126
+ * properties passed to the `Fragment` component. These properties can include `children`, which
127
+ * represents the child elements or components nested within the `Fragment`.
128
+ * @returns The `Fragment` component is returning a Ryunix fragment element created using the
129
+ * `createElement` function. The element is of type `RYUNIX_TYPES.RYUNIX_FRAGMENT` and contains the
130
+ * children passed to the `Fragment` component. If `props.children` is not an array, it is converted
131
+ * into an array before being spread into the `createElement` function.
106
132
  */
107
133
  const Fragment = (props) => {
108
134
  const children = Array.isArray(props.children)
@@ -450,6 +476,9 @@ const updateDom = (dom, prevProps = {}, nextProps = {}) => {
450
476
  });
451
477
  };
452
478
 
479
+ /**
480
+ * The `commitRoot` function commits the changes made to the virtual DOM by updating the actual DOM.
481
+ */
453
482
  const commitRoot = () => {
454
483
  const state = getState();
455
484
  state.deletions.forEach(commitWork);
@@ -587,34 +616,6 @@ const reconcileChildren = (wipFiber, elements) => {
587
616
  });
588
617
  };
589
618
 
590
- const updateFunctionComponent = (fiber) => {
591
- const state = getState();
592
- state.wipFiber = fiber;
593
- state.hookIndex = 0;
594
- state.wipFiber.hooks = [];
595
-
596
- const children = [fiber.type(fiber.props)];
597
-
598
- if (fiber.type._contextId && fiber.props.value !== undefined) {
599
- fiber._contextId = fiber.type._contextId;
600
- fiber._contextValue = fiber.props.value;
601
- }
602
-
603
- reconcileChildren(fiber, children);
604
- };
605
-
606
- const updateHostComponent = (fiber) => {
607
- if (!fiber.dom) {
608
- fiber.dom = createDom(fiber);
609
- }
610
- const children = fiber.props?.children || [];
611
- reconcileChildren(fiber, children);
612
- };
613
-
614
- const Image = ({ src, ...props }) => {
615
- return createElement('img', { ...props, src })
616
- };
617
-
618
619
  /**
619
620
  * Priority levels for updates
620
621
  */
@@ -628,369 +629,180 @@ const Priority = {
628
629
 
629
630
  Priority.NORMAL;
630
631
 
631
- /**
632
- * Performance profiler for Ryunix
633
- */
634
- class Profiler {
635
- constructor() {
636
- this.enabled = process.env.NODE_ENV !== 'production';
637
- this.measures = new Map();
638
- this.renderTimes = [];
639
- this.maxSamples = 100;
640
- }
641
-
642
- startMeasure(name) {
643
- if (!this.enabled) return
644
- this.measures.set(name, performance.now());
645
- }
646
-
647
- endMeasure(name) {
648
- if (!this.enabled) return
649
- const start = this.measures.get(name);
650
- if (!start) return
651
-
652
- const duration = performance.now() - start;
653
- this.measures.delete(name);
654
-
655
- return duration
632
+ const validateHookCall = () => {
633
+ const state = getState();
634
+ if (!state.wipFiber) {
635
+ throw new Error(
636
+ 'Hooks can only be called inside the body of a function component.',
637
+ )
656
638
  }
657
-
658
- recordRender(componentName, duration) {
659
- if (!this.enabled) return
660
-
661
- this.renderTimes.push({
662
- component: componentName,
663
- duration,
664
- timestamp: Date.now(),
665
- });
666
-
667
- if (this.renderTimes.length > this.maxSamples) {
668
- this.renderTimes.shift();
669
- }
639
+ if (!Array.isArray(state.wipFiber.hooks)) {
640
+ state.wipFiber.hooks = [];
670
641
  }
642
+ };
671
643
 
672
- getStats() {
673
- if (!this.enabled) return null
644
+ const haveDepsChanged = (oldDeps, newDeps) => {
645
+ if (!oldDeps || !newDeps) return true
646
+ if (oldDeps.length !== newDeps.length) return true
647
+ return oldDeps.some((dep, i) => !Object.is(dep, newDeps[i]))
648
+ };
674
649
 
675
- const total = this.renderTimes.reduce((sum, r) => sum + r.duration, 0);
676
- const avg = total / this.renderTimes.length;
677
- const max = Math.max(...this.renderTimes.map((r) => r.duration));
678
- const min = Math.min(...this.renderTimes.map((r) => r.duration));
650
+ /**
651
+ * The `useStore` function in JavaScript is a custom hook that uses a reducer to manage state updates
652
+ * based on actions provided.
653
+ * @param initialState - The `initialState` parameter in the `useStore` function is the initial state
654
+ * of the store that will be used with the `useReducer` hook. It represents the starting state of the
655
+ * store before any actions are dispatched to update it.
656
+ * @returns The `useStore` function is returning the result of calling the `useReducer` hook with the
657
+ * `reducer` function and the `initialState` as arguments.
658
+ */
659
+ const useStore = (initialState) => {
660
+ const reducer = (state, action) =>
661
+ is.function(action) ? action(state) : action;
662
+ return useReducer(reducer, initialState)
663
+ };
679
664
 
680
- return { total, avg, max, min, count: this.renderTimes.length }
681
- }
665
+ /**
666
+ * The `useReducer` function in JavaScript is used to manage state and actions.
667
+ *
668
+ * @param reducer - The `reducer` parameter in the `useReducer` function is a function that specifies
669
+ * how the state should be updated in response to an action. It takes the current state and an action
670
+ * as arguments and returns the new state based on the action.
671
+ * @param initialState - The `initialState` parameter in the `useReducer` function represents the
672
+ * initial state of the reducer. It is the state that will be used when the reducer is first called or
673
+ * when the state needs to be reset. This initial state can be a simple value, an object, an array, or
674
+ * @param init - The `init` parameter in the `useReducer` function is an optional function that can be
675
+ * used to initialize the state. If provided, it will be called with the `initialState` as its argument
676
+ * and the return value will be used as the initial state for the reducer. If `init`
677
+ * @returns An array containing the current state and the dispatch function is being returned.
678
+ */
679
+ const useReducer = (reducer, initialState, init) => {
680
+ validateHookCall();
682
681
 
683
- getSlowestComponents(limit = 10) {
684
- if (!this.enabled) return []
682
+ const state = getState();
683
+ const { wipFiber, hookIndex } = state;
684
+ const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
685
685
 
686
- const byComponent = new Map();
686
+ const hook = {
687
+ hookID: hookIndex,
688
+ type: RYUNIX_TYPES.RYUNIX_STORE,
689
+ state: oldHook ? oldHook.state : init ? init(initialState) : initialState,
690
+ queue: [],
691
+ };
687
692
 
688
- this.renderTimes.forEach(({ component, duration }) => {
689
- if (!byComponent.has(component)) {
690
- byComponent.set(component, { total: 0, count: 0, max: 0 });
693
+ if (oldHook?.queue) {
694
+ oldHook.queue.forEach((action) => {
695
+ try {
696
+ hook.state = reducer(hook.state, action);
697
+ } catch (error) {
698
+ if (process.env.NODE_ENV !== 'production') {
699
+ console.error('Error in reducer:', error);
700
+ }
691
701
  }
692
- const stats = byComponent.get(component);
693
- stats.total += duration;
694
- stats.count++;
695
- stats.max = Math.max(stats.max, duration);
696
702
  });
697
-
698
- return Array.from(byComponent.entries())
699
- .map(([name, stats]) => ({
700
- name,
701
- avg: stats.total / stats.count,
702
- max: stats.max,
703
- count: stats.count,
704
- }))
705
- .sort((a, b) => b.avg - a.avg)
706
- .slice(0, limit)
707
703
  }
708
704
 
709
- logStats() {
710
- if (!this.enabled) return
705
+ const dispatch = (action) => {
706
+ if (action === undefined) {
707
+ if (process.env.NODE_ENV !== 'production') {
708
+ console.warn('dispatch called with undefined action');
709
+ }
710
+ return
711
+ }
711
712
 
712
- const stats = this.getStats();
713
- if (!stats) return
713
+ hook.queue.push(action);
714
714
 
715
- console.group('🔍 Ryunix Performance Stats');
716
- console.log(`Total renders: ${stats.count}`);
717
- console.log(`Avg render time: ${stats.avg.toFixed(2)}ms`);
718
- console.log(
719
- `Min: ${stats.min.toFixed(2)}ms | Max: ${stats.max.toFixed(2)}ms`,
720
- );
715
+ const currentState = getState();
716
+ currentState.wipRoot = {
717
+ dom: currentState.currentRoot.dom,
718
+ props: currentState.currentRoot.props,
719
+ alternate: currentState.currentRoot,
720
+ };
721
+ currentState.deletions = [];
722
+ currentState.hookIndex = 0;
723
+ scheduleWork(currentState.wipRoot);
724
+ };
721
725
 
722
- const slowest = this.getSlowestComponents(5);
723
- if (slowest.length > 0) {
724
- console.log('\n⚠️ Slowest components:');
725
- slowest.forEach((comp, i) => {
726
- console.log(
727
- `${i + 1}. ${comp.name}: ${comp.avg.toFixed(2)}ms avg (${comp.count} renders)`,
728
- );
729
- });
730
- }
731
- console.groupEnd();
732
- }
726
+ wipFiber.hooks[hookIndex] = hook;
727
+ state.hookIndex++;
728
+ return [hook.state, dispatch]
729
+ };
733
730
 
734
- clear() {
735
- this.renderTimes = [];
736
- this.measures.clear();
737
- }
731
+ /**
732
+ * The `useEffect` function in JavaScript is used to manage side effects in functional components by
733
+ * comparing dependencies and executing a callback function when dependencies change.
734
+ * @param callback - The `callback` parameter in the `useEffect` function is a function that will be
735
+ * executed as the effect. This function can perform side effects like data fetching, subscriptions, or
736
+ * DOM manipulations.
737
+ * @param deps - The `deps` parameter in the `useEffect` function stands for dependencies. It is an
738
+ * optional array that contains values that the effect depends on. The effect will only re-run if any
739
+ * of the values in the `deps` array have changed since the last render. If the `deps` array
740
+ */
741
+ const useEffect = (callback, deps) => {
742
+ validateHookCall();
738
743
 
739
- enable() {
740
- this.enabled = true;
744
+ if (!is.function(callback)) {
745
+ throw new Error('useEffect callback must be a function')
741
746
  }
742
-
743
- disable() {
744
- this.enabled = false;
747
+ if (deps !== undefined && !Array.isArray(deps)) {
748
+ throw new Error('useEffect dependencies must be an array or undefined')
745
749
  }
746
- }
747
750
 
748
- // Global profiler instance
749
- const profiler = new Profiler();
751
+ const state = getState();
752
+ const { wipFiber, hookIndex } = state;
753
+ const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
754
+ const hasChanged = haveDepsChanged(oldHook?.deps, deps);
750
755
 
751
- /**
752
- * Hook to profile component render
753
- */
754
- const useProfiler = (componentName) => {
755
- const startTime = performance.now();
756
+ const hook = {
757
+ hookID: hookIndex,
758
+ type: RYUNIX_TYPES.RYUNIX_EFFECT,
759
+ deps,
760
+ effect: hasChanged ? callback : null,
761
+ cancel: oldHook?.cancel,
762
+ };
756
763
 
757
- return () => {
758
- const duration = performance.now() - startTime;
759
- profiler.recordRender(componentName, duration);
760
- }
764
+ wipFiber.hooks[hookIndex] = hook;
765
+ state.hookIndex++;
761
766
  };
762
767
 
763
768
  /**
764
- * HOC to profile component
769
+ * The useRef function in JavaScript creates a reference object with an initial value for use in functional components.
770
+ * @param initialValue - The `initialValue` parameter in the `useRef` function represents the initial
771
+ * value that will be assigned to the `current` property of the reference object. This initial value
772
+ * will be used if there is no previous value stored in the hook.
773
+ * @returns The `useRef` function is returning the `current` property of the `hook.value` object, which
774
+ * contains the initial value passed to the `useRef` function.
765
775
  */
766
- const withProfiler = (Component, name) => {
767
- return (props) => {
768
- profiler.startMeasure(name);
769
- const result = Component(props);
770
- const duration = profiler.endMeasure(name);
771
- if (duration) profiler.recordRender(name, duration);
772
- return result
773
- }
774
- };
776
+ const useRef = (initialValue) => {
777
+ validateHookCall();
775
778
 
776
- const workLoop = (deadline) => {
777
779
  const state = getState();
778
- let shouldYield = false;
779
-
780
- while (state.nextUnitOfWork && !shouldYield) {
781
- state.nextUnitOfWork = performUnitOfWork(state.nextUnitOfWork);
782
- shouldYield = deadline.timeRemaining() < 1;
783
- }
780
+ const { wipFiber, hookIndex } = state;
781
+ const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
784
782
 
785
- if (!state.nextUnitOfWork && state.wipRoot) {
786
- commitRoot();
787
- }
783
+ const hook = {
784
+ hookID: hookIndex,
785
+ type: RYUNIX_TYPES.RYUNIX_REF,
786
+ value: oldHook ? oldHook.value : { current: initialValue },
787
+ };
788
788
 
789
- requestIdleCallback(workLoop);
790
- };
791
-
792
- requestIdleCallback(workLoop);
793
-
794
- const performUnitOfWork = (fiber) => {
795
- const componentName = fiber.type?.name || fiber.type?.displayName || 'Unknown';
796
-
797
- profiler.startMeasure(componentName);
798
-
799
- const isFunctionComponent = fiber.type instanceof Function;
800
- if (isFunctionComponent) {
801
- updateFunctionComponent(fiber);
802
- } else {
803
- updateHostComponent(fiber);
804
- }
805
-
806
- const duration = profiler.endMeasure(componentName);
807
- if (duration) profiler.recordRender(componentName, duration);
808
-
809
- if (fiber.child) {
810
- return fiber.child
811
- }
812
- let nextFiber = fiber;
813
- while (nextFiber) {
814
- if (nextFiber.sibling) {
815
- return nextFiber.sibling
816
- }
817
- nextFiber = nextFiber.parent;
818
- }
819
- };
820
-
821
- const scheduleWork = (root, priority = Priority.NORMAL) => {
822
- const state = getState();
823
- state.nextUnitOfWork = root;
824
- state.wipRoot = root;
825
- state.deletions = [];
826
- state.hookIndex = 0;
827
- state.effects = [];
828
-
829
- // Higher priority = faster scheduling
830
- if (priority <= Priority.USER_BLOCKING) {
831
- requestIdleCallback(workLoop);
832
- } else {
833
- setTimeout(() => requestIdleCallback(workLoop), 0);
834
- }
835
- };
836
-
837
- const render = (element, container) => {
838
- const state = getState();
839
- state.wipRoot = {
840
- dom: container,
841
- props: {
842
- children: [element],
843
- },
844
- alternate: state.currentRoot,
845
- };
846
-
847
- state.nextUnitOfWork = state.wipRoot;
848
- state.deletions = [];
849
- scheduleWork(state.wipRoot);
850
- return state.wipRoot
851
- };
852
-
853
- const init = (MainElement, root = '__ryunix') => {
854
- const state = getState();
855
- state.containerRoot = document.getElementById(root);
856
- const renderProcess = render(MainElement, state.containerRoot);
857
- return renderProcess
858
- };
859
-
860
- const safeRender = (component, props, onError) => {
861
- try {
862
- return component(props)
863
- } catch (error) {
864
- if (process.env.NODE_ENV !== 'production') {
865
- console.error('Component error:', error);
866
- }
867
- if (onError) onError(error);
868
- return null
869
- }
870
- };
871
-
872
- const validateHookCall = () => {
873
- const state = getState();
874
- if (!state.wipFiber) {
875
- throw new Error(
876
- 'Hooks can only be called inside the body of a function component.',
877
- )
878
- }
879
- if (!Array.isArray(state.wipFiber.hooks)) {
880
- state.wipFiber.hooks = [];
881
- }
882
- };
883
-
884
- const haveDepsChanged = (oldDeps, newDeps) => {
885
- if (!oldDeps || !newDeps) return true
886
- if (oldDeps.length !== newDeps.length) return true
887
- return oldDeps.some((dep, i) => !Object.is(dep, newDeps[i]))
888
- };
889
-
890
- const useStore = (initialState) => {
891
- const reducer = (state, action) =>
892
- is.function(action) ? action(state) : action;
893
- return useReducer(reducer, initialState)
894
- };
895
-
896
- const useReducer = (reducer, initialState, init) => {
897
- validateHookCall();
898
-
899
- const state = getState();
900
- const { wipFiber, hookIndex } = state;
901
- const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
902
-
903
- const hook = {
904
- hookID: hookIndex,
905
- type: RYUNIX_TYPES.RYUNIX_STORE,
906
- state: oldHook ? oldHook.state : init ? init(initialState) : initialState,
907
- queue: [], // Siempre nueva cola vacía
908
- };
909
-
910
- // Procesar acciones del render anterior
911
- if (oldHook?.queue) {
912
- oldHook.queue.forEach((action) => {
913
- try {
914
- hook.state = reducer(hook.state, action);
915
- } catch (error) {
916
- if (process.env.NODE_ENV !== 'production') {
917
- console.error('Error in reducer:', error);
918
- }
919
- }
920
- });
921
- }
922
-
923
- const dispatch = (action) => {
924
- if (action === undefined) {
925
- if (process.env.NODE_ENV !== 'production') {
926
- console.warn('dispatch called with undefined action');
927
- }
928
- return
929
- }
930
-
931
- hook.queue.push(action);
932
-
933
- const currentState = getState();
934
- currentState.wipRoot = {
935
- dom: currentState.currentRoot.dom,
936
- props: currentState.currentRoot.props,
937
- alternate: currentState.currentRoot,
938
- };
939
- currentState.deletions = [];
940
- currentState.hookIndex = 0;
941
- scheduleWork(currentState.wipRoot);
942
- };
943
-
944
- wipFiber.hooks[hookIndex] = hook;
945
- state.hookIndex++;
946
- return [hook.state, dispatch]
947
- };
948
-
949
- const useEffect = (callback, deps) => {
950
- validateHookCall();
951
-
952
- if (!is.function(callback)) {
953
- throw new Error('useEffect callback must be a function')
954
- }
955
- if (deps !== undefined && !Array.isArray(deps)) {
956
- throw new Error('useEffect dependencies must be an array or undefined')
957
- }
958
-
959
- const state = getState();
960
- const { wipFiber, hookIndex } = state;
961
- const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
962
- const hasChanged = haveDepsChanged(oldHook?.deps, deps);
963
-
964
- const hook = {
965
- hookID: hookIndex,
966
- type: RYUNIX_TYPES.RYUNIX_EFFECT,
967
- deps,
968
- effect: hasChanged ? callback : null,
969
- cancel: oldHook?.cancel,
970
- };
971
-
972
- wipFiber.hooks[hookIndex] = hook;
973
- state.hookIndex++;
974
- };
975
-
976
- const useRef = (initialValue) => {
977
- validateHookCall();
978
-
979
- const state = getState();
980
- const { wipFiber, hookIndex } = state;
981
- const oldHook = wipFiber.alternate?.hooks?.[hookIndex];
982
-
983
- const hook = {
984
- hookID: hookIndex,
985
- type: RYUNIX_TYPES.RYUNIX_REF,
986
- value: oldHook ? oldHook.value : { current: initialValue },
987
- };
988
-
989
- wipFiber.hooks[hookIndex] = hook;
990
- state.hookIndex++;
991
- return hook.value
789
+ wipFiber.hooks[hookIndex] = hook;
790
+ state.hookIndex++;
791
+ return hook.value
992
792
  };
993
793
 
794
+ /**
795
+ * The useMemo function in JavaScript is used to memoize the result of a computation based on
796
+ * dependencies.
797
+ * @param compute - The `compute` parameter in the `useMemo` function is a callback function that
798
+ * calculates the value that `useMemo` will memoize and return. This function will be called to compute
799
+ * the memoized value when necessary.
800
+ * @param deps - The `deps` parameter in the `useMemo` function refers to an array of dependencies.
801
+ * These dependencies are used to determine whether the memoized value needs to be recalculated or if
802
+ * the previously calculated value can be reused. The `useMemo` hook will recompute the memoized value
803
+ * only if
804
+ * @returns The `useMemo` function is returning the `value` calculated by the `compute` function.
805
+ */
994
806
  const useMemo = (compute, deps) => {
995
807
  validateHookCall();
996
808
 
@@ -1031,6 +843,17 @@ const useMemo = (compute, deps) => {
1031
843
  return value
1032
844
  };
1033
845
 
846
+ /**
847
+ * The useCallback function in JavaScript ensures that a callback function is memoized based on its
848
+ * dependencies.
849
+ * @param callback - A function that you want to memoize and return for later use.
850
+ * @param deps - The `deps` parameter in the `useCallback` function refers to an array of dependencies.
851
+ * These dependencies are used to determine when the callback function should be re-evaluated and
852
+ * memoized. If any of the dependencies change, the callback function will be re-executed and the
853
+ * memoized value will
854
+ * @returns The useCallback function is returning the memoized version of the callback function passed
855
+ * as the first argument, based on the dependencies array provided as the second argument.
856
+ */
1034
857
  const useCallback = (callback, deps) => {
1035
858
  if (!is.function(callback)) {
1036
859
  throw new Error('useCallback requires a function as first argument')
@@ -1038,6 +861,20 @@ const useCallback = (callback, deps) => {
1038
861
  return useMemo(() => callback, deps)
1039
862
  };
1040
863
 
864
+ /**
865
+ * The createContext function creates a context provider and useContext hook in JavaScript.
866
+ * @param [contextId] - The `contextId` parameter in the `createContext` function is used to specify
867
+ * the unique identifier for the context being created. It defaults to `RYUNIX_TYPES.RYUNIX_CONTEXT` if
868
+ * not provided.
869
+ * @param [defaultValue] - The `defaultValue` parameter in the `createContext` function is used to
870
+ * specify the default value that will be returned by the `useContext` hook if no provider is found in
871
+ * the component tree. It is an optional parameter, and if not provided, an empty object `{}` will be
872
+ * used as
873
+ * @returns The `createContext` function returns an object with two properties: `Provider` and
874
+ * `useContext`. The `Provider` property is a component that accepts `children` and `value` props, and
875
+ * sets the `_contextId` and `_contextValue` properties on the element. The `useContext` property is a
876
+ * hook function that retrieves the context value based on the context ID provided, or
877
+ */
1041
878
  const createContext = (
1042
879
  contextId = RYUNIX_TYPES.RYUNIX_CONTEXT,
1043
880
  defaultValue = {},
@@ -1075,6 +912,10 @@ const createContext = (
1075
912
  return { Provider, useContext }
1076
913
  };
1077
914
 
915
+ /**
916
+ * The `useQuery` function extracts query parameters from the URL in a browser environment.
917
+ * @returns An object containing the query parameters from the current URL is being returned.
918
+ */
1078
919
  const useQuery = () => {
1079
920
  if (typeof window === 'undefined') return {}
1080
921
 
@@ -1086,6 +927,14 @@ const useQuery = () => {
1086
927
  return query
1087
928
  };
1088
929
 
930
+ /**
931
+ * The function `useHash` in JavaScript is used to manage and update the hash portion of the URL in a
932
+ * web application.
933
+ * @returns The `useHash` function returns the current hash value from the window's location. If the
934
+ * window is undefined (e.g., in a server-side environment), it returns an empty string. The function
935
+ * also sets up an event listener to update the hash value when the hash in the URL changes and removes
936
+ * the event listener when the component unmounts.
937
+ */
1089
938
  const useHash = () => {
1090
939
  if (typeof window === 'undefined') return ''
1091
940
 
@@ -1098,6 +947,25 @@ const useHash = () => {
1098
947
  return hash
1099
948
  };
1100
949
 
950
+ /**
951
+ * The `useMetadata` function in JavaScript is used to dynamically update metadata tags in the document
952
+ * head based on provided tags and options.
953
+ * @param [tags] - The `tags` parameter in the `useMetadata` function is an object that contains
954
+ * metadata information for the webpage. It can include properties like `pageTitle`, `canonical`, and
955
+ * other custom metadata tags like `og:title`, `og:description`, `twitter:title`,
956
+ * `twitter:description`, etc. These tags
957
+ * @param [options] - The `options` parameter in the `useMetadata` function is an object that can
958
+ * contain the following properties:
959
+ * - `title`: An object that can have the following properties:
960
+ * - `template`: A string that defines the template for the page title. It can include a placeholder
961
+ * `%s` that will be replaced with the actual page title.
962
+ * - `prefix`: A string that will be used as the default title if no specific page title is provided.
963
+ * @returns The `useMetadata` function does not return anything. It is a custom hook that updates the
964
+ * document's metadata (such as title and meta tags) based on the provided `tags` and `options` whenever
965
+ * they change.
966
+ * This hook can't be reached by google crawler.
967
+ */
968
+
1101
969
  const useMetadata = (tags = {}, options = {}) => {
1102
970
  useEffect(() => {
1103
971
  if (typeof document === 'undefined') return
@@ -1188,6 +1056,12 @@ const findRoute = (routes, path) => {
1188
1056
  return notFound
1189
1057
  };
1190
1058
 
1059
+ /**
1060
+ * The `RouterProvider` component manages routing in a Ryunix application by updating the location based
1061
+ * on window events and providing context for the current route.
1062
+ * @returns The `RouterProvider` component is returning a `RouterContext.Provider` component with a
1063
+ * `value` prop set to `contextValue`, and wrapping the `children` within a `Fragment`.
1064
+ */
1191
1065
  const RouterProvider = ({ routes, children }) => {
1192
1066
  const [location, setLocation] = useStore(window.location.pathname);
1193
1067
 
@@ -1224,10 +1098,24 @@ const RouterProvider = ({ routes, children }) => {
1224
1098
  )
1225
1099
  };
1226
1100
 
1101
+ /**
1102
+ * The function `useRouter` returns the context of the Router for navigation in a Ryunix application.
1103
+ * @returns The `useRouter` function is returning the result of calling
1104
+ * `RouterContext.useContext('ryunix.navigation')`. This function is likely attempting to retrieve the
1105
+ * navigation context from the RouterContext.
1106
+ */
1227
1107
  const useRouter = () => {
1228
1108
  return RouterContext.useContext('ryunix.navigation')
1229
1109
  };
1230
1110
 
1111
+ /**
1112
+ * The `Children` function in JavaScript uses router hooks to handle scrolling to a specific element
1113
+ * based on the hash in the URL.
1114
+ * @returns The `Children` component is returning the result of calling `createElement` with
1115
+ * `route.component` as the first argument and an object with `key`, `params`, `query`, and `hash`
1116
+ * properties as the second argument. The `key` property is set to `location`, and the `params`,
1117
+ * `query`, and `hash` properties are passed as values from the component's props.
1118
+ */
1231
1119
  const Children = () => {
1232
1120
  const { route, params, query, location } = useRouter();
1233
1121
  if (!route || !route.component) return null
@@ -1249,6 +1137,12 @@ const Children = () => {
1249
1137
  })
1250
1138
  };
1251
1139
 
1140
+ /**
1141
+ * The NavLink function in JavaScript is a component that generates a link element with customizable
1142
+ * classes and active state based on the current location.
1143
+ * @returns The `NavLink` component is returning a JSX element representing an anchor (`<a>`) tag with
1144
+ * the following attributes and properties:
1145
+ */
1252
1146
  const NavLink = ({ to, exact = false, ...props }) => {
1253
1147
  const { location, navigate } = useRouter();
1254
1148
  const isActive = exact ? location === to : location.startsWith(to);
@@ -1361,6 +1255,376 @@ var hooks = /*#__PURE__*/Object.freeze({
1361
1255
  useTransition: useTransition
1362
1256
  });
1363
1257
 
1258
+ const updateFunctionComponent = (fiber) => {
1259
+ const state = getState();
1260
+ state.wipFiber = fiber;
1261
+ state.hookIndex = 0;
1262
+ state.wipFiber.hooks = [];
1263
+
1264
+ const children = [fiber.type(fiber.props)];
1265
+
1266
+ if (fiber.type._contextId && fiber.props.value !== undefined) {
1267
+ fiber._contextId = fiber.type._contextId;
1268
+ fiber._contextValue = fiber.props.value;
1269
+ }
1270
+
1271
+ reconcileChildren(fiber, children);
1272
+ };
1273
+
1274
+ const updateHostComponent = (fiber) => {
1275
+ if (!fiber.dom) {
1276
+ fiber.dom = createDom(fiber);
1277
+ }
1278
+ const children = fiber.props?.children || [];
1279
+ reconcileChildren(fiber, children);
1280
+ };
1281
+
1282
+ /**
1283
+ * The Component `Image` takes in a `src` and other props, and returns an `img` element with the
1284
+ * specified `src` and props.
1285
+ * @returns The `Image` component is being returned. It is a functional component that renders an `img`
1286
+ * element with the specified `src` and other props passed to it.
1287
+ */
1288
+ const Image = ({ src, ...props }) => {
1289
+ return createElement('img', { ...props, src })
1290
+ };
1291
+
1292
+ const { Provider: MDXProvider, useContext: useMDXComponents } = createContext(
1293
+ 'ryunix.mdx',
1294
+ {},
1295
+ );
1296
+
1297
+ /**
1298
+ * Get merged MDX components from context and provided components
1299
+ * @param {Object} components - Additional components to merge
1300
+ * @returns {Object} Merged components object
1301
+ */
1302
+ const getMDXComponents = (components) => {
1303
+ const contextComponents = useMDXComponents();
1304
+ return {
1305
+ ...contextComponents,
1306
+ ...components,
1307
+ }
1308
+ };
1309
+
1310
+ /**
1311
+ * Default MDX components with Ryunix-optimized rendering
1312
+ */
1313
+ const defaultComponents = {
1314
+ // Headings
1315
+ h1: (props) => createElement('h1', { ...props }),
1316
+ h2: (props) => createElement('h2', { ...props }),
1317
+ h3: (props) => createElement('h3', { ...props }),
1318
+ h4: (props) => createElement('h4', { ...props }),
1319
+ h5: (props) => createElement('h5', { ...props }),
1320
+ h6: (props) => createElement('h6', { ...props }),
1321
+
1322
+ // Text
1323
+ p: (props) => createElement('p', { ...props }),
1324
+ a: (props) => createElement('a', { ...props }),
1325
+ strong: (props) => createElement('strong', { ...props }),
1326
+ em: (props) => createElement('em', { ...props }),
1327
+ code: (props) => createElement('code', { ...props }),
1328
+
1329
+ // Lists
1330
+ ul: (props) => createElement('ul', { ...props }),
1331
+ ol: (props) => createElement('ol', { ...props }),
1332
+ li: (props) => createElement('li', { ...props }),
1333
+
1334
+ // Blocks
1335
+ blockquote: (props) => createElement('blockquote', { ...props }),
1336
+ pre: (props) => createElement('pre', { ...props }),
1337
+ hr: (props) => createElement('hr', { ...props }),
1338
+
1339
+ // Tables
1340
+ table: (props) => createElement('table', { ...props }),
1341
+ thead: (props) => createElement('thead', { ...props }),
1342
+ tbody: (props) => createElement('tbody', { ...props }),
1343
+ tr: (props) => createElement('tr', { ...props }),
1344
+ th: (props) => createElement('th', { ...props }),
1345
+ td: (props) => createElement('td', { ...props }),
1346
+
1347
+ // Media
1348
+ img: (props) => createElement('img', { ...props }),
1349
+ };
1350
+
1351
+ /**
1352
+ * MDX Wrapper component
1353
+ * Provides default styling and components for MDX content
1354
+ */
1355
+ const MDXContent = ({ children, components = {} }) => {
1356
+ const mergedComponents = getMDXComponents(components);
1357
+
1358
+ return createElement(
1359
+ MDXProvider,
1360
+ { value: mergedComponents },
1361
+ createElement('div', null, children),
1362
+ )
1363
+ };
1364
+
1365
+ /**
1366
+ * Performance profiler for Ryunix
1367
+ */
1368
+ class Profiler {
1369
+ constructor() {
1370
+ this.enabled = process.env.NODE_ENV !== 'production';
1371
+ this.measures = new Map();
1372
+ this.renderTimes = [];
1373
+ this.maxSamples = 100;
1374
+ }
1375
+
1376
+ startMeasure(name) {
1377
+ if (!this.enabled) return
1378
+ this.measures.set(name, performance.now());
1379
+ }
1380
+
1381
+ endMeasure(name) {
1382
+ if (!this.enabled) return
1383
+ const start = this.measures.get(name);
1384
+ if (!start) return
1385
+
1386
+ const duration = performance.now() - start;
1387
+ this.measures.delete(name);
1388
+
1389
+ return duration
1390
+ }
1391
+
1392
+ recordRender(componentName, duration) {
1393
+ if (!this.enabled) return
1394
+
1395
+ this.renderTimes.push({
1396
+ component: componentName,
1397
+ duration,
1398
+ timestamp: Date.now(),
1399
+ });
1400
+
1401
+ if (this.renderTimes.length > this.maxSamples) {
1402
+ this.renderTimes.shift();
1403
+ }
1404
+ }
1405
+
1406
+ getStats() {
1407
+ if (!this.enabled) return null
1408
+
1409
+ const total = this.renderTimes.reduce((sum, r) => sum + r.duration, 0);
1410
+ const avg = total / this.renderTimes.length;
1411
+ const max = Math.max(...this.renderTimes.map((r) => r.duration));
1412
+ const min = Math.min(...this.renderTimes.map((r) => r.duration));
1413
+
1414
+ return { total, avg, max, min, count: this.renderTimes.length }
1415
+ }
1416
+
1417
+ getSlowestComponents(limit = 10) {
1418
+ if (!this.enabled) return []
1419
+
1420
+ const byComponent = new Map();
1421
+
1422
+ this.renderTimes.forEach(({ component, duration }) => {
1423
+ if (!byComponent.has(component)) {
1424
+ byComponent.set(component, { total: 0, count: 0, max: 0 });
1425
+ }
1426
+ const stats = byComponent.get(component);
1427
+ stats.total += duration;
1428
+ stats.count++;
1429
+ stats.max = Math.max(stats.max, duration);
1430
+ });
1431
+
1432
+ return Array.from(byComponent.entries())
1433
+ .map(([name, stats]) => ({
1434
+ name,
1435
+ avg: stats.total / stats.count,
1436
+ max: stats.max,
1437
+ count: stats.count,
1438
+ }))
1439
+ .sort((a, b) => b.avg - a.avg)
1440
+ .slice(0, limit)
1441
+ }
1442
+
1443
+ logStats() {
1444
+ if (!this.enabled) return
1445
+
1446
+ const stats = this.getStats();
1447
+ if (!stats) return
1448
+
1449
+ console.group('🔍 Ryunix Performance Stats');
1450
+ console.log(`Total renders: ${stats.count}`);
1451
+ console.log(`Avg render time: ${stats.avg.toFixed(2)}ms`);
1452
+ console.log(
1453
+ `Min: ${stats.min.toFixed(2)}ms | Max: ${stats.max.toFixed(2)}ms`,
1454
+ );
1455
+
1456
+ const slowest = this.getSlowestComponents(5);
1457
+ if (slowest.length > 0) {
1458
+ console.log('\n⚠️ Slowest components:');
1459
+ slowest.forEach((comp, i) => {
1460
+ console.log(
1461
+ `${i + 1}. ${comp.name}: ${comp.avg.toFixed(2)}ms avg (${comp.count} renders)`,
1462
+ );
1463
+ });
1464
+ }
1465
+ console.groupEnd();
1466
+ }
1467
+
1468
+ clear() {
1469
+ this.renderTimes = [];
1470
+ this.measures.clear();
1471
+ }
1472
+
1473
+ enable() {
1474
+ this.enabled = true;
1475
+ }
1476
+
1477
+ disable() {
1478
+ this.enabled = false;
1479
+ }
1480
+ }
1481
+
1482
+ // Global profiler instance
1483
+ const profiler = new Profiler();
1484
+
1485
+ /**
1486
+ * Hook to profile component render
1487
+ */
1488
+ const useProfiler = (componentName) => {
1489
+ const startTime = performance.now();
1490
+
1491
+ return () => {
1492
+ const duration = performance.now() - startTime;
1493
+ profiler.recordRender(componentName, duration);
1494
+ }
1495
+ };
1496
+
1497
+ /**
1498
+ * HOC to profile component
1499
+ */
1500
+ const withProfiler = (Component, name) => {
1501
+ return (props) => {
1502
+ profiler.startMeasure(name);
1503
+ const result = Component(props);
1504
+ const duration = profiler.endMeasure(name);
1505
+ if (duration) profiler.recordRender(name, duration);
1506
+ return result
1507
+ }
1508
+ };
1509
+
1510
+ const workLoop = (deadline) => {
1511
+ const state = getState();
1512
+ let shouldYield = false;
1513
+
1514
+ while (state.nextUnitOfWork && !shouldYield) {
1515
+ state.nextUnitOfWork = performUnitOfWork(state.nextUnitOfWork);
1516
+ shouldYield = deadline.timeRemaining() < 1;
1517
+ }
1518
+
1519
+ if (!state.nextUnitOfWork && state.wipRoot) {
1520
+ commitRoot();
1521
+ }
1522
+
1523
+ requestIdleCallback(workLoop);
1524
+ };
1525
+
1526
+ requestIdleCallback(workLoop);
1527
+
1528
+ const performUnitOfWork = (fiber) => {
1529
+ const componentName = fiber.type?.name || fiber.type?.displayName || 'Unknown';
1530
+
1531
+ profiler.startMeasure(componentName);
1532
+
1533
+ const isFunctionComponent = fiber.type instanceof Function;
1534
+ if (isFunctionComponent) {
1535
+ updateFunctionComponent(fiber);
1536
+ } else {
1537
+ updateHostComponent(fiber);
1538
+ }
1539
+
1540
+ const duration = profiler.endMeasure(componentName);
1541
+ if (duration) profiler.recordRender(componentName, duration);
1542
+
1543
+ if (fiber.child) {
1544
+ return fiber.child
1545
+ }
1546
+ let nextFiber = fiber;
1547
+ while (nextFiber) {
1548
+ if (nextFiber.sibling) {
1549
+ return nextFiber.sibling
1550
+ }
1551
+ nextFiber = nextFiber.parent;
1552
+ }
1553
+ };
1554
+
1555
+ const scheduleWork = (root, priority = Priority.NORMAL) => {
1556
+ const state = getState();
1557
+ state.nextUnitOfWork = root;
1558
+ state.wipRoot = root;
1559
+ state.deletions = [];
1560
+ state.hookIndex = 0;
1561
+ state.effects = [];
1562
+
1563
+ // Higher priority = faster scheduling
1564
+ if (priority <= Priority.USER_BLOCKING) {
1565
+ requestIdleCallback(workLoop);
1566
+ } else {
1567
+ setTimeout(() => requestIdleCallback(workLoop), 0);
1568
+ }
1569
+ };
1570
+
1571
+ /**
1572
+ * The `render` function in JavaScript updates the DOM with a new element and schedules work to be done
1573
+ * on the element.
1574
+ * @param element - The `element` parameter in the `render` function is the element that you want to
1575
+ * render in the specified container. It could be a DOM element, a component, or any other valid
1576
+ * element that you want to display on the screen.
1577
+ * @param container - The `container` parameter in the `render` function is the DOM element where the
1578
+ * `element` will be rendered. It is the target container where the element will be appended as a
1579
+ * child.
1580
+ * @returns The `render` function is returning the `state.wipRoot` object.
1581
+ */
1582
+ const render = (element, container) => {
1583
+ const state = getState();
1584
+ state.wipRoot = {
1585
+ dom: container,
1586
+ props: {
1587
+ children: [element],
1588
+ },
1589
+ alternate: state.currentRoot,
1590
+ };
1591
+
1592
+ state.nextUnitOfWork = state.wipRoot;
1593
+ state.deletions = [];
1594
+ scheduleWork(state.wipRoot);
1595
+ return state.wipRoot
1596
+ };
1597
+
1598
+ /**
1599
+ * The `init` function initializes a rendering process for a main element within a specified container
1600
+ * root element.
1601
+ * @param MainElement - MainElement is the main component or element that you want to render on the
1602
+ * webpage. It could be a React component, a DOM element, or any other element that you want to display
1603
+ * on the page.
1604
+ * @param [root=__ryunix] - The `root` parameter in the `init` function is a default parameter with the
1605
+ * value `'__ryunix'`. If no value is provided for `root` when calling the `init` function, it will
1606
+ * default to `'__ryunix'`.
1607
+ * @returns The `renderProcess` function is being returned from the `init` function.
1608
+ */
1609
+ const init = (MainElement, root = '__ryunix') => {
1610
+ const state = getState();
1611
+ state.containerRoot = document.getElementById(root);
1612
+ const renderProcess = render(MainElement, state.containerRoot);
1613
+ return renderProcess
1614
+ };
1615
+
1616
+ const safeRender = (component, props, onError) => {
1617
+ try {
1618
+ return component(props)
1619
+ } catch (error) {
1620
+ if (process.env.NODE_ENV !== 'production') {
1621
+ console.error('Component error:', error);
1622
+ }
1623
+ if (onError) onError(error);
1624
+ return null
1625
+ }
1626
+ };
1627
+
1364
1628
  /**
1365
1629
  * memo - Memoize component to prevent unnecessary re-renders
1366
1630
  */
@@ -1431,7 +1695,11 @@ let isBatching = false;
1431
1695
  let pendingUpdates = [];
1432
1696
 
1433
1697
  /**
1434
- * Batch multiple state updates into single render
1698
+ * The `batchUpdates` function in JavaScript allows for batching multiple updates and flushing them all
1699
+ * at once.
1700
+ * @param callback - The `callback` parameter in the `batchUpdates` function is a function that will be
1701
+ * executed within a batch update. This function can contain multiple updates that need to be processed
1702
+ * together in a batch to improve performance and avoid unnecessary re-renders.
1435
1703
  */
1436
1704
  const batchUpdates = (callback) => {
1437
1705
  const wasBatching = isBatching;
@@ -1449,7 +1717,9 @@ const batchUpdates = (callback) => {
1449
1717
  };
1450
1718
 
1451
1719
  /**
1452
- * Flush all pending updates
1720
+ * The `flushUpdates` function processes and executes pending updates stored in an array.
1721
+ * @returns If the `pendingUpdates` array is empty, the `flushUpdates` function will return nothing
1722
+ * (undefined).
1453
1723
  */
1454
1724
  const flushUpdates = () => {
1455
1725
  if (pendingUpdates.length === 0) return
@@ -1498,5 +1768,5 @@ var Ryunix = /*#__PURE__*/Object.freeze({
1498
1768
 
1499
1769
  window.Ryunix = Ryunix;
1500
1770
 
1501
- export { Children, Fragment, hooks as Hooks, Image, NavLink, Priority, RouterProvider, Suspense, batchUpdates, createContext, createElement, Ryunix as default, init, lazy, memo, profiler, render, safeRender, useCallback, useDeferredValue, useEffect, useHash, useMemo, useMetadata, useProfiler, useQuery, useReducer, useRef, useRouter, useStore, useStorePriority, useTransition, withProfiler };
1771
+ export { Children, Fragment, hooks as Hooks, Image, MDXContent, MDXProvider, NavLink, Priority, RouterProvider, Suspense, batchUpdates, createContext, createElement, Ryunix as default, defaultComponents, getMDXComponents, init, lazy, memo, profiler, render, safeRender, useCallback, useDeferredValue, useEffect, useHash, useMDXComponents, useMemo, useMetadata, useProfiler, useQuery, useReducer, useRef, useRouter, useStore, useStorePriority, useTransition, withProfiler };
1502
1772
  //# sourceMappingURL=Ryunix.esm.js.map