|
1 |
| -import type { DOMElement, DOMNode, PrimitiveNode } from "./types"; |
| 1 | +import type { DOMElement, DOMNode, FunctionComponent, PrimitiveNode } from "./types"; |
2 | 2 |
|
3 | 3 | /**
|
4 | 4 | * Extracts the body, parameters, and async status from a function
|
@@ -106,13 +106,84 @@ export const isElementNode = (node: unknown): node is DOMElement => (
|
106 | 106 | );
|
107 | 107 |
|
108 | 108 | /**
|
109 |
| - * Checks if a node is valid (not null, undefined, or false) |
110 |
| - * @param {unknown} node - The node to check |
111 |
| - * @returns {boolean} True if the node is valid |
| 109 | + * Enhanced DOM node validation with type-specific checks |
| 110 | + * @param node - The node to validate |
| 111 | + * @param options - Validation options |
| 112 | + * @returns True if the node is valid according to the specified criteria |
112 | 113 | */
|
113 |
| -export const isValidNode = (node: unknown): boolean => ( |
114 |
| - node !== null && node !== undefined && node !== false |
115 |
| -); |
| 114 | +export const isValidNode = ( |
| 115 | + node: unknown, |
| 116 | + options: { |
| 117 | + allowEmptyString?: boolean; |
| 118 | + allowZero?: boolean; |
| 119 | + allowBoolean?: boolean; |
| 120 | + allowPromise?: boolean; |
| 121 | + allowFunction?: boolean; |
| 122 | + } = {} |
| 123 | +): node is DOMNode => { |
| 124 | + // Default options |
| 125 | + const { |
| 126 | + allowEmptyString = false, |
| 127 | + allowZero = true, |
| 128 | + allowBoolean = false, |
| 129 | + allowPromise = true, |
| 130 | + allowFunction = true, |
| 131 | + } = options; |
| 132 | + |
| 133 | + // Null/undefined check |
| 134 | + if (node == null) return false; |
| 135 | + |
| 136 | + // Boolean check |
| 137 | + if (typeof node === 'boolean') return allowBoolean; |
| 138 | + |
| 139 | + // Number check (special handling for 0) |
| 140 | + if (typeof node === 'number') { |
| 141 | + if (node === 0) return allowZero; |
| 142 | + return !Number.isNaN(node); |
| 143 | + } |
| 144 | + |
| 145 | + // String check (empty string handling) |
| 146 | + if (typeof node === 'string') return allowEmptyString || node.length > 0; |
| 147 | + |
| 148 | + // Promise check |
| 149 | + if (node instanceof Promise) return allowPromise; |
| 150 | + |
| 151 | + // Function check (for components) |
| 152 | + if (typeof node === 'function') return allowFunction; |
| 153 | + |
| 154 | + // Array check (recursive validation) |
| 155 | + if (Array.isArray(node)) { |
| 156 | + return node.every(child => isValidNode(child, options)); |
| 157 | + } |
| 158 | + |
| 159 | + // DOM Node object check |
| 160 | + if (typeof node === 'object') { |
| 161 | + return ( |
| 162 | + node !== null && 'type' in node && |
| 163 | + node.type !== undefined && 'props' in node && |
| 164 | + node.props !== undefined |
| 165 | + ); |
| 166 | + } |
| 167 | + |
| 168 | + // All other cases |
| 169 | + return true; |
| 170 | +}; |
| 171 | + |
| 172 | +// Type guard version for specific node types |
| 173 | +export const isValidNodeOfType = <T extends DOMNode>( |
| 174 | + node: unknown, |
| 175 | + typeGuard: (n: DOMNode) => n is T, |
| 176 | + options?: Parameters<typeof isValidNode>[1] |
| 177 | +): node is T => { |
| 178 | + return isValidNode(node, options) && typeGuard(node as DOMNode); |
| 179 | +}; |
| 180 | + |
| 181 | +export const isComponentNode = (node: unknown): node is { type: FunctionComponent; props: any } => |
| 182 | + isValidNode(node) && typeof (node as any).type === 'function'; |
| 183 | + |
| 184 | +export const isTextNode = (node: unknown): node is string | number => |
| 185 | + typeof node === 'string' || typeof node === 'number'; |
| 186 | + |
116 | 187 |
|
117 | 188 | // Set of HTML void elements that don't need closing tags
|
118 | 189 | const voidElements = new Set([
|
|
0 commit comments