@domscribe/react 0.1.0

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.
Files changed (59) hide show
  1. package/adapter/react-adapter.d.ts +121 -0
  2. package/adapter/react-adapter.d.ts.map +1 -0
  3. package/adapter/react-adapter.js +486 -0
  4. package/adapter/types.d.ts +131 -0
  5. package/adapter/types.d.ts.map +1 -0
  6. package/adapter/types.js +17 -0
  7. package/auto-init.d.ts +2 -0
  8. package/auto-init.d.ts.map +1 -0
  9. package/auto-init.js +40 -0
  10. package/component/component-name-resolver.d.ts +118 -0
  11. package/component/component-name-resolver.d.ts.map +1 -0
  12. package/component/component-name-resolver.js +361 -0
  13. package/component/props-extractor.d.ts +51 -0
  14. package/component/props-extractor.d.ts.map +1 -0
  15. package/component/props-extractor.js +122 -0
  16. package/component/state-extractor.d.ts +60 -0
  17. package/component/state-extractor.d.ts.map +1 -0
  18. package/component/state-extractor.js +162 -0
  19. package/component/types.d.ts +256 -0
  20. package/component/types.d.ts.map +1 -0
  21. package/component/types.js +5 -0
  22. package/errors/index.d.ts +36 -0
  23. package/errors/index.d.ts.map +1 -0
  24. package/errors/index.js +75 -0
  25. package/fiber/fiber-walker.d.ts +58 -0
  26. package/fiber/fiber-walker.d.ts.map +1 -0
  27. package/fiber/fiber-walker.js +118 -0
  28. package/fiber/types.d.ts +162 -0
  29. package/fiber/types.d.ts.map +1 -0
  30. package/fiber/types.js +32 -0
  31. package/index.d.ts +14 -0
  32. package/index.d.ts.map +1 -0
  33. package/index.js +15 -0
  34. package/package.json +59 -0
  35. package/tsconfig.tsbuildinfo +1 -0
  36. package/utils/constants.d.ts +94 -0
  37. package/utils/constants.d.ts.map +1 -0
  38. package/utils/constants.js +123 -0
  39. package/utils/type-guards.d.ts +31 -0
  40. package/utils/type-guards.d.ts.map +1 -0
  41. package/utils/type-guards.js +89 -0
  42. package/utils/types.d.ts +6 -0
  43. package/utils/types.d.ts.map +1 -0
  44. package/utils/types.js +5 -0
  45. package/vite/index.d.ts +7 -0
  46. package/vite/index.d.ts.map +1 -0
  47. package/vite/index.js +5 -0
  48. package/vite/types.d.ts +56 -0
  49. package/vite/types.d.ts.map +1 -0
  50. package/vite/types.js +10 -0
  51. package/vite/vite-plugin.d.ts +29 -0
  52. package/vite/vite-plugin.d.ts.map +1 -0
  53. package/vite/vite-plugin.js +104 -0
  54. package/webpack/index.d.ts +7 -0
  55. package/webpack/index.d.ts.map +1 -0
  56. package/webpack/index.js +5 -0
  57. package/webpack/webpack-plugin.d.ts +50 -0
  58. package/webpack/webpack-plugin.d.ts.map +1 -0
  59. package/webpack/webpack-plugin.js +63 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * React adapter type definitions
