@gaddario98/react-pages
Version:
A powerful, performance-optimized React component library for creating dynamic pages that work seamlessly across web (React DOM) and React Native with integrated form management, query handling, SEO metadata, lazy loading, and content rendering.
420 lines (414 loc) • 15.8 kB
TypeScript
import * as react_jsx_runtime from 'react/jsx-runtime';
import React, { ReactNode, ComponentType, Suspense } from 'react';
/**
* Optimized shallow equality check for objects and functions
* @param objA - First object to compare
* @param objB - Second object to compare
* @returns True if objects are shallow equal
*/
declare function shallowEqual(objA: any, objB: any): boolean;
/**
* Checks if a value is stable for React dependency arrays
* @param value - Value to check for stability
* @returns True if value is considered stable
*/
declare function isStableValue(value: any): boolean;
/**
* Creates an optimized dependency array by filtering unstable values
* @param deps - Array of dependencies to optimize
* @returns Filtered array of stable dependencies
*/
declare function optimizeDeps(deps: any[]): any[];
/**
* Custom prop comparator for React.memo() to prevent unnecessary re-renders
* Compares props shallowly and ignores function references if they have the same name
* @param prevProps - Previous component props
* @param nextProps - Next component props
* @returns True if props are equal (component should NOT re-render)
*/
declare function memoPropsComparator<P extends Record<string, any>>(prevProps: Readonly<P>, nextProps: Readonly<P>): boolean;
/**
* Deep equality check for complex objects
* Use sparingly - prefer shallow equality for performance
* Uses fast-deep-equal library for optimized deep comparison with circular reference protection
* @param objA - First object
* @param objB - Second object
* @returns True if objects are deeply equal
*/
declare function deepEqual(objA: any, objB: any): boolean;
/**
* Memoization cache for expensive computations
* Simple LRU cache with configurable size
*/
declare class MemoizationCache<K, V> {
private cache;
private maxSize;
constructor(maxSize?: number);
get(key: K): V | undefined;
set(key: K, value: V): void;
has(key: K): boolean;
clear(): void;
}
/**
* Creates a memoized function with custom cache key generator
* @param fn - Function to memoize
* @param cacheKeyFn - Optional function to generate cache key from arguments
* @returns Memoized function
*/
declare function memoize<Args extends any[], Result>(fn: (...args: Args) => Result, cacheKeyFn?: (...args: Args) => string): (...args: Args) => Result;
/**
* Configuration Merge Utility (T084)
* Provides deep merging of configuration objects with proper precedence
* Handles arrays, objects, and primitives correctly
*
* @module utils/merge
*/
/**
* Options for controlling merge behavior
*/
interface MergeOptions {
/** If true, arrays are concatenated instead of replaced (default: false) */
concatArrays?: boolean;
/** If true, null/undefined values overwrite existing values (default: false) */
overwriteWithEmpty?: boolean;
/** Maximum depth for recursion to prevent infinite loops (default: 10) */
maxDepth?: number;
/** Keys to skip during merge */
skipKeys?: Set<string>;
}
/**
* Deep merge of multiple objects with precedence from left to right
* Later objects override earlier ones, with special handling for nested objects
*
* @param objects - Objects to merge (left to right precedence)
* @param options - Merge options
* @returns Merged object
*
* @example
* ```typescript
* const defaults = { theme: 'light', nested: { color: 'black' } };
* const overrides = { theme: 'dark', nested: { fontSize: 14 } };
* const result = deepMerge(defaults, overrides);
* // Result: { theme: 'dark', nested: { color: 'black', fontSize: 14 } }
* ```
*/
declare function deepMerge<T extends Record<string, any>>(...objects: (T | undefined | null)[]): T;
/**
* Deep merge with custom options
* @param target - Target object to merge into
* @param sources - Source objects to merge
* @param options - Merge options
* @returns Merged object
*/
declare function deepMergeWithOptions<T extends Record<string, any>>(target: T, sources: T[], options?: MergeOptions): T;
/**
* Shallow merge of objects (only top-level keys)
* @param objects - Objects to merge
* @returns Merged object
*/
declare function shallowMerge<T extends Record<string, any>>(...objects: (T | undefined | null)[]): T;
/**
* Merge arrays of objects by a key (useful for config arrays)
* @param baseArray - Base array
* @param overrideArray - Array to merge in
* @param keyName - Key to match on
* @returns Merged array
*
* @example
* ```typescript
* const base = [{ id: 'a', name: 'Alice' }, { id: 'b', name: 'Bob' }];
* const override = [{ id: 'a', name: 'Alicia' }, { id: 'c', name: 'Charlie' }];
* const result = mergeArraysByKey(base, override, 'id');
* // Result: [{ id: 'a', name: 'Alicia' }, { id: 'b', name: 'Bob' }, { id: 'c', name: 'Charlie' }]
* ```
*/
declare function mergeArraysByKey<T extends Record<string, any>>(baseArray: T[], overrideArray: T[], keyName: string): T[];
/**
* Check if two objects are deeply equal
* Useful for detecting if a merge actually changed anything
* Note: Use the fast-deep-equal version from optimization.ts instead
* @deprecated - Use deepEqual from utils/optimization.ts instead
* @param obj1 - First object
* @param obj2 - Second object
* @returns True if objects are deeply equal
*/
declare function deepEqualFallback(obj1: any, obj2: any): boolean;
/**
* Stable Cache for memoizing objects across renders
* Prevents unnecessary object creation in hooks
*
* @example
* ```typescript
* const cache = useRef(new StableCache<FormConfig>());
* const formConfig = cache.current.getOrSet('key', () => createConfig());
* ```
*/
declare class StableCache<T> {
private cache;
/**
* Get value from cache, or set it if not present
* @param key - Cache key
* @param factory - Function to create value if not cached
* @returns Cached or newly created value
*/
getOrSet(key: string, factory: () => T): T;
/**
* Get value from cache
* @param key - Cache key
* @returns Cached value or undefined
*/
get(key: string): T | undefined;
/**
* Set value in cache
* @param key - Cache key
* @param value - Value to cache
*/
set(key: string, value: T): void;
/**
* Check if key exists in cache
* @param key - Cache key
* @returns True if key exists
*/
has(key: string): boolean;
/**
* Clear all cached values
*/
clear(): void;
/**
* Get cache size
* @returns Number of cached items
*/
size(): number;
}
/**
* Configuration for lazy loading behavior
*/
interface LazyLoadConfig {
/** Whether to enable lazy loading (default: true) */
enabled?: boolean;
/** Preload on hover (only for interactive elements) */
preloadOnHover?: boolean;
/** Fallback component to show while loading */
suspenseFallback?: ReactNode;
/** Custom error boundary component */
errorBoundary?: ComponentType<{
error: Error;
retry: () => void;
}>;
/** T095: Custom Suspense fallback factory function for dynamic fallbacks */
fallbackFactory?: (componentName: string) => ReactNode;
/** T095: Timeout in ms to show fallback (useful for fast components) */
fallbackDelay?: number;
}
/**
* Enhanced lazy wrapper with optional preloading capability
*
* Wraps React.lazy() with a Suspense boundary and optional preload support.
* Helps reduce initial bundle size by code-splitting components.
*
* @example
* ```tsx
* // Basic lazy loading
* const HeavyComponent = lazyWithPreload(() => import('./Heavy'));
*
* function App() {
* return (
* <lazyWithPreload.Suspense fallback={<Loader />}>
* <HeavyComponent />
* </lazyWithPreload.Suspense>
* );
* }
* ```
*
* @example
* ```tsx
* // With preloading on hover
* const LazyModal = lazyWithPreload(
* () => import('./Modal'),
* { preloadOnHover: true }
* );
*
* function Button() {
* return (
* <button onMouseEnter={() => LazyModal.preload?.()}>
* Open Modal
* </button>
* );
* }
* ```
*/
declare function lazyWithPreload<P extends Record<string, any>>(importFunc: () => Promise<{
default: ComponentType<P>;
}>, config?: LazyLoadConfig): ComponentType<P> & {
preload: () => Promise<{
default: ComponentType<P>;
}>;
Suspense: typeof Suspense;
};
/**
* Create a batch of lazy components with shared preloading strategy
*
* Useful for code-splitting a route or feature module
*
* @example
* ```tsx
* const lazyPages = lazyBatch({
* UserList: () => import('./pages/UserList'),
* UserDetail: () => import('./pages/UserDetail'),
* UserForm: () => import('./pages/UserForm'),
* }, {
* preloadOnHover: true,
* suspenseFallback: <PageLoader />
* });
*
* function App() {
* return (
* <Routes>
* <Route path="/users" element={<lazyPages.UserList />} />
* <Route path="/users/:id" element={<lazyPages.UserDetail />} />
* </Routes>
* );
* }
* ```
*/
declare function lazyBatch<T extends Record<string, () => Promise<{
default: ComponentType<any>;
}>>>(modules: T, config?: LazyLoadConfig): T;
/**
* Preload multiple components in parallel
*
* Useful for preloading components before user interaction
*
* @example
* ```tsx
* // Preload critical components on app mount
* useEffect(() => {
* preloadComponents([UserComponent.preload, SettingsComponent.preload]);
* }, []);
* ```
*/
declare function preloadComponents(preloaders: Array<() => Promise<any>>): Promise<any[]>;
/**
* Higher-order component to wrap lazy-loaded components with error boundary
*
* @example
* ```tsx
* const SafeLazyComponent = withLazyErrorBoundary(LazyComponent, {
* fallback: <ErrorMessage />
* });
* ```
*/
declare function withLazyErrorBoundary<P extends Record<string, any>>(Component: ComponentType<P>, config?: {
fallback?: ReactNode;
}): (props: P) => string | number | true | Iterable<React.ReactNode> | react_jsx_runtime.JSX.Element;
/**
* T095: Hook to preload a lazy component on demand
* Useful for imperative preloading scenarios
*
* @example
* ```tsx
* function MyComponent() {
* const preload = usePreloadLazy(HeavyComponent);
*
* return (
* <button onMouseEnter={preload}>
* Hover to preload
* </button>
* );
* }
* ```
*/
declare function usePreloadLazy<P extends Record<string, any>>(component: ComponentType<P> & {
preload?: () => Promise<any>;
}): () => void;
/**
* T095: Hook to preload multiple lazy components
* Useful for preloading a set of components before user navigation
*
* @example
* ```tsx
* function Navigation() {
* usePreloadLazyBatch([UserList, UserDetail, UserForm]);
*
* return <nav>...</nav>;
* }
* ```
*/
declare function usePreloadLazyBatch(components: Array<ComponentType<any> & {
preload?: () => Promise<any>;
}>): () => void;
/**
* T095: Hook to preload lazy components on viewport intersection
* Useful for preloading components that are likely to be scrolled into view
*
* @example
* ```tsx
* function LazySection() {
* const ref = usePreloadOnViewport(ExpensiveComponent);
*
* return <div ref={ref}>Content goes here</div>;
* }
* ```
*/
declare function usePreloadOnViewport<P extends Record<string, any>>(component: ComponentType<P> & {
preload?: () => Promise<any>;
}): React.RefObject<HTMLDivElement>;
/**
* Deprecation warnings for v1.x → v2.0 migration
* This file manages deprecation notices and warnings for removed/changed v1.x APIs
*
* @module utils/deprecations
*/
/**
* Log a deprecation warning (only in development, only once per feature)
* @param featureName - Name of the deprecated feature
* @param message - Deprecation message with migration guidance
* @param replacement - Recommended replacement code/feature
*/
declare function deprecationWarning(featureName: string, message: string, replacement?: string): void;
/**
* Warnings for specific v1.x API patterns
*/
declare const V1_DEPRECATIONS: {
readonly HELMET_PROVIDER: {
readonly name: "react-helmet-async wrapper";
readonly message: "The <HelmetProvider> wrapper is no longer needed. Metadata injection is now automatic in PageGenerator.";
readonly replacement: "Remove <HelmetProvider> from your app:\n// Before (v1.x):\n<HelmetProvider>\n <PageGenerator {...pageProps} />\n</HelmetProvider>\n\n// After (v2.x):\n<PageGenerator {...pageProps} />";
};
readonly INLINE_METADATA: {
readonly name: "Inline metadata via viewSettings";
readonly message: "Setting metadata directly in viewSettings (metaTitle, metaDescription) is deprecated. Use the new PageProps.meta field instead.";
readonly replacement: "Update your PageProps:\n// Before (v1.x):\n{\n id: \"page\",\n viewSettings: {\n metaTitle: \"...\",\n metaDescription: \"...\"\n }\n}\n\n// After (v2.x):\n{\n id: \"page\",\n meta: {\n title: \"...\",\n description: \"...\"\n }\n}";
};
readonly MANUAL_LAZY_LOADING: {
readonly name: "Manual React.lazy() for content";
readonly message: "Wrapping content in React.lazy() and Suspense manually is no longer needed. Use the ContentItem.lazy configuration instead.";
readonly replacement: "// Before (v1.x):\nconst Component = lazy(() => import('./Heavy'));\n<Suspense fallback={<div>Loading...</div>}>\n <Component />\n</Suspense>\n\n// After (v2.x):\n{\n type: \"custom\",\n component: <HeavyComponent />,\n lazy: true,\n lazyTrigger: \"viewport\"\n}";
};
readonly CUSTOM_FORM_DEBOUNCE: {
readonly name: "Custom form debouncing";
readonly message: "Custom debouncing logic in form handlers is now built-in. Use the form.debounceDelay property instead.";
readonly replacement: "// Before (v1.x): You had to implement debouncing manually\n// After (v2.x):\n{\n form: {\n fields: [...],\n debounceDelay: 300 // Built-in debouncing\n }\n}";
};
readonly QUERY_DEPENDENCY_TRACKING: {
readonly name: "Implicit query dependency tracking";
readonly message: "Components that depend on specific queries should declare those dependencies explicitly using ContentItem.usedQueries for better performance.";
readonly replacement: "// Before (v1.x): All content items re-rendered on any query change\n// After (v2.x): Declare specific dependencies\n{\n type: \"custom\",\n component: <MyComponent />,\n usedQueries: [\"user\", \"posts\"] // Only re-render when these change\n}";
};
readonly HELMET_IMPORT: {
readonly name: "Direct react-helmet-async import";
readonly message: "Importing react-helmet-async directly is no longer recommended. The library handles metadata automatically.";
readonly replacement: "// Before (v1.x):\nimport { Helmet } from 'react-helmet-async';\n<Helmet>\n <title>Page Title</title>\n</Helmet>\n\n// After (v2.x):\nconst pageProps = {\n meta: {\n title: \"Page Title\"\n }\n}";
};
};
/**
* Check if the app is using a deprecated pattern and warn
* @param pattern - The deprecated pattern to check
*/
declare function warnIfUsingDeprecatedPattern(pattern: keyof typeof V1_DEPRECATIONS): void;
/**
* Reset deprecation warnings (useful for testing)
*/
declare function resetDeprecationWarnings(): void;
export { MemoizationCache, StableCache, V1_DEPRECATIONS, deepEqual, deepEqualFallback, deepMerge, deepMergeWithOptions, deprecationWarning, isStableValue, lazyBatch, lazyWithPreload, memoPropsComparator, memoize, mergeArraysByKey, optimizeDeps, preloadComponents, resetDeprecationWarnings, shallowEqual, shallowMerge, usePreloadLazy, usePreloadLazyBatch, usePreloadOnViewport, warnIfUsingDeprecatedPattern, withLazyErrorBoundary };
export type { LazyLoadConfig, MergeOptions };