@useviber/plugin
Version:
Universal Viber Plugin - Works with any framework (React, Vue, Angular, Svelte, Vanilla)
970 lines (969 loc) • 319 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ViberPlugin = {}));
})(this, (function (exports) { 'use strict';
// Remove DeepFrameworkInfo types - use shared types instead
class FrameworkDetector {
static detectFramework() {
if (this.isReact())
return 'react';
if (this.isVue())
return 'vue';
if (this.isAngular())
return 'angular';
if (this.isSvelte())
return 'svelte';
return 'vanilla';
}
static isReact() {
// Check for React global objects
const hasReactGlobal = !!(window.React ||
window.ReactDOM ||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__);
// Check for React elements in DOM
const hasReactElements = !!(document.querySelector('[data-reactroot]') ||
document.querySelector('[data-reactid]') ||
document.querySelector('[data-react-checksum]'));
// Check for React-specific classes
const hasReactClasses = !!(document.querySelector('.react-') ||
document.querySelector('[class*="react-"]') ||
document.querySelector('[class*="React"]'));
// Check for React scripts
const hasReactScripts = !!(document.querySelector('script[src*="react"]') ||
document.querySelector('script[src*="React"]'));
return hasReactGlobal || hasReactElements || hasReactClasses || hasReactScripts;
}
static isVue() {
return !!(window.Vue ||
window.__VUE__ ||
document.querySelector('[data-v-]') ||
document.querySelector('.v-application'));
}
static isAngular() {
return !!(window.angular ||
window.ng ||
document.querySelector('[ng-version]') ||
document.querySelector('[ng-controller]'));
}
static isSvelte() {
return !!(window.svelte ||
document.querySelector('[class*="svelte-"]') ||
document.querySelector('[data-svelte]'));
}
static isVanilla() {
return !this.isReact() && !this.isVue() && !this.isAngular() && !this.isSvelte();
}
// Main method to create unified component data
static createUnifiedComponentData(element, currentPath) {
var _a;
console.log('🔍 [FrameworkDetector] Creating UnifiedComponentData for element:', element.tagName);
console.log('🔍 [FrameworkDetector] Current path:', currentPath);
const frameworkType = this.detectElementFramework(element);
console.log('🔍 [FrameworkDetector] Detected framework type:', frameworkType);
const baseInfo = {
element: {
tagName: element.tagName.toLowerCase(),
className: element.className,
id: element.id,
innerText: ((_a = element.innerText) === null || _a === void 0 ? void 0 : _a.trim()) || '',
attributes: this.getElementAttributes(element),
boundingBox: element.getBoundingClientRect(),
computedStyles: this.getComputedStyles(element),
},
context: {
currentPath,
timestamp: new Date().toISOString(),
url: window.location.href,
},
};
console.log('🔍 [FrameworkDetector] Base info created, switching to framework-specific creation...');
let result;
switch (frameworkType) {
case 'react':
console.log('🔍 [FrameworkDetector] Creating React unified data...');
result = this.createReactUnifiedData(element, baseInfo);
break;
case 'vue':
console.log('🔍 [FrameworkDetector] Creating Vue unified data...');
result = this.createVueUnifiedData(element, baseInfo);
break;
case 'angular':
console.log('🔍 [FrameworkDetector] Creating Angular unified data...');
result = this.createAngularUnifiedData(element, baseInfo);
break;
case 'svelte':
console.log('🔍 [FrameworkDetector] Creating Svelte unified data...');
result = this.createSvelteUnifiedData(element, baseInfo);
break;
default:
console.log('🔍 [FrameworkDetector] Creating Vanilla unified data...');
result = this.createVanillaUnifiedData(element, baseInfo);
}
console.log('🔍 [FrameworkDetector] Final UnifiedComponentData:', {
frameworkType: result.framework.type,
componentName: result.component.name,
actualPath: result.file.actualPath,
componentPath: result.file.componentPath,
currentPath: baseInfo.context.currentPath
});
return result;
}
// Framework-specific data creation methods
static createReactUnifiedData(element, baseInfo) {
const reactInfo = this.extractReactInfo(element);
return {
...baseInfo,
component: {
name: reactInfo.componentName || element.tagName.toLowerCase(),
displayName: reactInfo.componentName || element.tagName.toLowerCase(),
type: 'react',
},
framework: {
type: 'react',
reactInfo,
},
file: {
actualPath: reactInfo.componentFile || baseInfo.context.currentPath,
componentPath: baseInfo.context.currentPath,
extension: this.getFileExtension(reactInfo.componentFile),
hasFileContent: !!reactInfo.componentFile,
},
};
}
static createVueUnifiedData(element, baseInfo) {
const vueInfo = this.extractVueInfo(element);
return {
...baseInfo,
component: {
name: vueInfo.componentName || element.tagName.toLowerCase(),
displayName: vueInfo.componentName || element.tagName.toLowerCase(),
type: 'vue',
},
framework: {
type: 'vue',
vueInfo,
},
file: {
actualPath: vueInfo.componentFile || baseInfo.context.currentPath,
componentPath: baseInfo.context.currentPath,
extension: this.getFileExtension(vueInfo.componentFile),
hasFileContent: !!vueInfo.componentFile,
},
};
}
static createAngularUnifiedData(element, baseInfo) {
const angularInfo = this.extractAngularInfo(element);
return {
...baseInfo,
component: {
name: angularInfo.componentName || element.tagName.toLowerCase(),
displayName: angularInfo.componentName || element.tagName.toLowerCase(),
type: 'angular',
},
framework: {
type: 'angular',
angularInfo,
},
file: {
actualPath: angularInfo.componentFile || baseInfo.context.currentPath,
componentPath: baseInfo.context.currentPath,
extension: this.getFileExtension(angularInfo.componentFile),
hasFileContent: !!angularInfo.componentFile,
},
};
}
static createSvelteUnifiedData(element, baseInfo) {
const svelteInfo = this.extractSvelteInfo(element);
return {
...baseInfo,
component: {
name: svelteInfo.componentName || element.tagName.toLowerCase(),
displayName: svelteInfo.componentName || element.tagName.toLowerCase(),
type: 'svelte',
},
framework: {
type: 'svelte',
svelteInfo,
},
file: {
actualPath: svelteInfo.componentFile || baseInfo.context.currentPath,
componentPath: baseInfo.context.currentPath,
extension: this.getFileExtension(svelteInfo.componentFile),
hasFileContent: !!svelteInfo.componentFile,
},
};
}
static createVanillaUnifiedData(element, baseInfo) {
const vanillaInfo = this.extractVanillaInfo(element);
return {
...baseInfo,
component: {
name: vanillaInfo.componentName || element.tagName.toLowerCase(),
displayName: vanillaInfo.componentName || element.tagName.toLowerCase(),
type: 'vanilla',
},
framework: {
type: 'vanilla',
vanillaInfo,
},
file: {
actualPath: vanillaInfo.componentFile || baseInfo.context.currentPath,
componentPath: baseInfo.context.currentPath,
extension: this.getFileExtension(vanillaInfo.componentFile),
hasFileContent: !!vanillaInfo.componentFile,
},
};
}
// Legacy method for backward compatibility - now returns UnifiedComponentData
static analyzeElement(element) {
const unifiedData = this.createUnifiedComponentData(element, window.location.pathname);
const frameworkType = unifiedData.framework.type;
return {
type: frameworkType,
element,
componentName: unifiedData.component.displayName,
componentPath: unifiedData.file.actualPath,
hasState: this.hasState(element, frameworkType),
stateKeys: this.getStateKeys(element, frameworkType),
eventHandlers: this.getEventHandlers(element),
...this.getFrameworkSpecificProps(element, frameworkType),
};
}
// Remove analyzeElementDeep method - use createUnifiedComponentData instead
// Helper methods
static detectElementFramework(element) {
console.log('🔍 [FrameworkDetector] Starting framework detection for element:', element.tagName);
// Check React first
const isReact = this.isReactElement(element);
console.log('🔍 [FrameworkDetector] React detection result:', isReact);
if (isReact) {
console.log('✅ [FrameworkDetector] Detected as REACT');
return 'react';
}
// Check Vue
const isVue = this.isVueElement(element);
console.log('🔍 [FrameworkDetector] Vue detection result:', isVue);
if (isVue) {
console.log('✅ [FrameworkDetector] Detected as VUE');
return 'vue';
}
// Check Angular
const isAngular = this.isAngularElement(element);
console.log('🔍 [FrameworkDetector] Angular detection result:', isAngular);
if (isAngular) {
console.log('✅ [FrameworkDetector] Detected as ANGULAR');
return 'angular';
}
// Check Svelte
const isSvelte = this.isSvelteElement(element);
console.log('🔍 [FrameworkDetector] Svelte detection result:', isSvelte);
if (isSvelte) {
console.log('✅ [FrameworkDetector] Detected as SVELTE');
return 'svelte';
}
console.log('⚠️ [FrameworkDetector] No framework detected, defaulting to VANILLA');
return 'vanilla';
}
static isReactElement(element) {
console.log('🔍 [FrameworkDetector] Checking if element is React...');
const reactElement = element;
// React 16-17 detection (same as React web client)
console.log('🔍 [FrameworkDetector] Checking React 16-17 properties...');
console.log(' - _reactInternalFiber:', !!reactElement._reactInternalFiber);
console.log(' - _reactEventPriority:', reactElement._reactEventPriority !== undefined);
console.log(' - _reactLanes:', reactElement._reactLanes !== undefined);
console.log(' - _reactProps:', !!reactElement._reactProps);
console.log(' - _reactInternalInstance:', !!reactElement._reactInternalInstance);
if (reactElement._reactInternalFiber ||
reactElement._reactEventPriority !== undefined ||
reactElement._reactLanes !== undefined ||
reactElement._reactProps ||
reactElement._reactInternalInstance ||
this.hasReactFiberKey(element)) {
console.log('✅ [FrameworkDetector] React 16-17 properties found!');
return true;
}
// React 18+ detection - check for new internal properties (same as React web client)
console.log('🔍 [FrameworkDetector] Checking React 18+ properties...');
const ownProps = Object.getOwnPropertyNames(element);
console.log(' - Element own properties:', ownProps);
const hasReact18Props = ownProps.some(key => key.startsWith('__reactProps$') ||
key.startsWith('__reactEvents$') ||
key.startsWith('__reactFiber$') ||
key.includes('react'));
console.log(' - Has React 18+ properties:', hasReact18Props);
if (hasReact18Props) {
console.log('✅ [FrameworkDetector] React 18+ properties found!');
return true;
}
// Check for React attributes
console.log('🔍 [FrameworkDetector] Checking React attributes...');
const hasReactAttributes = !!(element.hasAttribute('data-reactroot') ||
element.hasAttribute('data-reactid') ||
element.hasAttribute('data-react-checksum'));
console.log(' - Has React attributes:', hasReactAttributes);
// Check for React classes (common in development)
console.log('🔍 [FrameworkDetector] Checking React classes...');
const hasReactClasses = !!(element.className.includes('react-') ||
element.className.includes('React') ||
element.classList.contains('react-component'));
console.log(' - Has React classes:', hasReactClasses);
console.log(' - Element className:', element.className);
// Check if any parent has React properties
console.log('🔍 [FrameworkDetector] Checking React parent...');
const hasReactParent = this.hasReactParent(element);
console.log(' - Has React parent:', hasReactParent);
// Check global React context
console.log('🔍 [FrameworkDetector] Checking global React context...');
const hasGlobalReact = !!(window.React ||
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ ||
window.ReactDOM);
console.log(' - Has global React:', hasGlobalReact);
console.log(' - window.React:', !!window.React);
console.log(' - window.__REACT_DEVTOOLS_GLOBAL_HOOK__:', !!window.__REACT_DEVTOOLS_GLOBAL_HOOK__);
console.log(' - window.ReactDOM:', !!window.ReactDOM);
const finalResult = hasReactAttributes || hasReactClasses || hasReactParent || hasGlobalReact;
console.log('🔍 [FrameworkDetector] Final React detection result:', finalResult);
return finalResult;
}
/**
* Check if element has React Fiber keys (same as React web client)
*/
static hasReactFiberKey(element) {
const propertyNames = Object.getOwnPropertyNames(element);
return propertyNames.some((key) => key.startsWith('__reactFiber$') ||
key.startsWith('__reactInternalInstance$') ||
key.startsWith('__reactProps$') ||
key.startsWith('__reactEvents$'));
}
static hasReactParent(element) {
let parent = element.parentElement;
let depth = 0;
const maxDepth = 5; // Check up to 5 levels up
while (parent && depth < maxDepth) {
if (parent._reactInternalFiber ||
parent.__reactProps$ ||
parent.hasAttribute('data-reactroot')) {
return true;
}
parent = parent.parentElement;
depth++;
}
return false;
}
static isVueElement(element) {
console.log('🔍 [FrameworkDetector] Checking if element is Vue...');
// Check for Vue 2 internal property
const hasVue2 = !!element.__vue__;
console.log(' - Has Vue 2 __vue__:', hasVue2);
// Check for Vue 3 scoped CSS attributes (data-v-*)
const hasVue3Scoped = element.hasAttribute('data-v-');
console.log(' - Has Vue 3 scoped CSS (data-v-):', hasVue3Scoped);
// Check all data-v-* attributes
const dataVAttributes = Array.from(element.attributes)
.filter(attr => attr.name.startsWith('data-v-'))
.map(attr => attr.name);
console.log(' - All data-v-* attributes:', dataVAttributes);
// Check for Vuetify classes
const hasVuetify = element.classList.contains('v-application');
console.log(' - Has Vuetify class:', hasVuetify);
// Check for Vue 3 internal properties
const hasVue3Internal = !!element.__vnode || !!element.__vueParentComponent;
console.log(' - Has Vue 3 internal properties:', hasVue3Internal);
const isVue = hasVue2 || hasVue3Scoped || hasVuetify || hasVue3Internal;
console.log(' - Final Vue detection result:', isVue);
return isVue;
}
static isAngularElement(element) {
return (!!element.__ngContext__ ||
element.hasAttribute('ng-version') ||
element.hasAttribute('ng-controller'));
}
static isSvelteElement(element) {
return (!!element._svelte ||
element.classList.contains('svelte-') ||
element.hasAttribute('data-svelte'));
}
static getElementAttributes(element) {
const attributes = {};
Array.from(element.attributes).forEach(attr => {
attributes[attr.name] = attr.value;
});
return attributes;
}
static getComputedStyles(element) {
const styles = window.getComputedStyle(element);
const computedStyles = {};
// Get common CSS properties
const cssProperties = [
'color',
'background-color',
'font-size',
'font-weight',
'padding',
'margin',
'border',
'border-radius',
'display',
'position',
'width',
'height',
];
cssProperties.forEach(prop => {
computedStyles[prop] = styles.getPropertyValue(prop);
});
return computedStyles;
}
static getFileExtension(filePath) {
if (!filePath)
return '';
const lastDotIndex = filePath.lastIndexOf('.');
return lastDotIndex > -1 ? filePath.substring(lastDotIndex + 1) : '';
}
static hasState(element, frameworkType) {
var _a, _b, _c, _d, _e;
switch (frameworkType) {
case 'react':
return !!((_a = element._reactInternalFiber) === null || _a === void 0 ? void 0 : _a.memoizedState);
case 'vue':
return !!((_b = element.__vue__) === null || _b === void 0 ? void 0 : _b.$data);
case 'angular':
return !!((_c = element.__ngContext__) === null || _c === void 0 ? void 0 : _c.componentInstance);
case 'svelte':
return !!((_e = (_d = element._svelte) === null || _d === void 0 ? void 0 : _d.$$) === null || _e === void 0 ? void 0 : _e.dirty);
default:
return false;
}
}
static getStateKeys(element, frameworkType) {
var _a, _b, _c, _d, _e;
switch (frameworkType) {
case 'react':
const reactState = (_a = element._reactInternalFiber) === null || _a === void 0 ? void 0 : _a.memoizedState;
return reactState ? Object.keys(reactState) : [];
case 'vue':
const vueData = (_b = element.__vue__) === null || _b === void 0 ? void 0 : _b.$data;
return vueData ? Object.keys(vueData) : [];
case 'angular':
const angularInstance = (_c = element.__ngContext__) === null || _c === void 0 ? void 0 : _c.componentInstance;
return angularInstance ? Object.keys(angularInstance) : [];
case 'svelte':
const svelteStores = (_e = (_d = element._svelte) === null || _d === void 0 ? void 0 : _d.$$) === null || _e === void 0 ? void 0 : _e.stores;
return svelteStores ? Object.keys(svelteStores) : [];
default:
return [];
}
}
static getEventHandlers(element) {
const eventHandlers = [];
const eventTypes = [
'click',
'change',
'input',
'submit',
'keydown',
'keyup',
'mouseover',
'mouseout',
];
eventTypes.forEach(eventType => {
if (element[`on${eventType}`]) {
eventHandlers.push(eventType);
}
});
return eventHandlers;
}
static getFrameworkSpecificProps(element, frameworkType) {
switch (frameworkType) {
case 'react':
return { reactProps: this.extractReactProps(element) };
case 'vue':
return { vueProps: this.extractVueProps(element) };
case 'angular':
return { angularProps: this.extractAngularProps(element) };
case 'svelte':
return { svelteProps: this.extractSvelteProps(element) };
default:
return {};
}
}
// Framework-specific extraction methods
static extractReactInfo(element) {
console.log('🔍 [FrameworkDetector] Extracting React info from element:', element.tagName);
const reactElement = element;
const fiber = reactElement._reactInternalFiber;
console.log('🔍 [FrameworkDetector] React fiber found:', !!fiber);
const componentName = this.extractComponentNameFromElement(element) || element.tagName.toLowerCase();
console.log('🔍 [FrameworkDetector] Component name extracted:', componentName);
const componentFile = this.getReactComponentFile(element);
console.log('🔍 [FrameworkDetector] Component file extracted:', componentFile);
const result = {
componentName: componentName,
componentFile: componentFile,
props: this.extractReactProps(element),
state: this.extractReactState(element),
hierarchy: this.extractReactHierarchy(element),
fiber: fiber
? {
tag: fiber.tag,
key: fiber.key,
elementType: fiber.elementType,
type: fiber.type,
stateNode: fiber.stateNode,
return: fiber.return,
child: fiber.child,
sibling: fiber.sibling,
index: fiber.index,
ref: fiber.ref,
pendingProps: fiber.pendingProps,
memoizedProps: fiber.memoizedProps,
memoizedState: fiber.memoizedState,
updateQueue: fiber.updateQueue,
contextDependencies: fiber.contextDependencies,
mode: fiber.mode,
flags: fiber.flags,
subtreeFlags: fiber.subtreeFlags,
deletions: fiber.deletions,
lanes: fiber.lanes,
childLanes: fiber.childLanes,
alternate: fiber.alternate,
}
: undefined,
};
console.log('🔍 [FrameworkDetector] React info extraction result:', {
componentName: result.componentName,
componentFile: result.componentFile,
hasFiber: !!result.fiber
});
return result;
}
// Vue lifecycle extraction
static extractVueInfo(element) {
const vueElement = element;
const instance = vueElement.__vue__;
return {
componentName: this.extractComponentNameFromElement(element) || element.tagName.toLowerCase(),
componentFile: this.getVueComponentFile(element),
props: this.extractVueProps(element),
data: this.extractVueState(element),
instance: instance
? {
$el: instance.$el,
$options: instance.$options,
$props: instance.$props,
$data: instance.$data,
$refs: instance.$refs,
$slots: instance.$slots,
$scopedSlots: instance.$scopedSlots,
$children: instance.$children,
}
: undefined,
lifecycle: instance
? {
mounted: instance.$mounted,
destroyed: instance.$destroyed,
}
: undefined,
};
}
// Angular metadata extraction
static extractAngularInfo(element) {
const angularElement = element;
const ngContext = angularElement.__ngContext__;
return {
componentName: this.extractComponentNameFromElement(element) || element.tagName.toLowerCase(),
componentFile: this.getAngularComponentFile(element),
context: ngContext
? {
injector: ngContext.injector,
changeDetectorRef: ngContext.changeDetectorRef,
elementRef: ngContext.elementRef,
viewContainerRef: ngContext.viewContainerRef,
templateRef: ngContext.templateRef,
renderer2: ngContext.renderer2,
sanitizer: ngContext.sanitizer,
constructor: ngContext.constructor,
}
: undefined,
metadata: ngContext
? {
selector: ngContext.selector,
templateUrl: ngContext.templateUrl,
template: ngContext.template,
styleUrls: ngContext.styleUrls,
styles: ngContext.styles,
encapsulation: ngContext.encapsulation,
changeDetection: ngContext.changeDetection,
}
: undefined,
};
}
static extractSvelteInfo(element) {
var _a, _b;
const svelteElement = element;
const instance = svelteElement._svelte;
return {
componentName: this.extractComponentNameFromElement(element) || element.tagName.toLowerCase(),
componentFile: this.getSvelteComponentFile(element),
props: this.extractSvelteProps(element),
state: this.extractSvelteState(element),
instance: instance
? {
$$: instance.$$,
}
: undefined,
stores: ((_a = instance === null || instance === void 0 ? void 0 : instance.$$) === null || _a === void 0 ? void 0 : _a.stores)
? Object.keys(instance.$$.stores).map(key => {
var _a;
return ({
name: key,
value: instance.$$.stores[key],
subscribe: (_a = instance.$$.stores[key]) === null || _a === void 0 ? void 0 : _a.subscribe,
});
})
: [],
actions: ((_b = instance === null || instance === void 0 ? void 0 : instance.$$) === null || _b === void 0 ? void 0 : _b.actions) || [],
};
}
static extractVanillaInfo(element) {
return {
componentName: this.extractComponentNameFromElement(element) || element.tagName.toLowerCase(),
componentFile: this.getVanillaComponentFile(element),
state: this.extractVanillaState(element),
customElement: element.tagName.includes('-')
? {
tagName: element.tagName,
observedAttributes: element.constructor.observedAttributes || [],
lifecycleCallbacks: {
connectedCallback: !!element.connectedCallback,
disconnectedCallback: !!element.disconnectedCallback,
adoptedCallback: !!element.adoptedCallback,
attributeChangedCallback: !!element.attributeChangedCallback,
},
}
: undefined,
dataAttributes: this.extractDataAttributes(element),
customProperties: this.extractCustomProperties(element),
eventHandlers: this.extractEventHandlers(element),
};
}
// Component file extraction methods
static getReactComponentFile(element) {
var _a, _b, _c, _d, _e;
console.log('🔍 [FrameworkDetector] Getting React component file for element:', element.tagName);
// Method 1: Try to get from the element itself (React 16-17)
const reactElement = element;
let fiber = reactElement._reactInternalFiber;
console.log('🔍 [FrameworkDetector] Direct React fiber exists:', !!fiber);
// Method 2: If no direct fiber, try to find it in parent React components
if (!fiber) {
console.log('🔍 [FrameworkDetector] No direct fiber, searching parent components...');
fiber = this.findReactFiberInParents(element);
console.log('🔍 [FrameworkDetector] Found fiber in parents:', !!fiber);
}
// Method 3: Try to get from React DevTools source info (development only)
if ((_b = (_a = fiber === null || fiber === void 0 ? void 0 : fiber.type) === null || _a === void 0 ? void 0 : _a.__source) === null || _b === void 0 ? void 0 : _b.fileName) {
console.log('✅ [FrameworkDetector] Found fileName from fiber.type.__source:', fiber.type.__source.fileName);
return fiber.type.__source.fileName;
}
console.log('🔍 [FrameworkDetector] No fileName from fiber.type.__source');
// Method 4: Try to get from elementType (alternative path)
if ((_d = (_c = fiber === null || fiber === void 0 ? void 0 : fiber.elementType) === null || _c === void 0 ? void 0 : _c.__source) === null || _d === void 0 ? void 0 : _d.fileName) {
console.log('✅ [FrameworkDetector] Found fileName from fiber.elementType.__source:', fiber.elementType.__source.fileName);
return fiber.elementType.__source.fileName;
}
console.log('🔍 [FrameworkDetector] No fileName from fiber.elementType.__source');
// Method 5: Try to get from webpack module
if ((_e = fiber === null || fiber === void 0 ? void 0 : fiber.elementType) === null || _e === void 0 ? void 0 : _e.__webpack_module__) {
const webpackPath = `webpack://${fiber.elementType.__webpack_module__.id}`;
console.log('✅ [FrameworkDetector] Found webpack module path:', webpackPath);
return webpackPath;
}
console.log('🔍 [FrameworkDetector] No webpack module found');
// Method 6: Try to get component name from React DevTools if available
if (typeof window !== 'undefined' && window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
console.log('🔍 [FrameworkDetector] React DevTools available, trying to get component info...');
const devToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
if (devToolsHook.renderers && devToolsHook.renderers.size > 0) {
const renderer = devToolsHook.renderers.get(1); // Get first renderer
if (renderer && renderer.getFiberRoots) {
console.log('✅ [FrameworkDetector] Found React renderer, attempting to get component info');
// This is complex, but we can try to get component info
}
}
}
// Method 7: Smart approach - return component name for IndexingAgent to find
// This is the same approach used by the React web client
const componentName = this.extractComponentNameFromElement(element);
console.log('🔍 [FrameworkDetector] Extracted component name:', componentName);
console.log('🔍 [FrameworkDetector] Element tag name:', element.tagName.toLowerCase());
if (componentName && componentName !== element.tagName.toLowerCase()) {
// Return component name - IndexingAgent will search for the actual file
console.log('✅ [FrameworkDetector] Returning component name for IndexingAgent search:', componentName);
return componentName;
}
// Method 8: Try to get a meaningful component name from the element
const meaningfulName = this.extractMeaningfulComponentName(element);
if (meaningfulName) {
console.log('✅ [FrameworkDetector] Returning meaningful component name:', meaningfulName);
return meaningfulName;
}
// Only return real file paths, don't generate fake ones
// If no source info is available, return undefined
console.log('⚠️ [FrameworkDetector] No source info available, returning undefined');
return undefined;
}
/**
* Find React fiber in parent components (for React 18+)
*/
static findReactFiberInParents(element) {
let current = element;
let depth = 0;
const maxDepth = 10; // Check up to 10 levels up
while (current && depth < maxDepth) {
// Check if current element has React fiber
const fiber = current._reactInternalFiber;
if (fiber) {
console.log(`✅ [FrameworkDetector] Found React fiber at depth ${depth} in element:`, current.tagName);
return fiber;
}
// Check if current element has React 18+ properties
const ownProps = Object.getOwnPropertyNames(current);
const hasReactProps = ownProps.some(key => key.startsWith('__reactContainer$') ||
key.startsWith('_reactListening'));
if (hasReactProps) {
console.log(`🔍 [FrameworkDetector] Found React 18+ properties at depth ${depth} in element:`, current.tagName);
// Try to find the React component instance
const reactInstance = this.findReactInstanceFromElement(current);
if (reactInstance) {
console.log(`✅ [FrameworkDetector] Found React instance from element:`, current.tagName);
return reactInstance;
}
}
current = current.parentElement;
depth++;
}
console.log('🔍 [FrameworkDetector] No React fiber found in parents up to depth:', maxDepth);
return undefined;
}
/**
* Find React instance from element with React 18+ properties
*/
static findReactInstanceFromElement(element) {
// For React 18+, try to find the React component instance
// This is a more complex approach that might work in some cases
const ownProps = Object.getOwnPropertyNames(element);
for (const prop of ownProps) {
if (prop.startsWith('__reactContainer$')) {
console.log(`🔍 [FrameworkDetector] Found React container property: ${prop}`);
// Try to access the React instance
const container = element[prop];
console.log(`🔍 [FrameworkDetector] Container object:`, container);
if (container) {
// Try multiple paths to find React fiber
if (container._reactInternalFiber) {
console.log('✅ [FrameworkDetector] Found React fiber in container._reactInternalFiber');
return container._reactInternalFiber;
}
if (container._reactInternalInstance) {
console.log('✅ [FrameworkDetector] Found React instance in container._reactInternalInstance');
return container._reactInternalInstance;
}
// Check if container has fiber properties directly
if (container.tag || container.type || container.elementType) {
console.log('✅ [FrameworkDetector] Found React fiber properties directly in container');
return container;
}
// Try to find fiber in container's own properties
const containerProps = Object.getOwnPropertyNames(container);
console.log(`🔍 [FrameworkDetector] Container properties:`, containerProps);
for (const containerProp of containerProps) {
if (containerProp.includes('fiber') || containerProp.includes('react') || containerProp.includes('instance')) {
console.log(`🔍 [FrameworkDetector] Checking container property: ${containerProp}`);
const value = container[containerProp];
if (value && (value.tag || value.type || value.elementType)) {
console.log(`✅ [FrameworkDetector] Found React fiber in container.${containerProp}`);
return value;
}
}
}
}
}
}
return undefined;
}
/**
* Extract a meaningful component name from the element
*/
static extractMeaningfulComponentName(element) {
// Try to get component name from data attributes
if (element.dataset.component) {
return element.dataset.component;
}
// Try to get from aria-label
if (element.getAttribute('aria-label')) {
return element.getAttribute('aria-label');
}
// Try to get from title attribute
if (element.getAttribute('title')) {
return element.getAttribute('title');
}
// Try to get from class names that look like component names
if (element.className) {
const classNames = element.className.split(' ');
for (const className of classNames) {
// Look for classes that might be component names (PascalCase, camelCase)
if (className.length > 2 &&
(className[0] === className[0].toUpperCase() ||
className.includes('Component') ||
className.includes('Button') ||
className.includes('Input') ||
className.includes('Form'))) {
return className;
}
}
}
// Try to get from parent elements
let parent = element.parentElement;
let depth = 0;
const maxDepth = 3;
while (parent && depth < maxDepth) {
if (parent.dataset.component) {
return parent.dataset.component;
}
if (parent.className) {
const classNames = parent.className.split(' ');
for (const className of classNames) {
if (className.length > 2 &&
className[0] === className[0].toUpperCase() &&
!className.includes('css') &&
!className.includes('scss')) {
return className;
}
}
}
parent = parent.parentElement;
depth++;
}
console.log('⚠️ [FrameworkDetector] No clean component name found');
return undefined;
}
/**
* Find component fiber from React root fiber
*/
static findComponentFiberFromRoot(rootFiber) {
var _a, _b, _c, _d;
console.log('🔍 [FrameworkDetector] Traversing React fiber tree from root...');
// Try multiple ways to access the current fiber
let currentFiber = rootFiber.current || rootFiber.currentFiber || rootFiber._currentFiber;
if (!currentFiber) {
console.log('🔍 [FrameworkDetector] No current fiber in root, trying alternative access...');
// Try to find any fiber in the root object
const rootProps = Object.getOwnPropertyNames(rootFiber);
console.log('🔍 [FrameworkDetector] Root fiber properties:', rootProps);
for (const prop of rootProps) {
if (prop.includes('fiber') || prop.includes('current') || prop.includes('child')) {
const value = rootFiber[prop];
if (value && typeof value === 'object' && (value.tag || value.type)) {
console.log(`🔍 [FrameworkDetector] Found potential fiber in root.${prop}:`, value);
currentFiber = value;
break;
}
}
}
}
if (!currentFiber) {
console.log('🔍 [FrameworkDetector] Still no current fiber found');
return undefined;
}
// Traverse down to find component fibers
let depth = 0;
const maxDepth = 20; // Prevent infinite loops
while (currentFiber && depth < maxDepth) {
console.log(`🔍 [FrameworkDetector] Fiber at depth ${depth}:`, {
tag: currentFiber.tag,
type: currentFiber.type,
elementType: currentFiber.elementType
});
// Check if this fiber has source file information
if ((_b = (_a = currentFiber.type) === null || _a === void 0 ? void 0 : _a.__source) === null || _b === void 0 ? void 0 : _b.fileName) {
console.log('✅ [FrameworkDetector] Found component fiber with source file:', currentFiber.type.__source.fileName);
return currentFiber;
}
if ((_d = (_c = currentFiber.elementType) === null || _c === void 0 ? void 0 : _c.__source) === null || _d === void 0 ? void 0 : _d.fileName) {
console.log('✅ [FrameworkDetector] Found component fiber with source file in elementType:', currentFiber.elementType.__source.fileName);
return currentFiber;
}
// Check if this is a component fiber (tag: 1 = FunctionComponent, 2 = ClassComponent)
if (currentFiber.tag === 1 || currentFiber.tag === 2) {
console.log('🔍 [FrameworkDetector] Found component fiber, checking for source...');
// This is a component, but might not have source info
}
// Move to child fiber
if (currentFiber.child) {
currentFiber = currentFiber.child;
}
else if (currentFiber.sibling) {
currentFiber = currentFiber.sibling;
}
else {
// Go back up and try siblings
while (currentFiber && !currentFiber.sibling && currentFiber.return) {
currentFiber = currentFiber.return;
}
if (currentFiber && currentFiber.sibling) {
currentFiber = currentFiber.sibling;
}
else {
break; // No more fibers to traverse
}
}
depth++;
}
console.log('🔍 [FrameworkDetector] No component fiber with source file found in tree');
return undefined;
}
static getVueComponentFile(element) {
var _a, _b;
const vueElement = element;
const instance = vueElement.__vue__;
if ((_a = instance === null || instance === void 0 ? void 0 : instance.$options) === null || _a === void 0 ? void 0 : _a.__file) {
return instance.$options.__file;
}
if ((_b = instance === null || instance === void 0 ? void 0 : instance.$options) === null || _b === void 0 ? void 0 : _b.name) {
return `${instance.$options.name}.vue`;
}
return undefined;
}
static getAngularComponentFile(element) {
var _a, _b;
const angularElement = element;
const ngContext = angularElement.__ngContext__;
if ((_a = ngContext === null || ngContext === void 0 ? void 0 : ngContext.componentType) === null || _a === void 0 ? void 0 : _a.__file) {
return ngContext.componentType.__file;
}
if ((_b = ngContext === null || ngContext === void 0 ? void 0 : ngContext.componentType) === null || _b === void 0 ? void 0 : _b.name) {
return `${ngContext.componentType.name}.ts`;
}
return undefined;
}
static getSvelteComponentFile(element) {
var _a, _b, _c;
const svelteElement = element;
const instance = svelteElement._svelte;
if ((_c = (_b = (_a = instance === null || instance === void 0 ? void 0 : instance.$$) === null || _a === void 0 ? void 0 : _a.component) === null || _b === void 0 ? void 0 : _b.$$render) === null || _c === void 0 ? void 0 : _c.name) {
return `${instance.$$.component.$$render.name}.svelte`;
}
return undefined;
}
static getVanillaComponentFile(element) {
// For vanilla JS, try to get from data attributes or class names
if (element.dataset.component) {
return `${element.dataset.component}.js`;
}
if (element.className) {
const classNames = element.className.split(' ');
for (const className of classNames) {
if (className.includes('component') || className.includes('widget')) {
return `${cl