UNPKG

frontend-hamroun

Version:

A lightweight frontend JavaScript framework with React-like syntax

1,258 lines (1,241 loc) 40.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function jsx(type, props) { console.log('JSX Transform:', { type, props }); const processedProps = { ...props }; // Handle children properly if (arguments.length > 2) { processedProps.children = Array.prototype.slice.call(arguments, 2); } return { type, props: processedProps }; } const Fragment = ({ children }) => children; async function createElement(vnode) { console.log('Creating element from:', vnode); // Handle primitives and null if (vnode == null) { return document.createTextNode(''); } if (typeof vnode === 'boolean') { return document.createTextNode(''); } if (typeof vnode === 'number' || typeof vnode === 'string') { return document.createTextNode(String(vnode)); } // Handle arrays if (Array.isArray(vnode)) { const fragment = document.createDocumentFragment(); for (const child of vnode) { const node = await createElement(child); fragment.appendChild(node); } return fragment; } // Handle VNode if ('type' in vnode && vnode.props !== undefined) { const { type, props } = vnode; // Handle function components if (typeof type === 'function') { try { const result = await type(props || {}); const node = await createElement(result); if (node instanceof Element) { node.setAttribute('data-component-id', type.name || type.toString()); } return node; } catch (error) { console.error('Error rendering component:', error); return document.createTextNode(''); } } // Create DOM element const element = document.createElement(type); // Handle props for (const [key, value] of Object.entries(props || {})) { if (key === 'children') continue; if (key.startsWith('on') && typeof value === 'function') { const eventName = key.toLowerCase().slice(2); // Remove existing event listener if any const existingHandler = element.__events?.[eventName]; if (existingHandler) { element.removeEventListener(eventName, existingHandler); } // Add new event listener element.addEventListener(eventName, value); if (!element.__events) { element.__events = {}; } element.__events[eventName] = value; } else if (key === 'style' && typeof value === 'object') { Object.assign(element.style, value); } else if (key === 'className') { element.setAttribute('class', String(value)); } else if (key !== 'key' && key !== 'ref') { element.setAttribute(key, String(value)); } } // Handle children const children = props?.children; if (children != null) { const childArray = Array.isArray(children) ? children.flat() : [children]; for (const child of childArray) { const childNode = await createElement(child); element.appendChild(childNode); } } return element; } // Handle other objects by converting to string return document.createTextNode(String(vnode)); } let isBatching = false; const queue = []; function batchUpdates(fn) { if (isBatching) { queue.push(fn); return; } isBatching = true; try { fn(); while (queue.length > 0) { const nextFn = queue.shift(); nextFn?.(); } } finally { isBatching = false; } } const contexts = new Map(); function createContext(defaultValue) { const contextId = Symbol('context'); const Provider = ({ value, children }) => { contexts.set(contextId, value); return children; }; const Consumer = ({ children }) => { const value = contexts.get(contextId) ?? defaultValue; return children(value); }; const context = { Provider, Consumer, displayName: 'Context' }; return context; } function useContext(context) { // In a real implementation, this would access the context value from the component tree // For now, returning a default value to satisfy TypeScript return {}; } // Current render ID counter let currentRender = 0; let isSSR = false; // State storage const states = new Map(); const stateIndices = new Map(); const effects = new Map(); const memos = new Map(); const refs = new Map(); // Rendering callbacks let globalRenderCallback = null; let globalContainer = null; let currentElement = null; function setRenderCallback(callback, element, container) { globalRenderCallback = callback; globalContainer = container; currentElement = element; } function prepareRender(component = null, isServerSideRender = false) { currentRender++; isSSR = isServerSideRender; stateIndices.set(currentRender, 0); // Initialize state array for this render if it doesn't exist if (!states.has(currentRender)) { states.set(currentRender, []); } return currentRender; } function finishRender() { // Don't reset currentRender to 0 immediately to preserve state // currentRender = 0; } function useState(initial) { if (!currentRender) { throw new Error("useState must be called within a render"); } if (!states.has(currentRender)) { states.set(currentRender, []); } const componentStates = states.get(currentRender); const index = stateIndices.get(currentRender) || 0; if (index >= componentStates.length) { componentStates.push(initial); } const state = componentStates[index]; const setState = (newValue) => { // For SSR, don't actually update state if (isSSR) { return; } const nextValue = typeof newValue === 'function' ? newValue(componentStates[index]) : newValue; if (componentStates[index] === nextValue) return; componentStates[index] = nextValue; if (isBatching) { batchUpdates(() => rerender(currentRender)); } else { rerender(currentRender); } }; stateIndices.set(currentRender, index + 1); return [state, setState]; } function useEffect(callback, deps) { if (!currentRender) throw new Error("useEffect must be called within a render"); // Skip effects during SSR if (isSSR) { const effectIndex = stateIndices.get(currentRender) || 0; stateIndices.set(currentRender, effectIndex + 1); return; } const effectIndex = stateIndices.get(currentRender) || 0; if (!effects.has(currentRender)) { effects.set(currentRender, []); } const componentEffects = effects.get(currentRender); const prevEffect = componentEffects[effectIndex]; if (!prevEffect || !deps || !prevEffect.deps || deps.some((dep, i) => dep !== prevEffect.deps[i])) { if (prevEffect?.cleanup) { prevEffect.cleanup(); } // Schedule effect execution after render is complete queueMicrotask(() => { const cleanup = callback() || undefined; componentEffects[effectIndex] = { cleanup, deps: deps || [] }; }); } stateIndices.set(currentRender, effectIndex + 1); } function useMemo(factory, deps) { if (!currentRender) throw new Error("useMemo must be called within a render"); const memoIndex = stateIndices.get(currentRender) || 0; if (!memos.has(currentRender)) { memos.set(currentRender, []); } const componentMemos = memos.get(currentRender); const prevMemo = componentMemos[memoIndex]; if (!prevMemo || (deps && deps.some((dep, i) => !Object.is(dep, prevMemo.deps[i])))) { const value = factory(); componentMemos[memoIndex] = { value, deps: deps || [] }; stateIndices.set(currentRender, memoIndex + 1); return value; } stateIndices.set(currentRender, memoIndex + 1); return prevMemo.value; } function useRef(initial) { if (!currentRender) throw new Error("useRef must be called within a render"); const refIndex = stateIndices.get(currentRender) || 0; if (!refs.has(currentRender)) { refs.set(currentRender, []); } const componentRefs = refs.get(currentRender); if (refIndex >= componentRefs.length) { const ref = { current: initial }; componentRefs.push(ref); stateIndices.set(currentRender, refIndex + 1); return ref; } const ref = componentRefs[refIndex]; stateIndices.set(currentRender, refIndex + 1); return ref; } async function rerender(rendererId) { try { // Skip rerender during SSR if (isSSR) { return; } // Clean up effects const componentEffects = effects.get(rendererId); if (componentEffects) { componentEffects.forEach(effect => { if (effect.cleanup) effect.cleanup(); }); effects.set(rendererId, []); } // Trigger re-render if (globalRenderCallback && globalContainer && currentElement) { await globalRenderCallback(currentElement, globalContainer); } } catch (error) { console.error('Error during rerender:', error); } } function useErrorBoundary() { const [error, setError] = useState(null); return [error, () => setError(null)]; } // Render function for client-side rendering async function render(element, container) { try { // Set up render callback for state updates setRenderCallback(render, element, container); // Prepare render context const renderId = prepareRender(element); // Create DOM nodes from virtual elements const domNode = await createElement(element); // Clear container and append new content // Handle both Element and DocumentFragment cases if (container instanceof Element) { container.innerHTML = ''; } else { // For DocumentFragment, clear all children while (container.firstChild) { container.removeChild(container.firstChild); } } container.appendChild(domNode); // Finish render finishRender(); console.log('Render completed successfully'); } catch (error) { console.error('Error during render:', error); throw error; } } // Hydrate function for client-side hydration of SSR content async function hydrate(element, container) { try { console.log('Starting hydration...'); // For now, hydrate works the same as render // In a more advanced implementation, this would preserve existing DOM // and only attach event listeners and initialize state await render(element, container); console.log('Hydration completed successfully'); } catch (error) { console.error('Error during hydration:', error); throw error; } } async function renderToString(element) { prepareRender(element); // Pass the element to establish render context try { const html = await renderNodeToString(element); return html; } finally { } } async function renderNodeToString(node) { // Handle null, undefined, boolean if (node == null || typeof node === 'boolean') { return ''; } // Handle primitives if (typeof node === 'string' || typeof node === 'number') { return escapeHtml(String(node)); } // Handle arrays if (Array.isArray(node)) { const results = await Promise.all(node.map(child => renderNodeToString(child))); return results.join(''); } // Handle objects with type and props (React-like elements) if (node && typeof node === 'object' && 'type' in node) { const { type, props = {} } = node; // Handle function components if (typeof type === 'function') { try { // Set up a new render context for this component const componentRenderId = prepareRender(type); try { const result = await type(props); return await renderNodeToString(result); } finally { finishRender(); } } catch (error) { console.error('Error rendering component:', error); return `<!-- Error rendering component: ${error.message} -->`; } } // Handle DOM elements if (typeof type === 'string') { return await renderDOMElement(type, props); } } // Fallback for other objects if (typeof node === 'object') { return escapeHtml(JSON.stringify(node)); } return escapeHtml(String(node)); } async function renderDOMElement(tagName, props) { const { children, ...attrs } = props; // Self-closing tags const voidElements = new Set([ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr' ]); // Build attributes string const attributeString = Object.entries(attrs) .filter(([key, value]) => { // Filter out React-specific props and event handlers if (key.startsWith('on') || key === 'key' || key === 'ref') return false; if (value == null || value === false) return false; return true; }) .map(([key, value]) => { // Handle className -> class if (key === 'className') key = 'class'; // Handle boolean attributes if (value === true) return key; // Handle style objects if (key === 'style' && typeof value === 'object' && value !== null) { const styleString = Object.entries(value) .map(([prop, val]) => `${kebabCase(prop)}:${val}`) .join(';'); return `style="${escapeHtml(styleString)}"`; } return `${key}="${escapeHtml(String(value))}"`; }) .join(' '); const openTag = `<${tagName}${attributeString ? ' ' + attributeString : ''}>`; // Self-closing elements if (voidElements.has(tagName)) { return openTag.slice(0, -1) + '/>'; } // Elements with children const closeTag = `</${tagName}>`; if (children != null) { const childrenString = await renderNodeToString(children); return openTag + childrenString + closeTag; } return openTag + closeTag; } function escapeHtml(text) { const htmlEscapes = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#x27;', '/': '&#x2F;' }; return text.replace(/[&<>"'/]/g, (match) => htmlEscapes[match]); } function kebabCase(str) { return str.replace(/[A-Z]/g, (match) => `-${match.toLowerCase()}`); } function arePropsEqual(oldProps, newProps) { const oldKeys = Object.keys(oldProps).filter(k => k !== 'children'); const newKeys = Object.keys(newProps).filter(k => k !== 'children'); if (oldKeys.length !== newKeys.length) return false; return oldKeys.every(key => oldProps[key] === newProps[key]); } function diff(oldNode, newNode) { if (oldNode == null || newNode == null) return oldNode !== newNode; if (typeof oldNode !== typeof newNode) return true; if (typeof newNode === 'string' || typeof newNode === 'number') return oldNode !== newNode; if (newNode.type !== oldNode.type) return true; return !arePropsEqual(oldNode.props, newNode.props); } function shouldComponentUpdate(oldProps, newProps) { return !arePropsEqual(oldProps, newProps); } class Component { constructor(props = {}) { this.state = {}; this.element = null; this._mounted = false; this.props = props; } componentDidMount() { // Hook for after component is mounted } async setState(newState) { const prevState = { ...this.state }; this.state = { ...prevState, ...newState }; console.log(`${this.constructor.name} state updated:`, { prev: prevState, next: this.state }); await Promise.resolve(); // Ensure state is updated before re-render if (this._mounted) { await this.update(); } else { await this.update(); } } _replayEvents(oldElement, newElement) { const oldEvents = oldElement.__events || {}; Object.entries(oldEvents).forEach(([event, handler]) => { newElement.addEventListener(event, handler); }); newElement.__events = oldEvents; } _deepCloneWithEvents(node) { const clone = node.cloneNode(false); // Copy events from original element const events = node.__events || {}; clone.__events = events; Object.entries(events).forEach(([event, handler]) => { clone.addEventListener(event, handler); }); // Clone children Array.from(node.childNodes).forEach(child => { if (child instanceof HTMLElement) { clone.appendChild(this._deepCloneWithEvents(child)); } else { clone.appendChild(child.cloneNode(true)); } }); return clone; } async update() { const vdom = this.render(); if (!vdom) return document.createTextNode(''); const rendered = await createElement(vdom); if (rendered instanceof HTMLElement) { return this._updateElement(rendered); } const wrapper = document.createElement('div'); wrapper.appendChild(rendered); return this._updateElement(wrapper); } async _updateElement(rendered) { const newElement = this._deepCloneWithEvents(rendered); newElement.__instance = this; if (!this.element) { this.element = newElement; if (!this._mounted) { this._mounted = true; queueMicrotask(() => this.componentDidMount()); } } else if (this.element.parentNode) { this.element.parentNode.replaceChild(newElement, this.element); this.element = newElement; } return this.element; } render() { throw new Error('Component must implement render() method'); } } /** * Client-side router for single-page applications */ // Create context for router const RouterContext = createContext({ location: { pathname: '/', search: '', hash: '' }, params: {}, navigate: () => { }, match: () => false }); // Parse path pattern into regex function parsePath(path) { // Convert :param syntax to capture groups const pattern = path .replace(/\/+$/, '') // Remove trailing slashes .replace(/^\/+/, '/') // Ensure leading slash .replace(/\/:([^/]+)/g, '/([^/]+)'); // Get param names const paramNames = []; path.replace(/\/:([^/]+)/g, (_, paramName) => { paramNames.push(paramName); return ''; }); return { pattern, paramNames }; } // Match a path against a pattern function matchPath(pathname, route) { const { path, exact = false } = route; const { pattern, paramNames } = parsePath(path); // Create regex with or without exact matching const regex = new RegExp(`^${pattern}${exact ? '$' : ''}`); const match = pathname.match(regex); if (!match) return null; // Extract params const params = {}; paramNames.forEach((name, index) => { params[name] = match[index + 1]; }); return params; } // Router Provider component function RouterProvider({ routes, children }) { // Get initial location from window if available const getInitialLocation = () => { if (typeof window === 'undefined') { return { pathname: '/', search: '', hash: '' }; } return { pathname: window.location.pathname, search: window.location.search, hash: window.location.hash }; }; // Fix: Call the function to get the initial value instead of passing the function itself const [location, setLocation] = useState(getInitialLocation()); const [params, setParams] = useState({}); // Update params when location changes useEffect(() => { for (const route of routes) { const matchedParams = matchPath(location.pathname, route); if (matchedParams) { setParams(matchedParams); break; } } }, [location, routes]); // Listen for popstate events useEffect(() => { if (typeof window === 'undefined') return; const handlePopState = () => { setLocation({ pathname: window.location.pathname, search: window.location.search, hash: window.location.hash }); }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, []); // Navigation function const navigate = (to, options = {}) => { if (typeof window === 'undefined') return; const { replace = false, state = null } = options; if (replace) { window.history.replaceState(state, '', to); } else { window.history.pushState(state, '', to); } // Update location setLocation({ pathname: window.location.pathname, search: window.location.search, hash: window.location.hash }); }; // Match function to test if a path matches the current location const match = (path) => { const { pattern } = parsePath(path); const regex = new RegExp(`^${pattern}`); return regex.test(location.pathname); }; // Router context value const routerValue = { location: { pathname: location.pathname, search: location.search, hash: location.hash }, params, navigate, match }; return jsx(RouterContext.Provider, { value: routerValue, children }); } // Route component function Route({ path, component: Component, props = {} }) { const context = useContext(); const params = context.params; const locationPathname = context.location.pathname; const routeToMatch = { path, component: Component }; const match = matchPath(locationPathname, routeToMatch); if (!match) return null; return jsx(Component, { ...props, params }); } // Switch component function Switch({ children }) { const context = useContext(); const locationPathname = context.location.pathname; // Find the first matching route const child = Array.isArray(children) ? children.find(child => { if (!child || typeof child.type !== 'function' || !child.props.path) return false; const routeObj = { path: child.props.path, component: child.type, exact: child.props.exact || false }; return matchPath(locationPathname, routeObj); }) : children; return child || null; } // Link component function Link({ to, replace = false, state = null, className = '', activeClassName = '', children, ...rest }) { const context = useContext(); const navigate = context.navigate; const match = context.match; const handleClick = (e) => { e.preventDefault(); navigate(to, { replace, state }); }; const isActive = match(to); const classes = [ className, isActive ? activeClassName : '' ].filter(Boolean).join(' '); return jsx('a', { href: to, className: classes || undefined, onClick: handleClick, ...rest, children }); } // Redirect component function Redirect({ to, replace = true }) { const context = useContext(); const navigate = context.navigate; useEffect(() => { navigate(to, { replace }); }, [to]); return null; } // Hooks function useLocation() { const context = useContext(); return context.location; } function useParams() { const context = useContext(); return context.params; } function useNavigate() { const context = useContext(); return context.navigate; } /** * Form handling utilities for the framework */ function useForm(options) { const { initialValues, validate, onSubmit } = options; const [values, setValues] = useState(initialValues); const [errors, setErrors] = useState({}); const [touched, setTouched] = useState({}); const [dirty, setDirty] = useState({}); const [isSubmitting, setIsSubmitting] = useState(false); const [submitCount, setSubmitCount] = useState(0); // Track form validity and dirty state const isValid = Object.keys(errors).length === 0; const isDirty = Object.values(dirty).some(Boolean); // Validate form when values or validate function changes useEffect(() => { if (validate) { const validationErrors = validate(values); setErrors(validationErrors || {}); } }, [values, validate]); // Handle form input changes const handleChange = (e) => { const { name, value, type, checked } = e.target; const fieldValue = type === 'checkbox' ? checked : value; setValues(prev => ({ ...prev, [name]: fieldValue })); setDirty(prev => ({ ...prev, [name]: true })); }; // Handle field blur const handleBlur = (e) => { const { name } = e.target; setTouched(prev => ({ ...prev, [name]: true })); }; // Set field value programmatically const setFieldValue = (field, value) => { setValues(prev => ({ ...prev, [field]: value })); setDirty(prev => ({ ...prev, [field]: true })); }; // Set field error programmatically const setFieldError = (field, error) => { setErrors(prev => ({ ...prev, [field]: error })); }; // Update multiple values at once const updateValues = (newValues) => { setValues(prev => ({ ...prev, ...newValues })); // Mark changed fields as dirty const dirtyFields = {}; Object.keys(newValues).forEach(key => { dirtyFields[key] = true; }); setDirty(prev => ({ ...prev, ...dirtyFields })); }; // Reset form to initial state const resetForm = () => { setValues(initialValues); setErrors({}); setTouched({}); setDirty({}); setIsSubmitting(false); }; // Handle form submission const handleSubmit = async (e) => { e.preventDefault(); // Mark all fields as touched const allTouched = {}; Object.keys(values).forEach(key => { allTouched[key] = true; }); setTouched(allTouched); // Validate before submission let validationErrors = {}; if (validate) { validationErrors = validate(values); setErrors(validationErrors || {}); } // Only proceed if valid if (Object.keys(validationErrors).length === 0 && onSubmit) { setIsSubmitting(true); setSubmitCount(count => count + 1); try { await onSubmit(values, { fields: Object.keys(values).reduce((acc, key) => { acc[key] = { value: values[key], error: errors[key], touched: touched[key] || false, dirty: dirty[key] || false }; return acc; }, {}), isValid, isDirty, isSubmitting: true, submitCount: submitCount + 1 }); } finally { setIsSubmitting(false); } } }; return { values, errors, touched, dirty, isValid, isDirty, isSubmitting, submitCount, handleChange, handleBlur, handleSubmit, setFieldValue, setFieldError, setValues: updateValues, resetForm }; } function createEventBus() { const events = new Map(); const onceHandlers = new WeakMap(); const on = (event, handler) => { if (!events.has(event)) { events.set(event, new Set()); } events.get(event).add(handler); // Return a function to remove this handler return () => off(event, handler); }; const once = (event, handler) => { // Create a wrapper that will call the handler and remove itself const onceWrapper = (...args) => { off(event, onceWrapper); handler(...args); }; // Store the mapping between the original handler and the wrapper onceHandlers.set(handler, onceWrapper); // Register the wrapper return on(event, onceWrapper); }; const off = (event, handler) => { // If no handler is provided, remove all handlers for the event if (!handler) { events.delete(event); return; } // Check if it's a once handler wrapper const wrappedHandler = onceHandlers.get(handler); const handlerToRemove = wrappedHandler || handler; if (events.has(event)) { events.get(event).delete(handlerToRemove); // Clean up empty event sets if (events.get(event).size === 0) { events.delete(event); } } }; const emit = (event, ...args) => { if (!events.has(event)) { return; } // Create a copy of the handlers to avoid issues if handlers modify the set const handlers = Array.from(events.get(event)); // Call each handler with the arguments for (const handler of handlers) { handler(...args); } }; const clear = (event) => { if (event) { events.delete(event); } else { events.clear(); } }; return { on, once, off, emit, clear }; } // Create a global event bus instance const globalEventBus = createEventBus(); // Hook to use the event bus in components function useEvent(event, handler, options = {}) { return options.once ? globalEventBus.once(event, handler) : globalEventBus.on(event, handler); } /** * Global state management solution similar to Redux */ function createStore(reducer, initialState, middlewares = []) { let state = initialState; let listeners = []; // Apply middlewares let dispatch = (action) => { state = reducer(state, action); listeners.forEach(listener => listener()); return action; }; // Chain middlewares if (middlewares.length > 0) { const middlewareAPI = { getState: () => state, dispatch: (action) => dispatch(action), subscribe: (listener) => subscribe(listener) }; const chain = middlewares.map(middleware => middleware(middlewareAPI)); dispatch = chain.reduce((a, b) => (next) => a(b(next)))(dispatch); } // Subscribe to store changes function subscribe(listener) { listeners.push(listener); return function unsubscribe() { listeners = listeners.filter(l => l !== listener); }; } // Initialize store with default state dispatch({ type: '@@INIT' }); return { getState: () => state, dispatch, subscribe }; } const StoreContext = createContext({ store: { getState: () => ({}), dispatch: () => { }, subscribe: () => () => { } }, state: {} }); function StoreProvider({ store, children }) { const [state, setState] = useState(store.getState()); useEffect(() => { const unsubscribe = store.subscribe(() => { setState(store.getState()); }); return unsubscribe; }, [store]); return jsx(StoreContext.Provider, { value: { store, state }, children }); } function useSelector(selector) { const context = useContext(); return selector(context.state); } function useDispatch() { const context = useContext(); return context.store.dispatch; } function useStore() { const context = useContext(); return context.store; } // Common middlewares const logger = (store) => (next) => (action) => { console.group(action.type); console.log('Previous state:', store.getState()); console.log('Action:', action); const result = next(action); console.log('Next state:', store.getState()); console.groupEnd(); return result; }; const thunk = (store) => (next) => (action) => { if (typeof action === 'function') { return action(store.dispatch, store.getState); } return next(action); }; /** * Lifecycle events for components and application */ exports.LifecycleEvents = void 0; (function (LifecycleEvents) { LifecycleEvents["APP_INIT"] = "app:init"; LifecycleEvents["APP_MOUNTED"] = "app:mounted"; LifecycleEvents["APP_UPDATED"] = "app:updated"; LifecycleEvents["APP_ERROR"] = "app:error"; LifecycleEvents["APP_DESTROYED"] = "app:destroyed"; LifecycleEvents["COMPONENT_CREATED"] = "component:created"; LifecycleEvents["COMPONENT_MOUNTED"] = "component:mounted"; LifecycleEvents["COMPONENT_UPDATED"] = "component:updated"; LifecycleEvents["COMPONENT_ERROR"] = "component:error"; LifecycleEvents["COMPONENT_UNMOUNTED"] = "component:unmounted"; LifecycleEvents["ROUTER_BEFORE_CHANGE"] = "router:before-change"; LifecycleEvents["ROUTER_AFTER_CHANGE"] = "router:after-change"; LifecycleEvents["ROUTER_ERROR"] = "router:error"; LifecycleEvents["STORE_INITIALIZED"] = "store:initialized"; LifecycleEvents["STORE_BEFORE_ACTION"] = "store:before-action"; LifecycleEvents["STORE_AFTER_ACTION"] = "store:after-action"; LifecycleEvents["STORE_ERROR"] = "store:error"; })(exports.LifecycleEvents || (exports.LifecycleEvents = {})); // Event emitters function emitAppInit(data) { globalEventBus.emit(exports.LifecycleEvents.APP_INIT, data); } function emitAppMounted(rootElement) { globalEventBus.emit(exports.LifecycleEvents.APP_MOUNTED, rootElement); } function emitAppUpdated() { globalEventBus.emit(exports.LifecycleEvents.APP_UPDATED); } function emitAppError(error) { globalEventBus.emit(exports.LifecycleEvents.APP_ERROR, error); } function emitAppDestroyed() { globalEventBus.emit(exports.LifecycleEvents.APP_DESTROYED); } function emitComponentCreated(info) { globalEventBus.emit(exports.LifecycleEvents.COMPONENT_CREATED, info); } function emitComponentMounted(info, element) { globalEventBus.emit(exports.LifecycleEvents.COMPONENT_MOUNTED, info, element); } function emitComponentUpdated(info, prevProps, newProps) { globalEventBus.emit(exports.LifecycleEvents.COMPONENT_UPDATED, info, prevProps, newProps); } function emitComponentError(info, error) { globalEventBus.emit(exports.LifecycleEvents.COMPONENT_ERROR, info, error); } function emitComponentUnmounted(info) { globalEventBus.emit(exports.LifecycleEvents.COMPONENT_UNMOUNTED, info); } // Event listeners function onAppInit(handler) { return globalEventBus.on(exports.LifecycleEvents.APP_INIT, handler); } function onAppMounted(handler) { return globalEventBus.on(exports.LifecycleEvents.APP_MOUNTED, handler); } function onAppUpdated(handler) { return globalEventBus.on(exports.LifecycleEvents.APP_UPDATED, handler); } function onAppError(handler) { return globalEventBus.on(exports.LifecycleEvents.APP_ERROR, handler); } function onAppDestroyed(handler) { return globalEventBus.on(exports.LifecycleEvents.APP_DESTROYED, handler); } function onComponentCreated(handler) { return globalEventBus.on(exports.LifecycleEvents.COMPONENT_CREATED, handler); } function onComponentMounted(handler) { return globalEventBus.on(exports.LifecycleEvents.COMPONENT_MOUNTED, handler); } function onComponentUpdated(handler) { return globalEventBus.on(exports.LifecycleEvents.COMPONENT_UPDATED, handler); } function onComponentError(handler) { return globalEventBus.on(exports.LifecycleEvents.COMPONENT_ERROR, handler); } function onComponentUnmounted(handler) { return globalEventBus.on(exports.LifecycleEvents.COMPONENT_UNMOUNTED, handler); } // Core JSX and rendering functions // Default export object for compatibility const frontendHamroun = { // JSX jsx, jsxs: jsx, jsxDEV: jsx, Fragment, createElement, // Rendering render, hydrate, renderToString, // Hooks useState, useEffect, useMemo, useRef, useContext, useErrorBoundary, // Context createContext, // Utilities batchUpdates, diff, shouldComponentUpdate, // Component class Component, // Router RouterProvider, Route, Switch, Link, Redirect, useLocation, useParams, useNavigate, // Forms useForm, // Event bus createEventBus, useEvent, eventBus: globalEventBus }; exports.Component = Component; exports.Fragment = Fragment; exports.Link = Link; exports.Redirect = Redirect; exports.Route = Route; exports.RouterProvider = RouterProvider; exports.StoreProvider = StoreProvider; exports.Switch = Switch; exports.batchUpdates = batchUpdates; exports.createContext = createContext; exports.createElement = createElement; exports.createEventBus = createEventBus; exports.createStore = createStore; exports.default = frontendHamroun; exports.diff = diff; exports.emitAppDestroyed = emitAppDestroyed; exports.emitAppError = emitAppError; exports.emitAppInit = emitAppInit; exports.emitAppMounted = emitAppMounted; exports.emitAppUpdated = emitAppUpdated; exports.emitComponentCreated = emitComponentCreated; exports.emitComponentError = emitComponentError; exports.emitComponentMounted = emitComponentMounted; exports.emitComponentUnmounted = emitComponentUnmounted; exports.emitComponentUpdated = emitComponentUpdated; exports.eventBus = globalEventBus; exports.hydrate = hydrate; exports.jsx = jsx; exports.jsxDEV = jsx; exports.jsxs = jsx; exports.logger = logger; exports.onAppDestroyed = onAppDestroyed; exports.onAppError = onAppError; exports.onAppInit = onAppInit; exports.onAppMounted = onAppMounted; exports.onAppUpdated = onAppUpdated; exports.onComponentCreated = onComponentCreated; exports.onComponentError = onComponentError; exports.onComponentMounted = onComponentMounted; exports.onComponentUnmounted = onComponentUnmounted; exports.onComponentUpdated = onComponentUpdated; exports.render = render; exports.renderToString = renderToString; exports.shouldComponentUpdate = shouldComponentUpdate; exports.thunk = thunk; exports.useContext = useContext; exports.useDispatch = useDispatch; exports.useEffect = useEffect; exports.useErrorBoundary = useErrorBoundary; exports.useEvent = useEvent; exports.useForm = useForm; exports.useLocation = useLocation; exports.useMemo = useMemo; exports.useNavigate = useNavigate; exports.useParams = useParams; exports.useRef = useRef; exports.useSelector = useSelector; exports.useState = useState; exports.useStore = useStore; //# sourceMappingURL=index.cjs.map