@rn-ave/core 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.
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatForAgent = formatForAgent;
4
+ exports.formatCompact = formatCompact;
5
+ exports.formatForTerminal = formatForTerminal;
6
+ const source_1 = require("../fiber/source");
7
+ /**
8
+ * Formats extracted context as markdown for AI agents
9
+ */
10
+ function formatForAgent(context) {
11
+ const lines = [];
12
+ lines.push(`## Selected Component: ${context.componentName}`);
13
+ lines.push('');
14
+ // File location
15
+ if (context.source) {
16
+ lines.push(`### File`);
17
+ lines.push(`\`${(0, source_1.formatSourceLocation)(context.source)}\``);
18
+ lines.push('');
19
+ }
20
+ // Component hierarchy
21
+ if (context.componentHierarchy.length > 0) {
22
+ lines.push(`### Component Hierarchy`);
23
+ lines.push('```');
24
+ context.componentHierarchy.forEach((name, i) => {
25
+ const indent = ' '.repeat(i);
26
+ lines.push(`${indent}<${name}>`);
27
+ });
28
+ lines.push('```');
29
+ lines.push('');
30
+ }
31
+ // Props
32
+ if (Object.keys(context.props).length > 0) {
33
+ lines.push(`### Props`);
34
+ lines.push('```json');
35
+ lines.push(JSON.stringify(context.props, null, 2));
36
+ lines.push('```');
37
+ lines.push('');
38
+ }
39
+ // JSX Preview
40
+ lines.push(`### JSX Preview`);
41
+ lines.push('```jsx');
42
+ lines.push(context.jsxPreview);
43
+ lines.push('```');
44
+ lines.push('');
45
+ lines.push('---');
46
+ lines.push(`*Captured at ${new Date(context.timestamp).toLocaleTimeString()}*`);
47
+ return lines.join('\n');
48
+ }
49
+ /**
50
+ * Formats context as compact single-line (for clipboard)
51
+ */
52
+ function formatCompact(context) {
53
+ const parts = [];
54
+ parts.push(context.componentName);
55
+ if (context.source) {
56
+ parts.push(`at ${(0, source_1.formatSourceLocation)(context.source)}`);
57
+ }
58
+ if (context.componentHierarchy.length > 1) {
59
+ parts.push(`in ${context.componentHierarchy[context.componentHierarchy.length - 1]}`);
60
+ }
61
+ return parts.join(' ');
62
+ }
63
+ /**
64
+ * Formats context for terminal/console output
65
+ */
66
+ function formatForTerminal(context) {
67
+ const lines = [];
68
+ const divider = '─'.repeat(50);
69
+ lines.push('');
70
+ lines.push(`┌${divider}┐`);
71
+ lines.push(`│ 🔍 RN-AVE: ${context.componentName.padEnd(32)} │`);
72
+ lines.push(`├${divider}┤`);
73
+ if (context.source) {
74
+ lines.push(`│ 📁 ${(0, source_1.formatSourceLocation)(context.source).padEnd(45)} │`);
75
+ }
76
+ if (context.componentHierarchy.length > 0) {
77
+ const hierarchy = context.componentHierarchy.slice(0, 5).join(' → ');
78
+ lines.push(`│ 🏗️ ${hierarchy.substring(0, 44).padEnd(44)} │`);
79
+ }
80
+ if (Object.keys(context.props).length > 0) {
81
+ lines.push(`├${divider}┤`);
82
+ lines.push(`│ Props:${' '.repeat(42)} │`);
83
+ const propsStr = JSON.stringify(context.props, null, 2);
84
+ propsStr.split('\n').slice(0, 6).forEach(line => {
85
+ const trimmed = line.substring(0, 46);
86
+ lines.push(`│ ${trimmed.padEnd(46)} │`);
87
+ });
88
+ if (propsStr.split('\n').length > 6) {
89
+ lines.push(`│ ...${' '.repeat(43)} │`);
90
+ }
91
+ }
92
+ lines.push(`└${divider}┘`);
93
+ lines.push('');
94
+ return lines.join('\n');
95
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Dev Menu Integration for React Native and Expo
3
+ * Adds "Toggle Inspector" to the development menu
4
+ *
5
+ * Works with:
6
+ * - React Native CLI: Shake or Cmd+D (iOS) / Cmd+M (Android)
7
+ * - Expo Dev Client: Shake or three-finger long press
8
+ * - Expo Go: Shake gesture
9
+ */
10
+ /**
11
+ * Register the inspector toggle in the dev menu
12
+ * Call this once when the Inspector component mounts
13
+ */
14
+ export declare function registerDevMenuToggle(onToggle: () => void): void;
15
+ /**
16
+ * Update the toggle callback (when component re-renders)
17
+ */
18
+ export declare function updateDevMenuCallback(onToggle: () => void): void;
19
+ /**
20
+ * Check if dev menu is available
21
+ */
22
+ export declare function isDevMenuAvailable(): boolean;
23
+ /**
24
+ * Instructions for opening dev menu on different platforms
25
+ */
26
+ export declare function getDevMenuInstructions(): string;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * Dev Menu Integration for React Native and Expo
4
+ * Adds "Toggle Inspector" to the development menu
5
+ *
6
+ * Works with:
7
+ * - React Native CLI: Shake or Cmd+D (iOS) / Cmd+M (Android)
8
+ * - Expo Dev Client: Shake or three-finger long press
9
+ * - Expo Go: Shake gesture
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.registerDevMenuToggle = registerDevMenuToggle;
13
+ exports.updateDevMenuCallback = updateDevMenuCallback;
14
+ exports.isDevMenuAvailable = isDevMenuAvailable;
15
+ exports.getDevMenuInstructions = getDevMenuInstructions;
16
+ const react_native_1 = require("react-native");
17
+ let isRegistered = false;
18
+ let toggleCallback = null;
19
+ /**
20
+ * Register the inspector toggle in the dev menu
21
+ * Call this once when the Inspector component mounts
22
+ */
23
+ function registerDevMenuToggle(onToggle) {
24
+ if (typeof __DEV__ === 'undefined' || !__DEV__)
25
+ return;
26
+ toggleCallback = onToggle;
27
+ if (isRegistered)
28
+ return;
29
+ isRegistered = true;
30
+ registerWithDevSettings(onToggle);
31
+ }
32
+ /**
33
+ * Update the toggle callback (when component re-renders)
34
+ */
35
+ function updateDevMenuCallback(onToggle) {
36
+ toggleCallback = onToggle;
37
+ }
38
+ /**
39
+ * Register with React Native's DevSettings
40
+ */
41
+ function registerWithDevSettings(onToggle) {
42
+ try {
43
+ if (react_native_1.DevSettings && typeof react_native_1.DevSettings.addMenuItem === 'function') {
44
+ react_native_1.DevSettings.addMenuItem('Toggle Dev Inspector', () => {
45
+ toggleCallback?.();
46
+ });
47
+ }
48
+ }
49
+ catch {
50
+ // DevSettings not available (might be production build)
51
+ }
52
+ }
53
+ /**
54
+ * Check if dev menu is available
55
+ */
56
+ function isDevMenuAvailable() {
57
+ if (typeof __DEV__ === 'undefined' || !__DEV__)
58
+ return false;
59
+ try {
60
+ if (react_native_1.DevSettings && typeof react_native_1.DevSettings.addMenuItem === 'function') {
61
+ return true;
62
+ }
63
+ }
64
+ catch {
65
+ // ignore
66
+ }
67
+ return false;
68
+ }
69
+ /**
70
+ * Instructions for opening dev menu on different platforms
71
+ */
72
+ function getDevMenuInstructions() {
73
+ if (react_native_1.Platform.OS === 'ios') {
74
+ return 'Shake device or press Cmd+D to open dev menu, then tap "Toggle Dev Inspector"';
75
+ }
76
+ else if (react_native_1.Platform.OS === 'android') {
77
+ return 'Shake device or press Cmd+M to open dev menu, then tap "Toggle Dev Inspector"';
78
+ }
79
+ return 'Shake device to open dev menu, then tap "Toggle Dev Inspector"';
80
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Editor launching utilities
3
+ * Only Cursor is supported.
4
+ */
5
+ import type { SourceLocation } from './types';
6
+ export interface LaunchEditorOptions {
7
+ /** Source code information */
8
+ source: SourceLocation;
9
+ /** Editor name - only 'cursor' is supported */
10
+ editor?: string;
11
+ /** Dev server URL (for Metro-based launching) */
12
+ devServerUrl?: string;
13
+ }
14
+ /**
15
+ * Main function to launch Cursor editor
16
+ * Tries multiple methods:
17
+ * 1. URL scheme (deep linking)
18
+ * 2. Metro inspector endpoint
19
+ */
20
+ export declare function launchEditor(options: LaunchEditorOptions): Promise<boolean>;
package/dist/editor.js ADDED
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * Editor launching utilities
4
+ * Only Cursor is supported.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.launchEditor = launchEditor;
8
+ const react_native_1 = require("react-native");
9
+ /**
10
+ * Default dev server URL based on platform
11
+ */
12
+ function getDefaultDevServerUrl() {
13
+ if (react_native_1.Platform.OS === 'android') {
14
+ return 'http://10.0.2.2:8081';
15
+ }
16
+ return 'http://localhost:8081';
17
+ }
18
+ /**
19
+ * Build query string for editor endpoint
20
+ */
21
+ function buildEditorQueryString(source) {
22
+ const params = new URLSearchParams();
23
+ params.set('file', source.file);
24
+ if (source.line) {
25
+ params.set('line', String(source.line));
26
+ }
27
+ if (source.column) {
28
+ params.set('column', String(source.column));
29
+ }
30
+ params.set('editor', 'cursor');
31
+ return params.toString();
32
+ }
33
+ /**
34
+ * Launch editor via standard inspector endpoint
35
+ */
36
+ async function launchEditorViaInspector(options) {
37
+ const { source, devServerUrl } = options;
38
+ const baseUrl = devServerUrl || getDefaultDevServerUrl();
39
+ const queryString = buildEditorQueryString(source);
40
+ const url = `${baseUrl}/__inspect-open-in-editor?${queryString}`;
41
+ try {
42
+ const response = await fetch(url, { method: 'GET' });
43
+ return response.ok;
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ /**
50
+ * Try to launch Cursor via URL scheme (deep linking)
51
+ */
52
+ async function launchEditorViaUrlScheme(source) {
53
+ const { file, line, column } = source;
54
+ const url = `cursor://file${file}:${line}:${column || 1}`;
55
+ try {
56
+ const canOpen = await react_native_1.Linking.canOpenURL(url);
57
+ if (canOpen) {
58
+ await react_native_1.Linking.openURL(url);
59
+ return true;
60
+ }
61
+ return false;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ /**
68
+ * Main function to launch Cursor editor
69
+ * Tries multiple methods:
70
+ * 1. URL scheme (deep linking)
71
+ * 2. Metro inspector endpoint
72
+ */
73
+ async function launchEditor(options) {
74
+ const { source } = options;
75
+ console.log('\n🚀 [rn-ave] launchEditor called');
76
+ console.log(' source:', source);
77
+ // Try URL scheme first
78
+ console.log(' Trying URL scheme...');
79
+ const urlSuccess = await launchEditorViaUrlScheme(source);
80
+ console.log(' URL scheme result:', urlSuccess);
81
+ if (urlSuccess) {
82
+ return true;
83
+ }
84
+ // Try Metro inspector endpoint
85
+ console.log(' Trying Metro inspector endpoint...');
86
+ const inspectorSuccess = await launchEditorViaInspector(options);
87
+ console.log(' Inspector endpoint result:', inspectorSuccess);
88
+ if (inspectorSuccess) {
89
+ return true;
90
+ }
91
+ console.log(' All methods failed');
92
+ return false;
93
+ }
@@ -0,0 +1,18 @@
1
+ import type { FiberRoot, RNDevToolsHook } from '../types';
2
+ /**
3
+ * Installs the DevTools hook to capture React fiber tree
4
+ * Must be called BEFORE React Native initializes
5
+ */
6
+ export declare function installHook(): RNDevToolsHook;
7
+ /**
8
+ * Gets the current fiber root (usually there's only one in RN)
9
+ */
10
+ export declare function getFiberRoot(): FiberRoot | null;
11
+ /**
12
+ * Debug function to log hook state
13
+ */
14
+ export declare function debugHookState(): void;
15
+ /**
16
+ * Checks if the hook has been installed
17
+ */
18
+ export declare function isHookInstalled(): boolean;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installHook = installHook;
4
+ exports.getFiberRoot = getFiberRoot;
5
+ exports.debugHookState = debugHookState;
6
+ exports.isHookInstalled = isHookInstalled;
7
+ // Storage for fiber roots
8
+ const fiberRoots = new Map();
9
+ let hookInstalled = false;
10
+ /**
11
+ * Wraps an existing DevTools hook to capture fiber roots
12
+ */
13
+ function wrapExistingHook(existingHook) {
14
+ const originalOnCommit = existingHook.onCommitFiberRoot;
15
+ existingHook.onCommitFiberRoot = function (rendererID, root) {
16
+ fiberRoots.set(rendererID, root);
17
+ if (originalOnCommit) {
18
+ originalOnCommit.call(this, rendererID, root);
19
+ }
20
+ };
21
+ return existingHook;
22
+ }
23
+ /**
24
+ * Installs the DevTools hook to capture React fiber tree
25
+ * Must be called BEFORE React Native initializes
26
+ */
27
+ function installHook() {
28
+ if (hookInstalled) {
29
+ return globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
30
+ }
31
+ // Check if hook already exists
32
+ if (globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
33
+ hookInstalled = true;
34
+ return wrapExistingHook(globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__);
35
+ }
36
+ // Create our own hook
37
+ const hook = {
38
+ renderers: new Map(),
39
+ inject(renderer) {
40
+ const id = this.renderers.size + 1;
41
+ this.renderers.set(id, renderer);
42
+ return id;
43
+ },
44
+ onCommitFiberRoot(rendererID, root) {
45
+ fiberRoots.set(rendererID, root);
46
+ },
47
+ onCommitFiberUnmount(_rendererID, _fiber) {
48
+ // Cleanup references if needed
49
+ },
50
+ getFiberRoots(rendererID) {
51
+ const root = fiberRoots.get(rendererID);
52
+ return root ? new Set([root]) : new Set();
53
+ }
54
+ };
55
+ globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__ = hook;
56
+ hookInstalled = true;
57
+ return hook;
58
+ }
59
+ /**
60
+ * Gets the current fiber root (usually there's only one in RN)
61
+ */
62
+ function getFiberRoot() {
63
+ // Try to get from stored roots
64
+ for (const root of fiberRoots.values()) {
65
+ if (root && root.current) {
66
+ return root;
67
+ }
68
+ }
69
+ // Try to get from DevTools hook
70
+ const hook = globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__;
71
+ if (hook?.getFiberRoots) {
72
+ for (const rendererID of hook.renderers.keys()) {
73
+ const roots = hook.getFiberRoots(rendererID);
74
+ for (const root of roots) {
75
+ if (root && root.current) {
76
+ return root;
77
+ }
78
+ }
79
+ }
80
+ }
81
+ // Last resort: try to find fiber from any stored root
82
+ if (hook?.renderers) {
83
+ for (const [id] of hook.renderers) {
84
+ const roots = fiberRoots.get(id);
85
+ if (roots && roots.current) {
86
+ return roots;
87
+ }
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ /**
93
+ * Debug function to log hook state
94
+ */
95
+ function debugHookState() {
96
+ // Debug logging disabled
97
+ }
98
+ /**
99
+ * Checks if the hook has been installed
100
+ */
101
+ function isHookInstalled() {
102
+ return hookInstalled;
103
+ }
@@ -0,0 +1,21 @@
1
+ import type { Fiber, SourceLocation } from '../types';
2
+ /**
3
+ * Extracts source location from a fiber node (non-recursive)
4
+ */
5
+ export declare function getSourceLocation(fiber: Fiber): SourceLocation | null;
6
+ /**
7
+ * Recursive version of getSourceLocation that follows _debugOwner
8
+ */
9
+ export declare function getSourceLocationRecursive(fiber: Fiber): SourceLocation | null;
10
+ /**
11
+ * Checks if a fiber's source location is from node_modules
12
+ */
13
+ export declare function isFromNodeModules(fiber: Fiber): boolean;
14
+ /**
15
+ * Formats source location as a readable string
16
+ */
17
+ export declare function formatSourceLocation(source: SourceLocation | null): string;
18
+ /**
19
+ * Gets relative path from absolute path (simple version)
20
+ */
21
+ export declare function getRelativePath(absolutePath: string, rootDir?: string): string;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSourceLocation = getSourceLocation;
4
+ exports.getSourceLocationRecursive = getSourceLocationRecursive;
5
+ exports.isFromNodeModules = isFromNodeModules;
6
+ exports.formatSourceLocation = formatSourceLocation;
7
+ exports.getRelativePath = getRelativePath;
8
+ /**
9
+ * Extracts source location from a fiber node (non-recursive)
10
+ */
11
+ function getSourceLocation(fiber) {
12
+ // Strategy 1: Try custom prop from our Babel plugin (PRIMARY)
13
+ const rnAveMeta = fiber.memoizedProps?.__rnAve;
14
+ if (rnAveMeta && rnAveMeta.file) {
15
+ console.log(' [source] Found via __rnAve prop:', rnAveMeta.file);
16
+ return {
17
+ file: rnAveMeta.file,
18
+ line: rnAveMeta.line,
19
+ column: rnAveMeta.column,
20
+ };
21
+ }
22
+ // Strategy 2: Try __rnInspect (legacy or standard RN metadata - FALLBACK)
23
+ const rnInspect = fiber.memoizedProps?.__rnInspect;
24
+ if (rnInspect) {
25
+ if (rnInspect.fileName) {
26
+ console.log(' [source] Found via __rnInspect (fileName):', rnInspect.fileName);
27
+ return {
28
+ file: rnInspect.fileName,
29
+ line: rnInspect.lineNumber,
30
+ column: rnInspect.columnNumber,
31
+ };
32
+ }
33
+ if (rnInspect.file) {
34
+ console.log(' [source] Found via __rnInspect (file):', rnInspect.file);
35
+ return {
36
+ file: rnInspect.file,
37
+ line: rnInspect.line,
38
+ column: rnInspect.column,
39
+ };
40
+ }
41
+ }
42
+ // Strategy 3: Try _debugSource (React development mode)
43
+ if (fiber._debugSource) {
44
+ console.log(' [source] Found via _debugSource:', fiber._debugSource.fileName);
45
+ return {
46
+ file: fiber._debugSource.fileName,
47
+ line: fiber._debugSource.lineNumber,
48
+ column: fiber._debugSource.columnNumber,
49
+ };
50
+ }
51
+ return null;
52
+ }
53
+ /**
54
+ * Recursive version of getSourceLocation that follows _debugOwner
55
+ */
56
+ function getSourceLocationRecursive(fiber) {
57
+ const localSource = getSourceLocation(fiber);
58
+ if (localSource)
59
+ return localSource;
60
+ if (fiber._debugOwner) {
61
+ return getSourceLocationRecursive(fiber._debugOwner);
62
+ }
63
+ return null;
64
+ }
65
+ /**
66
+ * Checks if a fiber's source location is from node_modules
67
+ */
68
+ function isFromNodeModules(fiber) {
69
+ const source = getSourceLocation(fiber);
70
+ if (!source?.file)
71
+ return false;
72
+ return source.file.includes('node_modules');
73
+ }
74
+ /**
75
+ * Formats source location as a readable string
76
+ */
77
+ function formatSourceLocation(source) {
78
+ if (!source)
79
+ return 'Unknown location';
80
+ const column = source.column ? `:${source.column}` : '';
81
+ return `${source.file}:${source.line}${column}`;
82
+ }
83
+ /**
84
+ * Gets relative path from absolute path (simple version)
85
+ */
86
+ function getRelativePath(absolutePath, rootDir) {
87
+ if (!rootDir)
88
+ return absolutePath;
89
+ // Simple relative path calculation
90
+ if (absolutePath.startsWith(rootDir)) {
91
+ return absolutePath.slice(rootDir.length + 1);
92
+ }
93
+ return absolutePath;
94
+ }
@@ -0,0 +1,27 @@
1
+ import type { Fiber, FiberRoot, ComponentInfo } from '../types';
2
+ /**
3
+ * Finds a fiber node by its native tag
4
+ */
5
+ export declare function findFiberByNativeTag(nativeTag: number, fiberRoot: FiberRoot): Fiber | null;
6
+ /**
7
+ * Gets the display name of a component from its fiber
8
+ */
9
+ export declare function getDisplayName(fiber: Fiber): string;
10
+ /**
11
+ * Gets the component hierarchy from a fiber node up to the root
12
+ */
13
+ export declare function getComponentHierarchy(fiber: Fiber): ComponentInfo[];
14
+ /**
15
+ * Gets all props from a fiber, excluding internal ones
16
+ */
17
+ export declare function getCleanProps(fiber: Fiber): Record<string, any>;
18
+ /**
19
+ * Finds the nearest composite component ancestor
20
+ * @param fiber - The starting fiber node
21
+ * @param skipNodeModules - If true, skips components from node_modules and returns the nearest user-code component
22
+ */
23
+ export declare function findNearestComposite(fiber: Fiber, skipNodeModules?: boolean): Fiber | null;
24
+ /**
25
+ * Collects all host component fibers (View, Text, etc.) from the tree
26
+ */
27
+ export declare function collectAllHostFibers(fiberRoot: FiberRoot): Fiber[];