3
+ * @module @domscribe/react/adapter/types
4
+ */
5
+ import type { FrameworkAdapter } from '@domscribe/runtime';
6
+ /**
7
+ * React DevTools global hook shape (window.__REACT_DEVTOOLS_GLOBAL_HOOK__)
8
+ */
9
+ export interface ReactDevToolsHook {
10
+ renderers: Map<number, ReactDevToolsRenderer>;
11
+ rendererInterfaces?: Map<number, ReactDevToolsRendererInterface>;
12
+ }
13
+ /**
14
+ * Renderer entry exposed by the DevTools hook
15
+ */
16
+ export interface ReactDevToolsRenderer {
17
+ findFiberByHostInstance?: (instance: Element) => unknown;
18
+ version?: string;
19
+ }
20
+ /**
21
+ * Renderer interface entry exposed by the DevTools hook
22
+ */
23
+ export interface ReactDevToolsRendererInterface {
24
+ version?: string;
25
+ }
26
+ /**
27
+ * Capture strategy for React component data
28
+ *
29
+ * - `devtools`: Use React DevTools hook (most reliable, requires DevTools)
30
+ * - `fiber`: Direct Fiber tree access (fast, but React internal API)
31
+ * - `best-effort`: Try multiple strategies in order of reliability
32
+ */
33
+ export declare enum CaptureStrategy {
34
+ DEVTOOLS = "devtools",
35
+ FIBER = "fiber",
36
+ BEST_EFFORT = "best-effort"
37
+ }
38
+ /**
39
+ * Configuration options for ReactAdapter
40
+ */
41
+ export interface ReactAdapterOptions {
42
+ /**
43
+ * Capture strategy to use
44
+ * @default CaptureStrategy.BEST_EFFORT
45
+ */
46
+ strategy?: CaptureStrategy;
47
+ /**
48
+ * Maximum depth to traverse the Fiber tree
49
+ * @default 50
50
+ */
51
+ maxTreeDepth?: number;
52
+ /**
53
+ * Enable debug logging
54
+ * @default false
55
+ */
56
+ debug?: boolean;
57
+ /**
58
+ * Include wrappers (HOC, memo, forwardRef) in component names
59
+ * @default true
60
+ */
61
+ includeWrappers?: boolean;
62
+ /**
63
+ * Custom hook name resolvers for better debugging
64
+ * Maps hook index to semantic name (e.g., { 0: 'count', 1: 'setCount' })
65
+ */
66
+ hookNameResolvers?: Map<string, Map<number, string>>;
67
+ }
68
+ /**
69
+ * Internal state for ReactAdapter
70
+ */
71
+ export interface ReactAdapterState {
72
+ /**
73
+ * Whether the adapter is initialized
74
+ */
75
+ initialized: boolean;
76
+ /**
77
+ * Detected React version
78
+ */
79
+ version?: string;
80
+ /**
81
+ * Whether React DevTools hook is available
82
+ */
83
+ hasDevTools: boolean;
84
+ /**
85
+ * Whether Fiber internals are accessible
86
+ */
87
+ hasFiberAccess: boolean;
88
+ /**
89
+ * Active capture strategy
90
+ */
91
+ activeStrategy: CaptureStrategy;
92
+ }
93
+ /**
94
+ * Result of a component resolution operation
95
+ */
96
+ export interface ComponentResolutionResult {
97
+ /**
98
+ * Whether the resolution was successful
99
+ */
100
+ success: boolean;
101
+ /**
102
+ * The component instance (Fiber node)
103
+ */
104
+ component?: unknown;
105
+ /**
106
+ * The capture strategy that succeeded
107
+ */
108
+ strategy?: CaptureStrategy;
109
+ /**
110
+ * Error if resolution failed
111
+ */
112
+ error?: Error;
113
+ }
114
+ /**
115
+ * Extended FrameworkAdapter with React-specific methods
116
+ */
117
+ export interface ReactFrameworkAdapter extends FrameworkAdapter {
118
+ /**
119
+ * Get the active capture strategy
120
+ */
121
+ getActiveStrategy(): CaptureStrategy;
122
+ /**
123
+ * Get the React version
124
+ */
125
+ getReactVersion(): string | null;
126
+ /**
127
+ * Check if React DevTools is available
128
+ */
129
+ hasDevToolsAccess(): boolean;
130
+ }
131
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../packages/domscribe-react/src/adapter/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,qBAAqB,CAAC,CAAC;IAC9C,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC;CAClE;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uBAAuB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,oBAAY,eAAe;IACzB,QAAQ,aAAa;IACrB,KAAK,UAAU;IACf,WAAW,gBAAgB;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,cAAc,EAAE,OAAO,CAAC;IAExB;;OAEG;IACH,cAAc,EAAE,eAAe,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D;;OAEG;IACH,iBAAiB,IAAI,eAAe,CAAC;IAErC;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IAEjC;;OAEG;IACH,iBAAiB,IAAI,OAAO,CAAC;CAC9B"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * React adapter type definitions
3
+ * @module @domscribe/react/adapter/types
4
+ */
5
+ /**
6
+ * Capture strategy for React component data
7
+ *
8
+ * - `devtools`: Use React DevTools hook (most reliable, requires DevTools)
9
+ * - `fiber`: Direct Fiber tree access (fast, but React internal API)
10
+ * - `best-effort`: Try multiple strategies in order of reliability
11
+ */
12
+ export var CaptureStrategy;
13
+ (function (CaptureStrategy) {
14
+ CaptureStrategy["DEVTOOLS"] = "devtools";
15
+ CaptureStrategy["FIBER"] = "fiber";
16
+ CaptureStrategy["BEST_EFFORT"] = "best-effort";
17
+ })(CaptureStrategy || (CaptureStrategy = {}));
package/auto-init.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auto-init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-init.d.ts","sourceRoot":"","sources":["../../../packages/domscribe-react/src/auto-init.ts"],"names":[],"mappings":""}
package/auto-init.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Auto-initializing entry point for React adapter.
3
+ *
4
+ * Importing this module automatically initializes RuntimeManager with ReactAdapter.
5
+ * Used by the webpack plugin which adds this as an entry point.
6
+ *
7
+ * When used with the webpack plugin, `__DOMSCRIBE_RUNTIME_OPTIONS__` and
8
+ * `__DOMSCRIBE_ADAPTER_OPTIONS__` are injected via DefinePlugin. Falls back
9
+ * to empty objects when not defined (e.g. direct import without the plugin).
10
+ *
11
+ * @module @domscribe/react/auto-init
12
+ */
13
+ import { RuntimeManager } from '@domscribe/runtime';
14
+ import { createReactAdapter } from './adapter/react-adapter.js';
15
+ try {
16
+ const runtimeOpts = typeof __DOMSCRIBE_RUNTIME_OPTIONS__ !== 'undefined'
17
+ ? __DOMSCRIBE_RUNTIME_OPTIONS__
18
+ : {};
19
+ const adapterOpts = typeof __DOMSCRIBE_ADAPTER_OPTIONS__ !== 'undefined'
20
+ ? __DOMSCRIBE_ADAPTER_OPTIONS__
21
+ : {};
22
+ // Reconstruct hookNameResolvers from plain object to Map<string, Map<number, string>>
23
+ const rawResolvers = adapterOpts.hookNameResolvers;
24
+ const hookNameResolvers = rawResolvers
25
+ ? new Map(Object.entries(rawResolvers).map(([k, v]) => [
26
+ k,
27
+ new Map(Object.entries(v).map(([i, n]) => [Number(i), n])),
28
+ ]))
29
+ : undefined;
30
+ RuntimeManager.getInstance().initialize({
31
+ ...runtimeOpts,
32
+ adapter: createReactAdapter({
33
+ ...adapterOpts,
34
+ hookNameResolvers,
35
+ }),
36
+ });
37
+ }
38
+ catch (e) {
39
+ console.warn('[domscribe] Failed to auto-init React runtime:', e instanceof Error ? e.message : String(e));
40
+ }
@@ -0,0 +1,118 @@
1
+ /**
2
+ * ComponentNameResolver - Resolve component names from React Fiber nodes
3
+ *
4
+ * Handles name resolution with wrapper detection (HOC, memo, forwardRef),
5
+ * fallback strategies, and display name formatting.
6
+ *
7
+ * @module @domscribe/react/component/component-name-resolver
8
+ */
9
+ import type { ExtendedReactFiber } from '../fiber/types.js';
10
+ import type { NameResolutionResult, NameResolutionOptions } from './types.js';
11
+ /**
12
+ * ComponentNameResolver class for resolving component names from Fiber nodes
13
+ */
14
+ export declare class ComponentNameResolver {
15
+ /**
16
+ * Resolve the name of a component from its Fiber node
17
+ *
18
+ * @param fiber - Fiber node to resolve name from
19
+ * @param options - Resolution options
20
+ * @returns Name resolution result
21
+ * @throws {NameResolutionError} If fiber is invalid
22
+ */
23
+ resolve(fiber: ExtendedReactFiber, options?: NameResolutionOptions): NameResolutionResult;
24
+ /**
25
+ * Resolve host component (DOM element) name
26
+ *
27
+ * @param fiber - Host component Fiber node
28
+ * @returns Name resolution result
29
+ */
30
+ private resolveHostComponent;
31
+ /**
32
+ * Resolve text node name
33
+ *
34
+ * @returns Name resolution result for text nodes
35
+ */
36
+ private resolveTextNode;
37
+ /**
38
+ * Resolve component name without wrapper analysis
39
+ *
40
+ * @param fiber - Fiber node
41
+ * @param options - Resolution options
42
+ * @returns Name resolution result
43
+ */
44
+ private resolveSimple;
45
+ /**
46
+ * Resolve component name with wrapper analysis
47
+ *
48
+ * @param fiber - Fiber node
49
+ * @param options - Resolution options
50
+ * @returns Name resolution result with wrapper chain
51
+ */
52
+ private resolveWithWrappers;
53
+ /**
54
+ * Extract displayName from Fiber type
55
+ *
56
+ * @param fiber - Fiber node
57
+ * @returns Display name or null
58
+ */
59
+ private extractDisplayName;
60
+ /**
61
+ * Extract name from Fiber type
62
+ *
63
+ * @param fiber - Fiber node
64
+ * @returns Type name or null
65
+ */
66
+ private extractTypeName;
67
+ /**
68
+ * Extract name from Fiber elementType
69
+ *
70
+ * @param fiber - Fiber node
71
+ * @returns Element type name or null
72
+ */
73
+ private extractElementTypeName;
74
+ /**
75
+ * Detect HOC pattern from component name
76
+ *
77
+ * @param fiber - Fiber node
78
+ * @returns HOC name or null
79
+ */
80
+ private detectHOCPattern;
81
+ /**
82
+ * Unwrap a memo component to get the inner component
83
+ *
84
+ * @param fiber - Memo Fiber node
85
+ * @returns Unwrapped Fiber
86
+ */
87
+ private unwrapMemo;
88
+ /**
89
+ * Unwrap a forwardRef component to get the inner component
90
+ *
91
+ * @param fiber - ForwardRef Fiber node
92
+ * @returns Unwrapped Fiber
93
+ */
94
+ private unwrapForwardRef;
95
+ /**
96
+ * Unwrap an HOC to get the inner component
97
+ *
98
+ * @param fiber - HOC Fiber node
99
+ * @returns Unwrapped Fiber (or original if can't unwrap)
100
+ */
101
+ private unwrapHOC;
102
+ /**
103
+ * Format a component name with its wrapper chain
104
+ *
105
+ * @param name - Component name
106
+ * @param wrappers - Wrapper names
107
+ * @returns Formatted name
108
+ */
109
+ private formatWrappedName;
110
+ /**
111
+ * Normalize resolution options with defaults
112
+ *
113
+ * @param options - User-provided options
114
+ * @returns Normalized options
115
+ */
116
+ private normalizeOptions;
117
+ }
118
+ //# sourceMappingURL=component-name-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component-name-resolver.d.ts","sourceRoot":"","sources":["../../../../packages/domscribe-react/src/component/component-name-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAQ9E;;GAEG;AACH,qBAAa,qBAAqB;IAChC;;;;;;;OAOG;IACH,OAAO,CACL,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,oBAAoB;IA0BvB;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAe5B;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAoDrB;;;;;;OAMG;IACH,OAAO,CAAC,mBAAmB;IA6D3B;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IA2B1B;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAoBvB;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAgC9B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAsBlB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAUjB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IASzB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;CAWzB"}
@@ -0,0 +1,361 @@
1
+ /**
2
+ * ComponentNameResolver - Resolve component names from React Fiber nodes
3
+ *
4
+ * Handles name resolution with wrapper detection (HOC, memo, forwardRef),
5
+ * fallback strategies, and display name formatting.
6
+ *
7
+ * @module @domscribe/react/component/component-name-resolver
8
+ */
9
+ import { REACT_FIBER_TAGS, COMMON_HOC_PATTERNS, DEFAULT_OPTIONS, } from '../utils/constants.js';
10
+ import { NameResolutionError } from '../errors/index.js';
11
+ /**
12
+ * ComponentNameResolver class for resolving component names from Fiber nodes
13
+ */
14
+ export class ComponentNameResolver {
15
+ /**
16
+ * Resolve the name of a component from its Fiber node
17
+ *
18
+ * @param fiber - Fiber node to resolve name from
19
+ * @param options - Resolution options
20
+ * @returns Name resolution result
21
+ * @throws {NameResolutionError} If fiber is invalid
22
+ */
23
+ resolve(fiber, options) {
24
+ if (!fiber) {
25
+ throw new NameResolutionError('Fiber node is required');
26
+ }
27
+ const opts = this.normalizeOptions(options);
28
+ // Handle host components (DOM elements)
29
+ if (fiber.tag === REACT_FIBER_TAGS.HostComponent) {
30
+ return this.resolveHostComponent(fiber);
31
+ }
32
+ // Handle text nodes
33
+ if (fiber.tag === REACT_FIBER_TAGS.HostText) {
34
+ return this.resolveTextNode();
35
+ }
36
+ // Resolve with wrapper analysis if enabled
37
+ if (opts.includeWrappers) {
38
+ return this.resolveWithWrappers(fiber, opts);
39
+ }
40
+ // Simple resolution
41
+ return this.resolveSimple(fiber, opts);
42
+ }
43
+ /**
44
+ * Resolve host component (DOM element) name
45
+ *
46
+ * @param fiber - Host component Fiber node
47
+ * @returns Name resolution result
48
+ */
49
+ resolveHostComponent(fiber) {
50
+ const tagName = typeof fiber.type === 'string' ? fiber.type : 'UnknownElement';
51
+ return {
52
+ name: tagName,
53
+ displayName: tagName,
54
+ method: 'type-name',
55
+ confidence: 1.0,
56
+ wrappers: [],
57
+ };
58
+ }
59
+ /**
60
+ * Resolve text node name
61
+ *
62
+ * @returns Name resolution result for text nodes
63
+ */
64
+ resolveTextNode() {
65
+ return {
66
+ name: 'Text',
67
+ displayName: 'Text',
68
+ method: 'type-name',
69
+ confidence: 1.0,
70
+ wrappers: [],
71
+ };
72
+ }
73
+ /**
74
+ * Resolve component name without wrapper analysis
75
+ *
76
+ * @param fiber - Fiber node
77
+ * @param options - Resolution options
78
+ * @returns Name resolution result
79
+ */
80
+ resolveSimple(fiber, options) {
81
+ // Try type.displayName
82
+ const displayName = this.extractDisplayName(fiber);
83
+ if (displayName) {
84
+ return {
85
+ name: displayName,
86
+ displayName,
87
+ method: 'displayName',
88
+ confidence: 1.0,
89
+ wrappers: [],
90
+ };
91
+ }
92
+ // Try type.name
93
+ const typeName = this.extractTypeName(fiber);
94
+ if (typeName) {
95
+ // Determine if function or class
96
+ const isFunction = typeof fiber.type === 'function';
97
+ return {
98
+ name: typeName,
99
+ displayName: typeName,
100
+ method: isFunction ? 'function-name' : 'type-name',
101
+ confidence: 0.8,
102
+ wrappers: [],
103
+ };
104
+ }
105
+ // Try elementType
106
+ const elementTypeName = this.extractElementTypeName(fiber);
107
+ if (elementTypeName) {
108
+ return {
109
+ name: elementTypeName,
110
+ displayName: elementTypeName,
111
+ method: 'type-name',
112
+ confidence: 0.5,
113
+ wrappers: [],
114
+ };
115
+ }
116
+ // Fallback
117
+ return {
118
+ name: options.fallbackName,
119
+ displayName: options.fallbackName,
120
+ method: 'fallback',
121
+ confidence: 0.0,
122
+ wrappers: [],
123
+ };
124
+ }
125
+ /**
126
+ * Resolve component name with wrapper analysis
127
+ *
128
+ * @param fiber - Fiber node
129
+ * @param options - Resolution options
130
+ * @returns Name resolution result with wrapper chain
131
+ */
132
+ resolveWithWrappers(fiber, options) {
133
+ const wrappers = [];
134
+ let currentFiber = fiber;
135
+ let depth = 0;
136
+ // Unwrap layers
137
+ while (depth < options.maxWrapperDepth) {
138
+ // Check for memo
139
+ if (currentFiber.tag === REACT_FIBER_TAGS.MemoComponent ||
140
+ currentFiber.tag === REACT_FIBER_TAGS.SimpleMemoComponent) {
141
+ wrappers.push('memo');
142
+ const unwrapped = this.unwrapMemo(currentFiber);
143
+ if (unwrapped === currentFiber)
144
+ break;
145
+ currentFiber = unwrapped;
146
+ }
147
+ // Check for forwardRef
148
+ else if (currentFiber.tag === REACT_FIBER_TAGS.ForwardRef) {
149
+ wrappers.push('forwardRef');
150
+ const unwrapped = this.unwrapForwardRef(currentFiber);
151
+ if (unwrapped === currentFiber)
152
+ break;
153
+ currentFiber = unwrapped;
154
+ }
155
+ // Check for HOC patterns
156
+ else {
157
+ const hocName = this.detectHOCPattern(currentFiber);
158
+ if (hocName) {
159
+ wrappers.push(hocName);
160
+ const unwrapped = this.unwrapHOC(currentFiber);
161
+ if (unwrapped === currentFiber)
162
+ break;
163
+ currentFiber = unwrapped;
164
+ }
165
+ else {
166
+ break; // No more wrappers
167
+ }
168
+ }
169
+ depth++;
170
+ }
171
+ // Get the innermost component name
172
+ const innerResult = this.resolveSimple(currentFiber, options);
173
+ // Build display name with wrappers
174
+ const displayName = wrappers.length > 0
175
+ ? this.formatWrappedName(innerResult.name, wrappers)
176
+ : innerResult.name;
177
+ return {
178
+ name: innerResult.name,
179
+ displayName,
180
+ method: innerResult.method,
181
+ confidence: innerResult.confidence,
182
+ wrappers,
183
+ };
184
+ }
185
+ /**
186
+ * Extract displayName from Fiber type
187
+ *
188
+ * @param fiber - Fiber node
189
+ * @returns Display name or null
190
+ */
191
+ extractDisplayName(fiber) {
192
+ const type = fiber.type;
193
+ // Check function components (e.g., MyComponent.displayName = 'Pretty')
194
+ if (typeof type === 'function' &&
195
+ 'displayName' in type &&
196
+ typeof type.displayName === 'string' &&
197
+ type.displayName.length > 0) {
198
+ return type.displayName;
199
+ }
200
+ // Check object types (e.g., memo/forwardRef wrappers with displayName)
201
+ if (type &&
202
+ typeof type === 'object' &&
203
+ 'displayName' in type &&
204
+ typeof type.displayName === 'string' &&
205
+ type.displayName.length > 0) {
206
+ return type.displayName;
207
+ }
208
+ return null;
209
+ }
210
+ /**
211
+ * Extract name from Fiber type
212
+ *
213
+ * @param fiber - Fiber node
214
+ * @returns Type name or null
215
+ */
216
+ extractTypeName(fiber) {
217
+ const type = fiber.type;
218
+ if (typeof type === 'function' && type.name) {
219
+ return type.name;
220
+ }
221
+ if (type &&
222
+ typeof type === 'object' &&
223
+ 'name' in type &&
224
+ typeof type.name === 'string' &&
225
+ type.name.length > 0) {
226
+ return type.name;
227
+ }
228
+ return null;
229
+ }
230
+ /**
231
+ * Extract name from Fiber elementType
232
+ *
233
+ * @param fiber - Fiber node
234
+ * @returns Element type name or null
235
+ */
236
+ extractElementTypeName(fiber) {
237
+ const elementType = fiber.elementType;
238
+ if (typeof elementType === 'string') {
239
+ return elementType;
240
+ }
241
+ if (typeof elementType === 'function' && elementType.name) {
242
+ return elementType.name;
243
+ }
244
+ if (elementType &&
245
+ typeof elementType === 'object' &&
246
+ 'displayName' in elementType &&
247
+ typeof elementType.displayName === 'string') {
248
+ return elementType.displayName;
249
+ }
250
+ if (elementType &&
251
+ typeof elementType === 'object' &&
252
+ 'name' in elementType &&
253
+ typeof elementType.name === 'string') {
254
+ return elementType.name;
255
+ }
256
+ return null;
257
+ }
258
+ /**
259
+ * Detect HOC pattern from component name
260
+ *
261
+ * @param fiber - Fiber node
262
+ * @returns HOC name or null
263
+ */
264
+ detectHOCPattern(fiber) {
265
+ const name = this.extractTypeName(fiber) || this.extractDisplayName(fiber);
266
+ if (!name) {
267
+ return null;
268
+ }
269
+ const matchedPattern = COMMON_HOC_PATTERNS.find((pattern) => name.startsWith(pattern));
270
+ return matchedPattern || null;
271
+ }
272
+ /**
273
+ * Unwrap a memo component to get the inner component
274
+ *
275
+ * @param fiber - Memo Fiber node
276
+ * @returns Unwrapped Fiber
277
+ */
278
+ unwrapMemo(fiber) {
279
+ // Try to get the inner type from elementType
280
+ if (fiber.elementType &&
281
+ typeof fiber.elementType === 'object' &&
282
+ fiber.elementType !== null &&
283
+ 'type' in fiber.elementType) {
284
+ const innerType = fiber.elementType.type;
285
+ // Create a synthetic fiber with the inner type and reset tag
286
+ // so the unwrap loop doesn't re-match this as a memo wrapper
287
+ return {
288
+ ...fiber,
289
+ tag: REACT_FIBER_TAGS.FunctionComponent,
290
+ type: innerType,
291
+ elementType: innerType,
292
+ };
293
+ }
294
+ return fiber;
295
+ }
296
+ /**
297
+ * Unwrap a forwardRef component to get the inner component
298
+ *
299
+ * @param fiber - ForwardRef Fiber node
300
+ * @returns Unwrapped Fiber
301
+ */
302
+ unwrapForwardRef(fiber) {
303
+ // Try to get the render function from elementType
304
+ if (fiber.elementType &&
305
+ typeof fiber.elementType === 'object' &&
306
+ fiber.elementType !== null &&
307
+ 'render' in fiber.elementType) {
308
+ const renderFn = fiber.elementType.render;
309
+ // Create a synthetic fiber with the render function as type and reset tag
310
+ // so the unwrap loop doesn't re-match this as a forwardRef wrapper
311
+ return {
312
+ ...fiber,
313
+ tag: REACT_FIBER_TAGS.FunctionComponent,
314
+ type: renderFn,
315
+ elementType: renderFn,
316
+ };
317
+ }
318
+ return fiber;
319
+ }
320
+ /**
321
+ * Unwrap an HOC to get the inner component
322
+ *
323
+ * @param fiber - HOC Fiber node
324
+ * @returns Unwrapped Fiber (or original if can't unwrap)
325
+ */
326
+ unwrapHOC(fiber) {
327
+ // For HOCs, we can't reliably unwrap without runtime inspection
328
+ // This is a best-effort attempt using child
329
+ if (fiber.child) {
330
+ return fiber.child;
331
+ }
332
+ return fiber;
333
+ }
334
+ /**
335
+ * Format a component name with its wrapper chain
336
+ *
337
+ * @param name - Component name
338
+ * @param wrappers - Wrapper names
339
+ * @returns Formatted name
340
+ */
341
+ formatWrappedName(name, wrappers) {
342
+ if (wrappers.length === 0) {
343
+ return name;
344
+ }
345
+ // Format: wrapper1(wrapper2(Component))
346
+ return `${wrappers.join('(')}(${name}${')'.repeat(wrappers.length)}`;
347
+ }
348
+ /**
349
+ * Normalize resolution options with defaults
350
+ *
351
+ * @param options - User-provided options
352
+ * @returns Normalized options
353
+ */
354
+ normalizeOptions(options) {
355
+ return {
356
+ includeWrappers: options?.includeWrappers ?? false,
357
+ maxWrapperDepth: options?.maxWrapperDepth ?? DEFAULT_OPTIONS.MAX_WRAPPER_DEPTH,
358
+ fallbackName: options?.fallbackName ?? DEFAULT_OPTIONS.FALLBACK_COMPONENT_NAME,
359
+ };
360
+ }
361
+ }