@keak/sdk 2.0.1 → 2.0.2
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.
- package/dist/index.cjs.js +143 -441
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +143 -441
- package/dist/index.js.map +1 -1
- package/dist/runtime/sourceInjector.d.ts +41 -1
- package/dist/runtime/sourceInjector.d.ts.map +1 -1
- package/dist/toolbar/KeakToolbar.d.ts.map +1 -1
- package/dist/toolbar.js +23 -4
- package/dist/toolbar.js.map +1 -1
- package/package.json +1 -2
package/dist/index.cjs.js
CHANGED
|
@@ -35,301 +35,88 @@ var css_248z$1 = "@import \"./toolbar/styles/toolbar.css\";.keak-toolbar,.keak-t
|
|
|
35
35
|
styleInject(css_248z$1);
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
* jsx-tool may inject source locations directly into elements or via WebSocket
|
|
38
|
+
* Find the internal React fiber node from a DOM element
|
|
40
39
|
*/
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
__JSX_TOOL__: !!window.__JSX_TOOL__,
|
|
59
|
-
__JSX_TOOL_SOURCE_MAP__: !!window.__JSX_TOOL_SOURCE_MAP__,
|
|
60
|
-
__JSX_TOOL_WS__: !!window.__JSX_TOOL_WS__
|
|
61
|
-
});
|
|
40
|
+
function getFiberFromNode(node) {
|
|
41
|
+
try {
|
|
42
|
+
// React stores fiber on DOM nodes with keys starting with __react
|
|
43
|
+
const key = Object.keys(node).find(k => k.startsWith('__reactFiber') ||
|
|
44
|
+
k.startsWith('__reactInternalInstance'));
|
|
45
|
+
if (key) {
|
|
46
|
+
const fiber = node[key];
|
|
47
|
+
return fiber;
|
|
48
|
+
}
|
|
49
|
+
// Fallback: search through all properties for React-like objects
|
|
50
|
+
const allValues = Object.values(node);
|
|
51
|
+
const fiberCandidate = allValues.find((v) => v &&
|
|
52
|
+
typeof v === 'object' &&
|
|
53
|
+
v.hasOwnProperty('memoizedProps') &&
|
|
54
|
+
v.hasOwnProperty('return') &&
|
|
55
|
+
(v.hasOwnProperty('type') || v.hasOwnProperty('elementType')));
|
|
56
|
+
return fiberCandidate;
|
|
62
57
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Skip if already has our source location
|
|
67
|
-
if (element.hasAttribute('data-keak-src'))
|
|
68
|
-
return;
|
|
69
|
-
// Skip Keak's own elements
|
|
70
|
-
if (element.classList.contains('keak-toolbar') ||
|
|
71
|
-
element.closest('.keak-toolbar') ||
|
|
72
|
-
element.id === 'keak-toolbar-root') {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
// Check for jsx-tool injected source attributes
|
|
76
|
-
// jsx-tool may use various attribute names - check common patterns
|
|
77
|
-
const jsxSource = element.getAttribute('data-jsx-source') ||
|
|
78
|
-
element.getAttribute('data-jsx-tool-source') ||
|
|
79
|
-
element.getAttribute('data-source') ||
|
|
80
|
-
element.getAttribute('data-jsx-file') ||
|
|
81
|
-
element.getAttribute('data-jsx-line');
|
|
82
|
-
if (jsxSource) {
|
|
83
|
-
// Copy jsx-tool source location to our attribute
|
|
84
|
-
element.setAttribute('data-keak-src', jsxSource);
|
|
85
|
-
injectedCount++;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
// Also check for jsx-tool ID attributes that we can use to look up in source map
|
|
89
|
-
const jsxToolId = element.getAttribute('data-jsx-tool-id') ||
|
|
90
|
-
element.getAttribute('data-jsx-id') ||
|
|
91
|
-
element.getAttribute('data-reactid');
|
|
92
|
-
if (jsxToolId && window.__JSX_TOOL_SOURCE_MAP__) {
|
|
93
|
-
const sourceMap = window.__JSX_TOOL_SOURCE_MAP__;
|
|
94
|
-
if (sourceMap[jsxToolId]) {
|
|
95
|
-
const mapping = sourceMap[jsxToolId];
|
|
96
|
-
if (mapping.file && mapping.line) {
|
|
97
|
-
const sourceLocation = `${mapping.file}:${mapping.line}:${mapping.column || 1}`;
|
|
98
|
-
element.setAttribute('data-keak-src', sourceLocation);
|
|
99
|
-
injectedCount++;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
if (injectedCount > 0) {
|
|
106
|
-
console.log(`[Keak] ✅ Injected ${injectedCount} source locations via jsx-tool (direct attributes)`);
|
|
107
|
-
return injectedCount;
|
|
108
|
-
}
|
|
109
|
-
else if (hasJsxToolGlobal) {
|
|
110
|
-
console.log('[Keak] 🔍 jsx-tool globals found but no source locations injected yet (may need to wait for jsx-tool to initialize)');
|
|
111
|
-
}
|
|
112
|
-
// Check for jsx-tool WebSocket connection or source map global
|
|
113
|
-
const jsxTool = window.__JSX_TOOL__;
|
|
114
|
-
const jsxToolWS = window.__JSX_TOOL_WS__;
|
|
115
|
-
const jsxToolSourceMap = window.__JSX_TOOL_SOURCE_MAP__;
|
|
116
|
-
// Debug logging for jsx-tool detection
|
|
117
|
-
if (jsxTool || jsxToolWS || jsxToolSourceMap) {
|
|
118
|
-
console.log('[Keak] 🔍 jsx-tool detected:', {
|
|
119
|
-
__JSX_TOOL__: !!jsxTool,
|
|
120
|
-
__JSX_TOOL_WS__: !!jsxToolWS,
|
|
121
|
-
__JSX_TOOL_SOURCE_MAP__: !!jsxToolSourceMap,
|
|
122
|
-
sourceMapKeys: jsxToolSourceMap ? Object.keys(jsxToolSourceMap).length : 0
|
|
123
|
-
});
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.warn('[Keak Source] Error accessing fiber node:', error);
|
|
60
|
+
return null;
|
|
124
61
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const sourceLocation = `${mapping.file}:${mapping.line}:${mapping.column || 1}`;
|
|
145
|
-
element.setAttribute('data-keak-src', sourceLocation);
|
|
146
|
-
if (mapping.component) {
|
|
147
|
-
element.setAttribute('data-keak-component', mapping.component);
|
|
148
|
-
}
|
|
149
|
-
injectedCount++;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
if (injectedCount > 0) {
|
|
154
|
-
console.log(`[Keak] ✅ Injected ${injectedCount} source locations via jsx-tool (global source map)`);
|
|
155
|
-
return injectedCount;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// Try to connect to jsx-tool WebSocket server (non-blocking)
|
|
159
|
-
// Only try once per page load to avoid repeated connection attempts
|
|
160
|
-
// Use the WebSocket URL from jsx-tool's configuration
|
|
161
|
-
if (!jsxTool && !jsxToolWS && !window.__KEAK_JSX_TOOL_CONNECTION_ATTEMPTED__) {
|
|
162
|
-
// Check for jsx-tool WebSocket URL (set by Vite plugin or manual script)
|
|
163
|
-
const jsxToolWsUrl = window.__JSX_TOOL_DEV_SERVER_WS_URL__;
|
|
164
|
-
// Also check for other indicators
|
|
165
|
-
const hasJsxToolScript = Array.from(document.scripts).some(script => script.src.includes('jsx-tool') || script.textContent?.includes('jsx-tool') || script.textContent?.includes('__JSX_TOOL_DEV_SERVER_WS_URL__'));
|
|
166
|
-
// Check for any elements with jsx-tool attributes (indicates jsx-tool is active)
|
|
167
|
-
const hasJsxToolAttributes = document.querySelector('[data-jsx-source], [data-jsx-tool-source], [data-jsx-tool-id]') !== null;
|
|
168
|
-
// Use jsx-tool's WebSocket URL if available, otherwise try default port
|
|
169
|
-
let wsUrl = null;
|
|
170
|
-
if (jsxToolWsUrl) {
|
|
171
|
-
// Use the URL provided by jsx-tool
|
|
172
|
-
wsUrl = jsxToolWsUrl;
|
|
173
|
-
console.log('[Keak] 🔍 Using jsx-tool WebSocket URL:', wsUrl);
|
|
174
|
-
}
|
|
175
|
-
else if (hasJsxToolScript || hasJsxToolAttributes || window.__JSX_TOOL_WS_PORT__) {
|
|
176
|
-
// Fallback to default port if indicators suggest jsx-tool might be running
|
|
177
|
-
const wsPort = window.__JSX_TOOL_WS_PORT__ || 12021;
|
|
178
|
-
const wsHost = window.__JSX_TOOL_WS_HOST__ || 'localhost';
|
|
179
|
-
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
180
|
-
wsUrl = `${wsProtocol}//${wsHost}:${wsPort}`;
|
|
181
|
-
console.log('[Keak] 🔍 jsx-tool indicators found, attempting WebSocket connection to:', wsUrl);
|
|
182
|
-
}
|
|
183
|
-
if (wsUrl) {
|
|
184
|
-
window.__KEAK_JSX_TOOL_CONNECTION_ATTEMPTED__ = true;
|
|
185
|
-
try {
|
|
186
|
-
// Try to establish connection (non-blocking, async)
|
|
187
|
-
// Use a very short timeout to fail fast if server isn't running
|
|
188
|
-
const testWs = new WebSocket(wsUrl);
|
|
189
|
-
// Set a short timeout to close connection if it doesn't establish quickly
|
|
190
|
-
const connectionTimeout = setTimeout(() => {
|
|
191
|
-
if (testWs.readyState === WebSocket.CONNECTING) {
|
|
192
|
-
testWs.close();
|
|
193
|
-
}
|
|
194
|
-
}, 1000); // Reduced to 1 second to fail faster
|
|
195
|
-
testWs.onopen = () => {
|
|
196
|
-
clearTimeout(connectionTimeout);
|
|
197
|
-
console.log('[Keak] ✅ Connected to jsx-tool WebSocket server');
|
|
198
|
-
window.__JSX_TOOL_WS__ = testWs;
|
|
199
|
-
// Request source mappings for all elements
|
|
200
|
-
testWs.send(JSON.stringify({ type: 'getSourceMappings' }));
|
|
201
|
-
};
|
|
202
|
-
testWs.onmessage = (event) => {
|
|
203
|
-
try {
|
|
204
|
-
const data = JSON.parse(event.data);
|
|
205
|
-
if (data.type === 'sourceMappings' && data.mappings) {
|
|
206
|
-
let wsInjectedCount = 0;
|
|
207
|
-
// Apply source mappings to elements
|
|
208
|
-
Object.entries(data.mappings).forEach(([elementId, mapping]) => {
|
|
209
|
-
const element = document.querySelector(`[data-jsx-tool-id="${elementId}"]`);
|
|
210
|
-
if (element && mapping.file && mapping.line && !element.hasAttribute('data-keak-src')) {
|
|
211
|
-
const sourceLocation = `${mapping.file}:${mapping.line}:${mapping.column || 1}`;
|
|
212
|
-
element.setAttribute('data-keak-src', sourceLocation);
|
|
213
|
-
if (mapping.component) {
|
|
214
|
-
element.setAttribute('data-keak-component', mapping.component);
|
|
215
|
-
}
|
|
216
|
-
wsInjectedCount++;
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
if (wsInjectedCount > 0) {
|
|
220
|
-
console.log(`[Keak] ✅ Injected ${wsInjectedCount} source locations via jsx-tool WebSocket`);
|
|
221
|
-
// Re-run injection to update counts
|
|
222
|
-
setTimeout(() => injectSourceLocations(), 100);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
catch (error) {
|
|
227
|
-
console.warn('[Keak] Error parsing jsx-tool WebSocket message:', error);
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
testWs.onerror = () => {
|
|
231
|
-
clearTimeout(connectionTimeout);
|
|
232
|
-
// WebSocket connection failed - this is expected if jsx-tool isn't running
|
|
233
|
-
// Silently fall back to React Fiber - no error logging needed
|
|
234
|
-
};
|
|
235
|
-
testWs.onclose = () => {
|
|
236
|
-
clearTimeout(connectionTimeout);
|
|
237
|
-
// Connection closed - expected if jsx-tool server isn't running
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
catch (error) {
|
|
241
|
-
// WebSocket not available - will fall back silently
|
|
242
|
-
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Extract source information from a React fiber node
|
|
65
|
+
*/
|
|
66
|
+
function getSourceFromFiber(fiber) {
|
|
67
|
+
if (!fiber)
|
|
68
|
+
return null;
|
|
69
|
+
let pointer = fiber;
|
|
70
|
+
let steps = 0;
|
|
71
|
+
const maxSteps = 20; // Prevent infinite loops
|
|
72
|
+
while (pointer && steps < maxSteps) {
|
|
73
|
+
// Check _debugSource (primary source of truth)
|
|
74
|
+
if (pointer._debugSource) {
|
|
75
|
+
const src = pointer._debugSource;
|
|
76
|
+
return {
|
|
77
|
+
fileName: src.fileName,
|
|
78
|
+
lineNumber: src.lineNumber,
|
|
79
|
+
columnNumber: src.columnNumber
|
|
80
|
+
};
|
|
243
81
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
82
|
+
// Check owner fiber if available
|
|
83
|
+
if (pointer._owner && pointer._owner._debugSource) {
|
|
84
|
+
const src = pointer._owner._debugSource;
|
|
85
|
+
return {
|
|
86
|
+
fileName: src.fileName,
|
|
87
|
+
lineNumber: src.lineNumber,
|
|
88
|
+
columnNumber: src.columnNumber
|
|
89
|
+
};
|
|
247
90
|
}
|
|
91
|
+
// Check props.__source as fallback (Babel plugin output)
|
|
92
|
+
const props = pointer.memoizedProps || pointer.pendingProps;
|
|
93
|
+
if (props && props.__source) {
|
|
94
|
+
const src = props.__source;
|
|
95
|
+
return {
|
|
96
|
+
fileName: src.fileName,
|
|
97
|
+
lineNumber: src.lineNumber,
|
|
98
|
+
columnNumber: src.columnNumber
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Try both return and _owner for traversal
|
|
102
|
+
pointer = pointer.return || pointer._owner;
|
|
103
|
+
steps++;
|
|
248
104
|
}
|
|
249
|
-
return
|
|
105
|
+
return null;
|
|
250
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Inject source attributes into all DOM elements using React Fiber data
|
|
109
|
+
*/
|
|
251
110
|
function injectSourceLocations() {
|
|
252
|
-
if (typeof window === 'undefined')
|
|
253
|
-
return;
|
|
254
|
-
// Step 1: Try jsx-tool first (primary method)
|
|
255
|
-
const jsxToolCount = injectSourceLocationsViaJsxTool();
|
|
256
|
-
if (jsxToolCount > 0) {
|
|
257
|
-
console.log(`[Keak] ✅ Used jsx-tool for source mapping (${jsxToolCount} elements)`);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
// Step 1.5: If jsx-tool is detected but no injections yet, schedule a retry
|
|
261
|
-
// Check for the WebSocket URL (primary indicator) or other globals
|
|
262
|
-
const jsxToolWsUrl = window.__JSX_TOOL_DEV_SERVER_WS_URL__;
|
|
263
|
-
const hasJsxToolGlobals = !!jsxToolWsUrl || !!window.__JSX_TOOL__ || !!window.__JSX_TOOL_SOURCE_MAP__ || !!window.__JSX_TOOL_WS__;
|
|
264
|
-
const retryCount = (window.__KEAK_JSX_TOOL_RETRY_COUNT__ || 0);
|
|
265
|
-
if (hasJsxToolGlobals && retryCount < 3) {
|
|
266
|
-
window.__KEAK_JSX_TOOL_RETRY_COUNT__ = retryCount + 1;
|
|
267
|
-
console.log(`[Keak] 🔄 jsx-tool detected (WS URL: ${jsxToolWsUrl || 'not set'}) but not ready yet, will retry in 1s (attempt ${retryCount + 1}/3)...`);
|
|
268
|
-
setTimeout(() => {
|
|
269
|
-
injectSourceLocations();
|
|
270
|
-
}, 1000);
|
|
271
|
-
return; // Don't fall back yet, wait for retry
|
|
272
|
-
}
|
|
273
|
-
// Step 2: Fall back to React Fiber
|
|
274
|
-
// Only log if we haven't already tried and failed multiple times
|
|
275
|
-
const attemptCount = (window.__KEAK_INJECTION_ATTEMPTS__ || 0) + 1;
|
|
276
|
-
window.__KEAK_INJECTION_ATTEMPTS__ = attemptCount;
|
|
277
|
-
if (attemptCount <= 3) {
|
|
278
|
-
// Only log on first few attempts to avoid console spam
|
|
279
|
-
if (attemptCount === 1) {
|
|
280
|
-
if (!hasJsxToolGlobals) {
|
|
281
|
-
console.log('[Keak] jsx-tool not detected, trying React Fiber fallback...');
|
|
282
|
-
console.log('[Keak] 💡 To use jsx-tool for better source mapping:');
|
|
283
|
-
console.log('[Keak] 1. Install: npm install @jsx-tool/jsx-tool');
|
|
284
|
-
console.log('[Keak] 2. For Vite: Add jsxToolDevServer() to vite.config.ts plugins');
|
|
285
|
-
console.log('[Keak] 3. For non-Vite: Add script tag with window.__JSX_TOOL_DEV_SERVER_WS_URL__');
|
|
286
|
-
console.log('[Keak] 4. Restart your dev server');
|
|
287
|
-
}
|
|
288
|
-
else {
|
|
289
|
-
console.log('[Keak] jsx-tool detected but no source locations found, falling back to React Fiber...');
|
|
290
|
-
console.log('[Keak] 💡 Make sure jsx-tool dev server is running (check for WebSocket connection)');
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
// Check if React DevTools is available (development mode)
|
|
295
|
-
const reactDevTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
296
|
-
if (!reactDevTools) {
|
|
297
|
-
console.warn('[Keak] React DevTools not detected. Source locations will not be available.');
|
|
298
|
-
console.warn('[Keak] Make sure you are running in development mode.');
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
// Find React Fiber roots
|
|
302
|
-
const reactRoots = [];
|
|
303
|
-
if (reactDevTools.renderers) {
|
|
304
|
-
reactDevTools.renderers.forEach((renderer) => {
|
|
305
|
-
try {
|
|
306
|
-
// Get the fiber root from the renderer
|
|
307
|
-
const fiberRoot = renderer.findFiberByHostInstance || renderer.getFiberRoots;
|
|
308
|
-
if (fiberRoot) {
|
|
309
|
-
reactRoots.push(renderer);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
console.warn('[Keak] Error accessing renderer:', error);
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
if (reactRoots.length === 0) {
|
|
318
|
-
// Logging disabled - using Babel plugin for source injection instead
|
|
319
|
-
// console.warn('[Keak] No React roots found. Trying alternative method...');
|
|
320
|
-
injectSourceLocationsViaFiber();
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
// Logging disabled - using Babel plugin for source injection instead
|
|
324
|
-
// console.log(`[Keak] Found ${reactRoots.length} React renderer(s)`);
|
|
325
|
-
// Process all DOM elements
|
|
111
|
+
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
112
|
+
return 0;
|
|
326
113
|
let injectedCount = 0;
|
|
327
114
|
const allElements = document.querySelectorAll('*');
|
|
328
115
|
allElements.forEach((element) => {
|
|
329
116
|
if (!(element instanceof HTMLElement))
|
|
330
117
|
return;
|
|
331
|
-
// Skip if already has source
|
|
332
|
-
if (element.hasAttribute('data-
|
|
118
|
+
// Skip if already has source attributes
|
|
119
|
+
if (element.hasAttribute('data-source-file'))
|
|
333
120
|
return;
|
|
334
121
|
// Skip Keak's own elements
|
|
335
122
|
if (element.classList.contains('keak-toolbar') ||
|
|
@@ -337,171 +124,68 @@ function injectSourceLocations() {
|
|
|
337
124
|
element.id === 'keak-toolbar-root') {
|
|
338
125
|
return;
|
|
339
126
|
}
|
|
340
|
-
//
|
|
341
|
-
const fiber =
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
// Clean
|
|
345
|
-
let
|
|
346
|
-
//
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
127
|
+
// Get React fiber for this element
|
|
128
|
+
const fiber = getFiberFromNode(element);
|
|
129
|
+
const source = getSourceFromFiber(fiber);
|
|
130
|
+
if (source) {
|
|
131
|
+
// Clean the file path for better display
|
|
132
|
+
let cleanPath = source.fileName;
|
|
133
|
+
// Extract from src/ onwards
|
|
134
|
+
if (cleanPath.includes('/src/')) {
|
|
135
|
+
const parts = cleanPath.split('/src/');
|
|
136
|
+
cleanPath = 'src/' + parts[parts.length - 1];
|
|
137
|
+
}
|
|
138
|
+
else if (cleanPath.includes('/app/')) {
|
|
139
|
+
const parts = cleanPath.split('/app/');
|
|
140
|
+
cleanPath = 'app/' + parts[parts.length - 1];
|
|
141
|
+
}
|
|
142
|
+
else if (cleanPath.includes('/components/')) {
|
|
143
|
+
const parts = cleanPath.split('/components/');
|
|
144
|
+
cleanPath = 'components/' + parts[parts.length - 1];
|
|
145
|
+
}
|
|
146
|
+
// Inject source attributes (same format as react-source-lens)
|
|
147
|
+
element.setAttribute('data-source-file', cleanPath);
|
|
148
|
+
element.setAttribute('data-source-line', String(source.lineNumber));
|
|
149
|
+
// Also create combined data-keak-src for backward compatibility
|
|
150
|
+
element.setAttribute('data-keak-src', `${cleanPath}:${source.lineNumber}:${source.columnNumber || 1}`);
|
|
362
151
|
injectedCount++;
|
|
363
152
|
}
|
|
364
153
|
});
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
console.warn('[Keak] No source locations injected. Make sure you are running in development mode with source maps enabled.');
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
// Find React Fiber node for a DOM element
|
|
371
|
-
function findFiberForElement(element) {
|
|
372
|
-
// Look for React Fiber keys on the element
|
|
373
|
-
const allKeys = Object.keys(element);
|
|
374
|
-
const fiberKey = allKeys.find(key => key.startsWith('__reactFiber') ||
|
|
375
|
-
key.startsWith('__reactInternalInstance') ||
|
|
376
|
-
key.startsWith('__reactProps'));
|
|
377
|
-
if (fiberKey) {
|
|
378
|
-
const fiber = element[fiberKey];
|
|
379
|
-
// Debug disabled - using Babel plugin instead of runtime injection
|
|
380
|
-
// if (!element.hasAttribute('data-keak-debug-logged')) {
|
|
381
|
-
// element.setAttribute('data-keak-debug-logged', 'true');
|
|
382
|
-
// console.log('[Keak Debug] Sample Fiber node:', {
|
|
383
|
-
// hasFiber: !!fiber,
|
|
384
|
-
// hasDebugSource: !!fiber?._debugSource,
|
|
385
|
-
// debugSource: fiber?._debugSource,
|
|
386
|
-
// type: fiber?.type,
|
|
387
|
-
// elementType: fiber?.elementType,
|
|
388
|
-
// keys: allKeys.filter(k => k.startsWith('__react'))
|
|
389
|
-
// });
|
|
390
|
-
// }
|
|
391
|
-
return fiber;
|
|
154
|
+
if (injectedCount > 0) {
|
|
155
|
+
console.log(`[Keak Source] ✅ Injected ${injectedCount} source locations from React Fiber`);
|
|
392
156
|
}
|
|
393
|
-
return
|
|
394
|
-
}
|
|
395
|
-
// Alternative method: Walk the Fiber tree and inject source locations
|
|
396
|
-
function injectSourceLocationsViaFiber() {
|
|
397
|
-
if (typeof window === 'undefined')
|
|
398
|
-
return;
|
|
399
|
-
// Find all elements with React Fiber data
|
|
400
|
-
const allElements = document.querySelectorAll('*');
|
|
401
|
-
allElements.forEach((element) => {
|
|
402
|
-
if (!(element instanceof HTMLElement))
|
|
403
|
-
return;
|
|
404
|
-
// Skip if already has source location
|
|
405
|
-
if (element.hasAttribute('data-keak-src')) {
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
// Skip Keak's own elements
|
|
409
|
-
if (element.classList.contains('keak-toolbar') ||
|
|
410
|
-
element.closest('.keak-toolbar') ||
|
|
411
|
-
element.id === 'keak-toolbar-root' ||
|
|
412
|
-
element.id?.startsWith('keak-')) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
// Find React Fiber
|
|
416
|
-
const fiber = findFiberForElement(element);
|
|
417
|
-
if (!fiber) {
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
if (!fiber._debugSource) {
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
const { fileName, lineNumber, columnNumber } = fiber._debugSource;
|
|
424
|
-
// Skip node_modules BEFORE cleaning
|
|
425
|
-
if (fileName.includes('node_modules')) {
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
// Clean file name
|
|
429
|
-
let cleanFileName = fileName;
|
|
430
|
-
// Remove webpack prefixes
|
|
431
|
-
cleanFileName = cleanFileName.replace(/^webpack:\/\/\/_next\//, '');
|
|
432
|
-
cleanFileName = cleanFileName.replace(/^webpack:\/\/\/\.\//, '');
|
|
433
|
-
cleanFileName = cleanFileName.replace(/^\.\/_next\//, '');
|
|
434
|
-
cleanFileName = cleanFileName.replace(/^webpack-internal:\/\/\//, '');
|
|
435
|
-
cleanFileName = cleanFileName.replace(/^\(app-pages-browser\)\//, '');
|
|
436
|
-
// Make relative to project root - find common patterns
|
|
437
|
-
const patterns = [
|
|
438
|
-
/^.*?\/(app|src|pages|components|lib|utils)\//,
|
|
439
|
-
/^.*\/\.next\/server\/(app|src|pages)\//,
|
|
440
|
-
];
|
|
441
|
-
for (const pattern of patterns) {
|
|
442
|
-
if (pattern.test(cleanFileName)) {
|
|
443
|
-
cleanFileName = cleanFileName.replace(pattern, '$1/');
|
|
444
|
-
break;
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
const sourceLocation = `${cleanFileName}:${lineNumber}:${columnNumber || 1}`;
|
|
448
|
-
element.setAttribute('data-keak-src', sourceLocation);
|
|
449
|
-
// Add component name
|
|
450
|
-
if (fiber.type) {
|
|
451
|
-
const componentName = fiber.type.name || fiber.type.displayName;
|
|
452
|
-
if (componentName && typeof componentName === 'string') {
|
|
453
|
-
element.setAttribute('data-keak-component', componentName);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
});
|
|
457
|
-
// Logging disabled - using Babel plugin for source injection instead
|
|
458
|
-
// console.log(`[Keak] Source injection summary:`);
|
|
459
|
-
// console.log(` ✅ Injected: ${injectedCount} elements`);
|
|
460
|
-
// console.log(` ⏭️ Skipped (no fiber): ${skippedNoFiber}`);
|
|
461
|
-
// console.log(` ⏭️ Skipped (no source): ${skippedNoSource}`);
|
|
462
|
-
// console.log(` ⏭️ Skipped (node_modules): ${skippedNodeModules}`);
|
|
463
|
-
// // If no elements injected, log a warning with tips
|
|
464
|
-
// if (injectedCount === 0) {
|
|
465
|
-
// console.warn('[Keak] ⚠️ No source locations injected!');
|
|
466
|
-
// console.warn('[Keak] Make sure:');
|
|
467
|
-
// console.warn('[Keak] 1. You are running in development mode (npm run dev)');
|
|
468
|
-
// console.warn('[Keak] 2. React DevTools is available');
|
|
469
|
-
// console.warn('[Keak] 3. Next.js is generating source maps (default in dev)');
|
|
470
|
-
// }
|
|
157
|
+
return injectedCount;
|
|
471
158
|
}
|
|
472
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Initialize source injection on DOM ready and watch for changes
|
|
161
|
+
*/
|
|
473
162
|
if (typeof window !== 'undefined') {
|
|
474
|
-
// Wait
|
|
475
|
-
const runInjection = () => {
|
|
476
|
-
// Try multiple times with delays
|
|
477
|
-
setTimeout(() => injectSourceLocations(), 1000);
|
|
478
|
-
setTimeout(() => injectSourceLocations(), 2000);
|
|
479
|
-
setTimeout(() => injectSourceLocations(), 3000);
|
|
480
|
-
// Also watch for DOM changes
|
|
481
|
-
const observer = new MutationObserver(() => {
|
|
482
|
-
// Debounced injection on DOM changes
|
|
483
|
-
clearTimeout(window.__keakInjectionTimeout);
|
|
484
|
-
window.__keakInjectionTimeout = setTimeout(() => {
|
|
485
|
-
injectSourceLocations();
|
|
486
|
-
}, 500);
|
|
487
|
-
});
|
|
488
|
-
observer.observe(document.body, {
|
|
489
|
-
childList: true,
|
|
490
|
-
subtree: true
|
|
491
|
-
});
|
|
492
|
-
};
|
|
163
|
+
// Wait for DOM to be ready
|
|
493
164
|
if (document.readyState === 'loading') {
|
|
494
|
-
document.addEventListener('DOMContentLoaded',
|
|
165
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
166
|
+
// Initial injection with delays to catch dynamically rendered content
|
|
167
|
+
setTimeout(() => injectSourceLocations(), 500);
|
|
168
|
+
setTimeout(() => injectSourceLocations(), 1500);
|
|
169
|
+
setTimeout(() => injectSourceLocations(), 3000);
|
|
170
|
+
});
|
|
495
171
|
}
|
|
496
172
|
else {
|
|
497
|
-
|
|
173
|
+
// DOM already ready
|
|
174
|
+
setTimeout(() => injectSourceLocations(), 500);
|
|
175
|
+
setTimeout(() => injectSourceLocations(), 1500);
|
|
176
|
+
setTimeout(() => injectSourceLocations(), 3000);
|
|
498
177
|
}
|
|
499
|
-
//
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
178
|
+
// Watch for DOM changes and re-inject
|
|
179
|
+
const observer = new MutationObserver(() => {
|
|
180
|
+
injectSourceLocations();
|
|
181
|
+
});
|
|
182
|
+
// Start observing after a brief delay
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
observer.observe(document.body, {
|
|
185
|
+
childList: true,
|
|
186
|
+
subtree: true
|
|
503
187
|
});
|
|
504
|
-
}
|
|
188
|
+
}, 1000);
|
|
505
189
|
}
|
|
506
190
|
|
|
507
191
|
/**
|
|
@@ -1830,14 +1514,33 @@ const KeakToolbar = ({ position = 'bottom-right', theme = 'auto' }) => {
|
|
|
1830
1514
|
sourceMappingTelemetry.fiberSuccess++;
|
|
1831
1515
|
}
|
|
1832
1516
|
else {
|
|
1833
|
-
// Fallback to data
|
|
1834
|
-
|
|
1517
|
+
// Fallback to various data-* attributes from different source mapping tools
|
|
1518
|
+
// Priority: data-keak-src > react-source-lens > webpack loader
|
|
1519
|
+
let keakSrc = element.getAttribute('data-keak-src');
|
|
1835
1520
|
const keakComponent = element.getAttribute('data-keak-component');
|
|
1836
1521
|
const keakIdx = element.getAttribute('data-keak-idx');
|
|
1837
1522
|
console.log('[Keak Toolbar] No Fiber source, checking data-keak-* attributes:');
|
|
1838
1523
|
console.log('[Keak Toolbar] data-keak-src:', keakSrc);
|
|
1839
1524
|
console.log('[Keak Toolbar] data-keak-component:', keakComponent);
|
|
1840
1525
|
console.log('[Keak Toolbar] data-keak-idx:', keakIdx);
|
|
1526
|
+
// Fallback 1: Check for react-source-lens attributes if data-keak-src not found
|
|
1527
|
+
if (!keakSrc) {
|
|
1528
|
+
const sourceLensFile = element.getAttribute('data-source-file');
|
|
1529
|
+
const sourceLensLine = element.getAttribute('data-source-line');
|
|
1530
|
+
if (sourceLensFile && sourceLensLine) {
|
|
1531
|
+
keakSrc = `${sourceLensFile}:${sourceLensLine}:1`;
|
|
1532
|
+
console.log('[Keak Toolbar] react-source-lens detected:', keakSrc);
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
// Fallback 2: Check for webpack loader attributes if still not found
|
|
1536
|
+
if (!keakSrc) {
|
|
1537
|
+
const keakFile = element.getAttribute('data-keak-file');
|
|
1538
|
+
const keakLine = element.getAttribute('data-keak-line');
|
|
1539
|
+
if (keakFile && keakLine) {
|
|
1540
|
+
keakSrc = `${keakFile}:${keakLine}:1`;
|
|
1541
|
+
console.log('[Keak Toolbar] webpack loader attributes detected:', keakSrc);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1841
1544
|
if (keakSrc) {
|
|
1842
1545
|
// Format is "file:line:column"
|
|
1843
1546
|
const parts = keakSrc.split(':');
|
|
@@ -1846,7 +1549,7 @@ const KeakToolbar = ({ position = 'bottom-right', theme = 'auto' }) => {
|
|
|
1846
1549
|
const columnNumber = parts[parts.length - 1];
|
|
1847
1550
|
const parsedLineNumber = parseInt(lineNumber, 10);
|
|
1848
1551
|
const parsedColumnNumber = parseInt(columnNumber, 10);
|
|
1849
|
-
console.log('[Keak Toolbar] ✅ Using
|
|
1552
|
+
console.log('[Keak Toolbar] ✅ Using build-time source attributes:', { fileName, lineNumber: parsedLineNumber, columnNumber: parsedColumnNumber });
|
|
1850
1553
|
elementData.sourceMapping = {
|
|
1851
1554
|
method: 'babel-plugin',
|
|
1852
1555
|
fileName: fileName,
|
|
@@ -1860,7 +1563,7 @@ const KeakToolbar = ({ position = 'bottom-right', theme = 'auto' }) => {
|
|
|
1860
1563
|
sourceMappingTelemetry.babelUsed++;
|
|
1861
1564
|
}
|
|
1862
1565
|
else {
|
|
1863
|
-
console.log('[Keak Toolbar] ❌ No source mapping found - no data-keak-src
|
|
1566
|
+
console.log('[Keak Toolbar] ❌ No source mapping found - no data-keak-src, react-source-lens, or webpack loader attributes in DOM');
|
|
1864
1567
|
// Track complete failure (Fiber failed, no Babel either)
|
|
1865
1568
|
sourceMappingTelemetry.fiberFailed++;
|
|
1866
1569
|
sourceMappingTelemetry.noMapping++;
|
|
@@ -3008,7 +2711,6 @@ class KeakSDK {
|
|
|
3008
2711
|
}
|
|
3009
2712
|
/**
|
|
3010
2713
|
* Main Keak hook - initializes the SDK with minimal configuration
|
|
3011
|
-
* Similar to react-source-lens's useReactSourceLens() hook
|
|
3012
2714
|
*
|
|
3013
2715
|
* @example
|
|
3014
2716
|
* // Minimal usage (no config needed)
|