react-code-locator 0.1.9 → 0.1.13

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 (52) hide show
  1. package/README.md +49 -44
  2. package/dist/babel.cjs +428 -40
  3. package/dist/babel.cjs.map +1 -1
  4. package/dist/babel.d.cts +12 -1
  5. package/dist/babel.d.ts +12 -1
  6. package/dist/babel.js +425 -29
  7. package/dist/babel.js.map +1 -1
  8. package/dist/babelInjectComponentSource.cjs +400 -38
  9. package/dist/babelInjectComponentSource.cjs.map +1 -1
  10. package/dist/babelInjectComponentSource.d.cts +3 -4
  11. package/dist/babelInjectComponentSource.d.ts +3 -4
  12. package/dist/babelInjectComponentSource.js +400 -28
  13. package/dist/babelInjectComponentSource.js.map +1 -1
  14. package/dist/client-sm5wi0uT.d.cts +15 -0
  15. package/dist/client-sm5wi0uT.d.ts +15 -0
  16. package/dist/client.cjs +157 -28
  17. package/dist/client.cjs.map +1 -1
  18. package/dist/client.d.cts +1 -14
  19. package/dist/client.d.ts +1 -14
  20. package/dist/client.js +157 -28
  21. package/dist/client.js.map +1 -1
  22. package/dist/esbuild.cjs +616 -0
  23. package/dist/esbuild.cjs.map +1 -0
  24. package/dist/esbuild.d.cts +25 -0
  25. package/dist/esbuild.d.ts +25 -0
  26. package/dist/esbuild.js +589 -0
  27. package/dist/esbuild.js.map +1 -0
  28. package/dist/index.cjs +843 -30
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.cts +9 -1
  31. package/dist/index.d.ts +9 -1
  32. package/dist/index.js +830 -29
  33. package/dist/index.js.map +1 -1
  34. package/dist/sourceAdapter-DLWo_ABo.d.cts +15 -0
  35. package/dist/sourceAdapter-DLWo_ABo.d.ts +15 -0
  36. package/dist/swc.cjs +589 -0
  37. package/dist/swc.cjs.map +1 -0
  38. package/dist/swc.d.cts +29 -0
  39. package/dist/swc.d.ts +29 -0
  40. package/dist/swc.js +560 -0
  41. package/dist/swc.js.map +1 -0
  42. package/dist/vite.cjs +529 -84
  43. package/dist/vite.cjs.map +1 -1
  44. package/dist/vite.d.cts +20 -6
  45. package/dist/vite.d.ts +20 -6
  46. package/dist/vite.js +524 -72
  47. package/dist/vite.js.map +1 -1
  48. package/dist/webpackRuntimeEntry.cjs +157 -28
  49. package/dist/webpackRuntimeEntry.cjs.map +1 -1
  50. package/dist/webpackRuntimeEntry.js +157 -28
  51. package/dist/webpackRuntimeEntry.js.map +1 -1
  52. package/package.json +12 -1
package/dist/client.cjs CHANGED
@@ -27,6 +27,63 @@ module.exports = __toCommonJS(client_exports);
27
27
 
28
28
  // src/constants.ts
29
29
  var SOURCE_PROP = "__componentSourceLoc";
30
+ var JSX_SOURCE_PROP = "$componentSourceLoc";
31
+ var JSX_SOURCE_REGISTRY_SYMBOL = "react-code-locator.jsxSourceRegistry";
32
+
33
+ // src/sourceMetadata.ts
34
+ function normalizeSlashes(value) {
35
+ return value.replace(/\\/g, "/");
36
+ }
37
+ function trimTrailingSlash(value) {
38
+ return value.replace(/\/+$/, "");
39
+ }
40
+ function splitPathSegments(value) {
41
+ return normalizeSlashes(value).split("/").filter(Boolean);
42
+ }
43
+ function computeRelativePath(fromPath, toPath) {
44
+ const fromSegments = splitPathSegments(fromPath);
45
+ const toSegments = splitPathSegments(toPath);
46
+ let sharedIndex = 0;
47
+ while (sharedIndex < fromSegments.length && sharedIndex < toSegments.length && fromSegments[sharedIndex] === toSegments[sharedIndex]) {
48
+ sharedIndex += 1;
49
+ }
50
+ const upSegments = new Array(Math.max(0, fromSegments.length - sharedIndex)).fill("..");
51
+ const downSegments = toSegments.slice(sharedIndex);
52
+ const relativeSegments = [...upSegments, ...downSegments];
53
+ return relativeSegments.length > 0 ? relativeSegments.join("/") : ".";
54
+ }
55
+ function normalizeProjectRoot(projectRoot) {
56
+ if (projectRoot) {
57
+ return trimTrailingSlash(normalizeSlashes(projectRoot));
58
+ }
59
+ return "";
60
+ }
61
+ function getSourceFile(source) {
62
+ if (!source) {
63
+ return null;
64
+ }
65
+ const match = source.match(/^(.*):\d+:\d+$/);
66
+ return match?.[1] ?? null;
67
+ }
68
+ function isProjectLocalFile(filename, projectRoot) {
69
+ if (!filename) {
70
+ return false;
71
+ }
72
+ const root = normalizeProjectRoot(projectRoot);
73
+ const normalizedFilename = normalizeSlashes(filename);
74
+ if (!root) {
75
+ return !normalizedFilename.startsWith("../") && !normalizedFilename.startsWith("/") && !/^[A-Za-z]:\//.test(normalizedFilename);
76
+ }
77
+ if (normalizedFilename.startsWith(`${root}/`) || normalizedFilename === root) {
78
+ return true;
79
+ }
80
+ const relativePath = computeRelativePath(root, normalizedFilename);
81
+ return !relativePath.startsWith("../");
82
+ }
83
+ function isProjectLocalSource(source, projectRoot) {
84
+ const file = getSourceFile(source);
85
+ return isProjectLocalFile(file ?? void 0, projectRoot);
86
+ }
30
87
 
31
88
  // src/runtime.ts
32
89
  function isTriggerPressed(event, triggerKey) {
@@ -74,7 +131,16 @@ function getSourceFromType(type) {
74
131
  return typeof source === "string" ? source : null;
75
132
  }
76
133
  function getSourceFromProps(props) {
77
- const source = props?.[SOURCE_PROP];
134
+ if (props && typeof props === "object") {
135
+ const registry = globalThis[Symbol.for(JSX_SOURCE_REGISTRY_SYMBOL)];
136
+ if (registry instanceof WeakMap) {
137
+ const intrinsicSource = registry.get(props);
138
+ if (typeof intrinsicSource === "string") {
139
+ return intrinsicSource;
140
+ }
141
+ }
142
+ }
143
+ const source = props?.[JSX_SOURCE_PROP];
78
144
  return typeof source === "string" ? source : null;
79
145
  }
80
146
  function resolveComponentSourceFromFiber(fiber) {
@@ -95,35 +161,69 @@ function getDirectDebugSource(fiber) {
95
161
  }
96
162
  return null;
97
163
  }
98
- function resolveOwnerJsxSource(fiber) {
99
- let current = fiber?._debugOwner ?? null;
100
- while (current) {
101
- const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
102
- if (source) {
103
- return source;
104
- }
105
- current = current._debugOwner ?? null;
106
- }
107
- return null;
108
- }
109
- function resolveNearestJsxSource(fiber) {
164
+ function resolveSourceCandidates(fiber) {
110
165
  let current = fiber;
166
+ const jsxCandidates = [];
167
+ const componentCandidates = [];
111
168
  while (current) {
112
- const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
113
- if (source) {
114
- return source;
169
+ const jsxSource = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
170
+ if (jsxSource) {
171
+ const file = getSourceFile(jsxSource);
172
+ if (file && !jsxCandidates.some((candidate) => candidate.source === jsxSource)) {
173
+ jsxCandidates.push({ source: jsxSource, file });
174
+ }
175
+ }
176
+ const componentSource = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);
177
+ if (componentSource) {
178
+ const file = getSourceFile(componentSource);
179
+ if (file && !componentCandidates.some((candidate) => candidate.source === componentSource)) {
180
+ componentCandidates.push({ source: componentSource, file });
181
+ }
115
182
  }
116
183
  current = current.return ?? null;
117
184
  }
118
- return null;
185
+ const direct = jsxCandidates[0]?.source ?? null;
186
+ const nearestProjectLocalComponentFile = componentCandidates.find((candidate) => isProjectLocalSource(candidate.source))?.file;
187
+ let screen = null;
188
+ if (nearestProjectLocalComponentFile) {
189
+ const matchingJsxCandidate = jsxCandidates.find((candidate) => candidate.file === nearestProjectLocalComponentFile);
190
+ if (matchingJsxCandidate) {
191
+ screen = matchingJsxCandidate.source;
192
+ } else {
193
+ const matchingComponentCandidate = componentCandidates.find(
194
+ (candidate) => candidate.file === nearestProjectLocalComponentFile
195
+ );
196
+ if (matchingComponentCandidate) {
197
+ screen = matchingComponentCandidate.source;
198
+ }
199
+ }
200
+ }
201
+ const implementationComponentCandidate = componentCandidates.find((candidate) => !isProjectLocalSource(candidate.source))?.source ?? null;
202
+ const implementationJsxCandidate = jsxCandidates.find((candidate) => !isProjectLocalSource(candidate.source))?.source ?? null;
203
+ const projectLocalJsxCandidate = jsxCandidates.find((candidate) => isProjectLocalSource(candidate.source))?.source ?? null;
204
+ const screenFallback = screen ?? projectLocalJsxCandidate ?? componentCandidates.find((candidate) => isProjectLocalSource(candidate.source))?.source ?? null;
205
+ return {
206
+ direct: direct ?? screenFallback,
207
+ screen: screenFallback,
208
+ implementation: implementationComponentCandidate ?? implementationJsxCandidate ?? screenFallback
209
+ };
210
+ }
211
+ function getModeDescription(mode) {
212
+ if (mode === "direct") {
213
+ return "Direct JSX";
214
+ }
215
+ if (mode === "screen") {
216
+ return "Screen source";
217
+ }
218
+ return "Implementation source";
119
219
  }
120
220
  function createStatusOverlay(triggerKey) {
121
221
  if (typeof document === "undefined") {
122
222
  return null;
123
223
  }
124
224
  const element = document.createElement("div");
125
- let currentText = "";
126
225
  let copyValue = null;
226
+ let currentMode = "screen";
127
227
  let hideTimer = null;
128
228
  element.setAttribute("data-react-code-locator", "true");
129
229
  Object.assign(element.style, {
@@ -147,7 +247,6 @@ function createStatusOverlay(triggerKey) {
147
247
  transition: "opacity 120ms ease"
148
248
  });
149
249
  const show = (message, tone) => {
150
- currentText = message;
151
250
  element.textContent = message;
152
251
  element.style.background = tone === "success" ? "rgba(6, 95, 70, 0.92)" : tone === "error" ? "rgba(153, 27, 27, 0.94)" : "rgba(17, 24, 39, 0.92)";
153
252
  element.style.opacity = "1";
@@ -158,7 +257,7 @@ function createStatusOverlay(triggerKey) {
158
257
  hideTimer = setTimeout(() => {
159
258
  element.style.opacity = "0";
160
259
  element.style.pointerEvents = "none";
161
- }, 1500);
260
+ }, 2e3);
162
261
  };
163
262
  element.addEventListener("click", async () => {
164
263
  if (!copyValue) {
@@ -171,7 +270,7 @@ function createStatusOverlay(triggerKey) {
171
270
  show(`[react-code-locator] copy failed`, "error");
172
271
  }
173
272
  });
174
- show(`[react-code-locator] enabled (${triggerKey}+click)`, "idle");
273
+ show(`[react-code-locator] enabled (${triggerKey}+click, alt+1/2/3 to switch mode)`, "idle");
175
274
  const mount = () => {
176
275
  if (!element.isConnected && document.body) {
177
276
  document.body.appendChild(element);
@@ -189,6 +288,10 @@ function createStatusOverlay(triggerKey) {
189
288
  setCopyValue(value) {
190
289
  copyValue = value;
191
290
  },
291
+ setMode(mode) {
292
+ currentMode = mode;
293
+ show(`[react-code-locator] ${getModeDescription(mode)}`, "idle");
294
+ },
192
295
  remove() {
193
296
  if (hideTimer) {
194
297
  clearTimeout(hideTimer);
@@ -197,17 +300,18 @@ function createStatusOverlay(triggerKey) {
197
300
  }
198
301
  };
199
302
  }
200
- function locateComponentSource(target) {
303
+ function locateComponentSource(target, mode = "screen") {
201
304
  const elementTarget = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
202
305
  const fiber = getClosestReactFiber(elementTarget);
203
306
  if (!fiber) {
204
307
  return null;
205
308
  }
206
- const jsxSource = resolveOwnerJsxSource(fiber) ?? resolveNearestJsxSource(fiber);
207
- if (jsxSource) {
309
+ const candidates = resolveSourceCandidates(fiber);
310
+ const source = candidates[mode] ?? candidates.screen ?? candidates.direct ?? candidates.implementation;
311
+ if (source) {
208
312
  return {
209
- source: jsxSource,
210
- mode: "jsx"
313
+ source,
314
+ mode
211
315
  };
212
316
  }
213
317
  const componentSource = resolveComponentSourceFromFiber(fiber);
@@ -216,11 +320,12 @@ function locateComponentSource(target) {
216
320
  }
217
321
  return {
218
322
  source: componentSource,
219
- mode: "component"
323
+ mode
220
324
  };
221
325
  }
222
326
  function enableReactComponentJump(options = {}) {
223
327
  const overlay = createStatusOverlay(options.triggerKey ?? "shift");
328
+ let currentMode = "screen";
224
329
  const {
225
330
  triggerKey = "shift",
226
331
  onLocate = (result) => {
@@ -236,6 +341,28 @@ function enableReactComponentJump(options = {}) {
236
341
  }
237
342
  } = options;
238
343
  console.log("[react-code-locator] enabled", { triggerKey });
344
+ const keyHandler = (event) => {
345
+ if (!event.altKey) {
346
+ return;
347
+ }
348
+ if (event.code === "Digit1") {
349
+ currentMode = "direct";
350
+ overlay?.setMode(currentMode);
351
+ event.preventDefault();
352
+ return;
353
+ }
354
+ if (event.code === "Digit2") {
355
+ currentMode = "screen";
356
+ overlay?.setMode(currentMode);
357
+ event.preventDefault();
358
+ return;
359
+ }
360
+ if (event.code === "Digit3") {
361
+ currentMode = "implementation";
362
+ overlay?.setMode(currentMode);
363
+ event.preventDefault();
364
+ }
365
+ };
239
366
  const handler = (event) => {
240
367
  console.log("[react-code-locator] click", {
241
368
  triggerKey,
@@ -248,7 +375,7 @@ function enableReactComponentJump(options = {}) {
248
375
  if (!isTriggerPressed(event, triggerKey)) {
249
376
  return;
250
377
  }
251
- const result = locateComponentSource(event.target);
378
+ const result = locateComponentSource(event.target, currentMode);
252
379
  if (!result) {
253
380
  onError(new Error("No React component source metadata found for clicked element."));
254
381
  return;
@@ -258,8 +385,10 @@ function enableReactComponentJump(options = {}) {
258
385
  onLocate(result);
259
386
  };
260
387
  document.addEventListener("click", handler, true);
388
+ document.addEventListener("keydown", keyHandler, true);
261
389
  return () => {
262
390
  document.removeEventListener("click", handler, true);
391
+ document.removeEventListener("keydown", keyHandler, true);
263
392
  overlay?.remove();
264
393
  };
265
394
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/constants.ts","../src/runtime.ts"],"sourcesContent":["export { enableReactComponentJump, locateComponentSource } from \"./runtime\";\nexport type { LocatorOptions, LocatorResult, TriggerKey } from \"./runtime\";\n\n","export const SOURCE_PROP = \"__componentSourceLoc\";\n\n","import { SOURCE_PROP } from \"./constants\";\n\nexport type TriggerKey = \"alt\" | \"meta\" | \"ctrl\" | \"shift\" | \"none\";\n\ntype ReactFiber = {\n return?: ReactFiber | null;\n type?: unknown;\n elementType?: unknown;\n pendingProps?: Record<string, unknown> | null;\n memoizedProps?: Record<string, unknown> | null;\n _debugOwner?: ReactFiber | null;\n _debugSource?: {\n fileName?: string;\n lineNumber?: number;\n columnNumber?: number;\n } | null;\n};\n\nexport type LocatorResult = {\n source: string;\n mode: \"jsx\" | \"component\";\n};\n\nexport type LocatorOptions = {\n triggerKey?: TriggerKey;\n onLocate?: (result: LocatorResult) => void;\n onError?: (error: unknown) => void;\n};\n\ntype StatusOverlay = {\n setStatus: (message: string, tone?: \"idle\" | \"success\" | \"error\") => void;\n setCopyValue: (value: string | null) => void;\n remove: () => void;\n};\n\nfunction isTriggerPressed(event: MouseEvent, triggerKey: TriggerKey) {\n if (triggerKey === \"none\") {\n return true;\n }\n\n if (triggerKey === \"alt\") {\n return event.altKey;\n }\n\n if (triggerKey === \"meta\") {\n return event.metaKey;\n }\n\n if (triggerKey === \"ctrl\") {\n return event.ctrlKey;\n }\n\n return event.shiftKey;\n}\n\nfunction getReactFiberKey(element: Element) {\n return Object.keys(element).find((key) => key.startsWith(\"__reactFiber$\") || key.startsWith(\"__reactInternalInstance$\"));\n}\n\nfunction getClosestReactFiber(target: Element | null) {\n let current = target;\n\n while (current) {\n const fiberKey = getReactFiberKey(current);\n if (fiberKey) {\n return (current as unknown as Record<string, unknown>)[fiberKey] as ReactFiber;\n }\n\n current = current.parentElement;\n }\n\n return null;\n}\n\nfunction getSourceFromType(type: unknown) {\n if (!type) {\n return null;\n }\n\n if (typeof type === \"function\") {\n const source = (type as unknown as Record<string, unknown>)[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n }\n\n if (typeof type !== \"object\") {\n return null;\n }\n\n const record = type as {\n type?: Record<string, unknown>;\n render?: Record<string, unknown>;\n [SOURCE_PROP]?: unknown;\n };\n\n const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction getSourceFromProps(props: Record<string, unknown> | null | undefined) {\n const source = props?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction resolveJsxSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction resolveComponentSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction getDirectDebugSource(fiber: ReactFiber | null) {\n const debugSource = fiber?._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n return null;\n}\n\nfunction resolveOwnerJsxSource(fiber: ReactFiber | null) {\n let current = fiber?._debugOwner ?? null;\n\n while (current) {\n const source =\n getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);\n if (source) {\n return source;\n }\n\n current = current._debugOwner ?? null;\n }\n\n return null;\n}\n\nfunction resolveNearestJsxSource(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source =\n getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction createStatusOverlay(triggerKey: TriggerKey): StatusOverlay | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n\n const element = document.createElement(\"div\");\n let currentText = \"\";\n let copyValue: string | null = null;\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n element.setAttribute(\"data-react-code-locator\", \"true\");\n Object.assign(element.style, {\n position: \"fixed\",\n right: \"12px\",\n bottom: \"12px\",\n zIndex: \"2147483647\",\n padding: \"8px 10px\",\n borderRadius: \"8px\",\n background: \"rgba(17, 24, 39, 0.92)\",\n color: \"#fff\",\n fontSize: \"12px\",\n lineHeight: \"1.4\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n boxShadow: \"0 8px 30px rgba(0, 0, 0, 0.25)\",\n pointerEvents: \"auto\",\n cursor: \"pointer\",\n maxWidth: \"min(70vw, 720px)\",\n wordBreak: \"break-all\",\n opacity: \"0\",\n transition: \"opacity 120ms ease\",\n });\n\n const show = (message: string, tone: \"idle\" | \"success\" | \"error\") => {\n currentText = message;\n element.textContent = message;\n element.style.background =\n tone === \"success\"\n ? \"rgba(6, 95, 70, 0.92)\"\n : tone === \"error\"\n ? \"rgba(153, 27, 27, 0.94)\"\n : \"rgba(17, 24, 39, 0.92)\";\n element.style.opacity = \"1\";\n element.style.pointerEvents = \"auto\";\n\n if (hideTimer) {\n clearTimeout(hideTimer);\n }\n\n hideTimer = setTimeout(() => {\n element.style.opacity = \"0\";\n element.style.pointerEvents = \"none\";\n }, 1500);\n };\n\n element.addEventListener(\"click\", async () => {\n if (!copyValue) {\n return;\n }\n\n try {\n await navigator.clipboard.writeText(copyValue);\n show(`[react-code-locator] copied`, \"success\");\n } catch {\n show(`[react-code-locator] copy failed`, \"error\");\n }\n });\n\n show(`[react-code-locator] enabled (${triggerKey}+click)`, \"idle\");\n\n const mount = () => {\n if (!element.isConnected && document.body) {\n document.body.appendChild(element);\n }\n };\n\n if (document.body) {\n mount();\n } else {\n document.addEventListener(\"DOMContentLoaded\", mount, { once: true });\n }\n\n return {\n setStatus(message, tone = \"idle\") {\n show(message, tone);\n },\n setCopyValue(value) {\n copyValue = value;\n },\n remove() {\n if (hideTimer) {\n clearTimeout(hideTimer);\n }\n element.remove();\n },\n };\n}\n\nexport function locateComponentSource(target: EventTarget | null): LocatorResult | null {\n const elementTarget =\n target instanceof Element ? target : target instanceof Node ? target.parentElement : null;\n const fiber = getClosestReactFiber(elementTarget);\n if (!fiber) {\n return null;\n }\n\n const jsxSource = resolveOwnerJsxSource(fiber) ?? resolveNearestJsxSource(fiber);\n if (jsxSource) {\n return {\n source: jsxSource,\n mode: \"jsx\",\n };\n }\n\n const componentSource = resolveComponentSourceFromFiber(fiber);\n if (!componentSource) {\n return null;\n }\n\n return {\n source: componentSource,\n mode: \"component\",\n };\n}\n\nexport function enableReactComponentJump(options: LocatorOptions = {}) {\n const overlay = createStatusOverlay(options.triggerKey ?? \"shift\");\n const {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n overlay?.setCopyValue(result.source);\n overlay?.setStatus(`[react-code-locator] ${result.source}`, \"success\");\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n const message = error instanceof Error ? error.message : String(error);\n overlay?.setCopyValue(null);\n overlay?.setStatus(`[react-code-locator] ${message}`, \"error\");\n },\n } = options;\n\n console.log(\"[react-code-locator] enabled\", { triggerKey });\n\n const handler = (event: MouseEvent) => {\n console.log(\"[react-code-locator] click\", {\n triggerKey,\n shiftKey: event.shiftKey,\n altKey: event.altKey,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n target: event.target,\n });\n\n if (!isTriggerPressed(event, triggerKey)) {\n return;\n }\n\n const result = locateComponentSource(event.target);\n if (!result) {\n onError(new Error(\"No React component source metadata found for clicked element.\"));\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n onLocate(result);\n };\n\n document.addEventListener(\"click\", handler, true);\n\n return () => {\n document.removeEventListener(\"click\", handler, true);\n overlay?.remove();\n };\n}\n"],"mappings":"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","names":["source"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/constants.ts","../src/sourceMetadata.ts","../src/runtime.ts"],"sourcesContent":["export { enableReactComponentJump, locateComponentSource } from \"./runtime\";\nexport type { LocatorOptions, LocatorResult, TriggerKey } from \"./runtime\";\n\n","export const SOURCE_PROP = \"__componentSourceLoc\";\nexport const JSX_SOURCE_PROP = \"$componentSourceLoc\";\nexport const JSX_SOURCE_REGISTRY_SYMBOL = \"react-code-locator.jsxSourceRegistry\";\n","export type SourceLocation = {\n line: number;\n column: number;\n};\n\nfunction normalizeSlashes(value: string) {\n return value.replace(/\\\\/g, \"/\");\n}\n\nfunction trimTrailingSlash(value: string) {\n return value.replace(/\\/+$/, \"\");\n}\n\nfunction splitPathSegments(value: string) {\n return normalizeSlashes(value).split(\"/\").filter(Boolean);\n}\n\nfunction computeRelativePath(fromPath: string, toPath: string) {\n const fromSegments = splitPathSegments(fromPath);\n const toSegments = splitPathSegments(toPath);\n\n let sharedIndex = 0;\n while (\n sharedIndex < fromSegments.length &&\n sharedIndex < toSegments.length &&\n fromSegments[sharedIndex] === toSegments[sharedIndex]\n ) {\n sharedIndex += 1;\n }\n\n const upSegments = new Array(Math.max(0, fromSegments.length - sharedIndex)).fill(\"..\");\n const downSegments = toSegments.slice(sharedIndex);\n const relativeSegments = [...upSegments, ...downSegments];\n return relativeSegments.length > 0 ? relativeSegments.join(\"/\") : \".\";\n}\n\nexport function normalizeProjectRoot(projectRoot?: string) {\n if (projectRoot) {\n return trimTrailingSlash(normalizeSlashes(projectRoot));\n }\n return \"\";\n}\n\nexport function toRelativeSource(\n filename: string | undefined,\n loc: SourceLocation | null | undefined,\n projectRoot?: string,\n) {\n if (!filename || !loc) {\n return null;\n }\n\n const root = normalizeProjectRoot(projectRoot);\n const normalizedFilename = normalizeSlashes(filename);\n const relPath =\n root && normalizedFilename.startsWith(`${root}/`)\n ? normalizedFilename.slice(root.length + 1)\n : root\n ? computeRelativePath(root, normalizedFilename)\n : normalizedFilename;\n return `${relPath}:${loc.line}:${loc.column + 1}`;\n}\n\nexport function getSourceFile(source: string | null) {\n if (!source) {\n return null;\n }\n\n const match = source.match(/^(.*):\\d+:\\d+$/);\n return match?.[1] ?? null;\n}\n\nexport function isProjectLocalFile(filename: string | undefined, projectRoot?: string) {\n if (!filename) {\n return false;\n }\n\n const root = normalizeProjectRoot(projectRoot);\n const normalizedFilename = normalizeSlashes(filename);\n\n if (!root) {\n return (\n !normalizedFilename.startsWith(\"../\") &&\n !normalizedFilename.startsWith(\"/\") &&\n !/^[A-Za-z]:\\//.test(normalizedFilename)\n );\n }\n\n if (normalizedFilename.startsWith(`${root}/`) || normalizedFilename === root) {\n return true;\n }\n\n const relativePath = computeRelativePath(root, normalizedFilename);\n return !relativePath.startsWith(\"../\");\n}\n\nexport function isExternalToProjectRoot(filename: string | undefined, projectRoot?: string) {\n return !isProjectLocalFile(filename, projectRoot);\n}\n\nexport function isProjectLocalSource(source: string, projectRoot?: string) {\n const file = getSourceFile(source);\n return isProjectLocalFile(file ?? undefined, projectRoot);\n}\n","import {\n JSX_SOURCE_PROP,\n JSX_SOURCE_REGISTRY_SYMBOL,\n SOURCE_PROP,\n} from \"./constants\";\nimport { getSourceFile, isProjectLocalSource } from \"./sourceMetadata\";\n\nexport type TriggerKey = \"alt\" | \"meta\" | \"ctrl\" | \"shift\" | \"none\";\nexport type LocatorMode = \"direct\" | \"screen\" | \"implementation\";\n\ntype ReactFiber = {\n return?: ReactFiber | null;\n type?: unknown;\n elementType?: unknown;\n pendingProps?: Record<string, unknown> | null;\n memoizedProps?: Record<string, unknown> | null;\n _debugOwner?: ReactFiber | null;\n _debugSource?: {\n fileName?: string;\n lineNumber?: number;\n columnNumber?: number;\n } | null;\n};\n\nexport type LocatorResult = {\n source: string;\n mode: LocatorMode;\n};\n\nexport type LocatorOptions = {\n triggerKey?: TriggerKey;\n onLocate?: (result: LocatorResult) => void;\n onError?: (error: unknown) => void;\n};\n\ntype StatusOverlay = {\n setStatus: (message: string, tone?: \"idle\" | \"success\" | \"error\") => void;\n setCopyValue: (value: string | null) => void;\n setMode: (mode: LocatorMode) => void;\n remove: () => void;\n};\n\nfunction isTriggerPressed(event: MouseEvent, triggerKey: TriggerKey) {\n if (triggerKey === \"none\") {\n return true;\n }\n\n if (triggerKey === \"alt\") {\n return event.altKey;\n }\n\n if (triggerKey === \"meta\") {\n return event.metaKey;\n }\n\n if (triggerKey === \"ctrl\") {\n return event.ctrlKey;\n }\n\n return event.shiftKey;\n}\n\nfunction getReactFiberKey(element: Element) {\n return Object.keys(element).find((key) => key.startsWith(\"__reactFiber$\") || key.startsWith(\"__reactInternalInstance$\"));\n}\n\nfunction getClosestReactFiber(target: Element | null) {\n let current = target;\n\n while (current) {\n const fiberKey = getReactFiberKey(current);\n if (fiberKey) {\n return (current as unknown as Record<string, unknown>)[fiberKey] as ReactFiber;\n }\n\n current = current.parentElement;\n }\n\n return null;\n}\n\nfunction getSourceFromType(type: unknown) {\n if (!type) {\n return null;\n }\n\n if (typeof type === \"function\") {\n const source = (type as unknown as Record<string, unknown>)[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n }\n\n if (typeof type !== \"object\") {\n return null;\n }\n\n const record = type as {\n type?: Record<string, unknown>;\n render?: Record<string, unknown>;\n [SOURCE_PROP]?: unknown;\n };\n\n const source = record[SOURCE_PROP] ?? record.type?.[SOURCE_PROP] ?? record.render?.[SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction getSourceFromProps(props: Record<string, unknown> | null | undefined) {\n if (props && typeof props === \"object\") {\n const registry = (globalThis as Record<symbol, unknown>)[\n Symbol.for(JSX_SOURCE_REGISTRY_SYMBOL)\n ];\n if (registry instanceof WeakMap) {\n const intrinsicSource = registry.get(props as object);\n if (typeof intrinsicSource === \"string\") {\n return intrinsicSource;\n }\n }\n }\n\n const source = props?.[JSX_SOURCE_PROP];\n return typeof source === \"string\" ? source : null;\n}\n\nfunction resolveComponentSourceFromFiber(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);\n if (source) {\n return source;\n }\n\n current = current.return ?? null;\n }\n\n return null;\n}\n\nfunction getDirectDebugSource(fiber: ReactFiber | null) {\n const debugSource = fiber?._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n return null;\n}\n\ntype SourceCandidate = {\n source: string;\n file: string;\n};\n\ntype ResolvedCandidates = {\n direct: string | null;\n screen: string | null;\n implementation: string | null;\n};\n\nfunction resolveSourceCandidates(fiber: ReactFiber | null): ResolvedCandidates {\n let current = fiber;\n const jsxCandidates: SourceCandidate[] = [];\n const componentCandidates: SourceCandidate[] = [];\n\n while (current) {\n const jsxSource =\n getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);\n if (jsxSource) {\n const file = getSourceFile(jsxSource);\n if (file && !jsxCandidates.some((candidate) => candidate.source === jsxSource)) {\n jsxCandidates.push({ source: jsxSource, file });\n }\n }\n\n const componentSource = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);\n if (componentSource) {\n const file = getSourceFile(componentSource);\n if (file && !componentCandidates.some((candidate) => candidate.source === componentSource)) {\n componentCandidates.push({ source: componentSource, file });\n }\n }\n\n current = current.return ?? null;\n }\n\n const direct = jsxCandidates[0]?.source ?? null;\n const nearestProjectLocalComponentFile = componentCandidates.find((candidate) => isProjectLocalSource(candidate.source))?.file;\n let screen: string | null = null;\n if (nearestProjectLocalComponentFile) {\n const matchingJsxCandidate = jsxCandidates.find((candidate) => candidate.file === nearestProjectLocalComponentFile);\n if (matchingJsxCandidate) {\n screen = matchingJsxCandidate.source;\n } else {\n const matchingComponentCandidate = componentCandidates.find(\n (candidate) => candidate.file === nearestProjectLocalComponentFile,\n );\n if (matchingComponentCandidate) {\n screen = matchingComponentCandidate.source;\n }\n }\n }\n\n const implementationComponentCandidate =\n componentCandidates.find((candidate) => !isProjectLocalSource(candidate.source))?.source ?? null;\n const implementationJsxCandidate =\n jsxCandidates.find((candidate) => !isProjectLocalSource(candidate.source))?.source ?? null;\n\n const projectLocalJsxCandidate = jsxCandidates.find((candidate) => isProjectLocalSource(candidate.source))?.source ?? null;\n const screenFallback = screen ?? projectLocalJsxCandidate ?? componentCandidates.find((candidate) => isProjectLocalSource(candidate.source))?.source ?? null;\n\n return {\n direct: direct ?? screenFallback,\n screen: screenFallback,\n implementation: implementationComponentCandidate ?? implementationJsxCandidate ?? screenFallback,\n };\n}\n\nfunction getModeDescription(mode: LocatorMode) {\n if (mode === \"direct\") {\n return \"Direct JSX\";\n }\n\n if (mode === \"screen\") {\n return \"Screen source\";\n }\n\n return \"Implementation source\";\n}\n\nfunction createStatusOverlay(triggerKey: TriggerKey): StatusOverlay | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n\n const element = document.createElement(\"div\");\n let copyValue: string | null = null;\n let currentMode: LocatorMode = \"screen\";\n let hideTimer: ReturnType<typeof setTimeout> | null = null;\n element.setAttribute(\"data-react-code-locator\", \"true\");\n Object.assign(element.style, {\n position: \"fixed\",\n right: \"12px\",\n bottom: \"12px\",\n zIndex: \"2147483647\",\n padding: \"8px 10px\",\n borderRadius: \"8px\",\n background: \"rgba(17, 24, 39, 0.92)\",\n color: \"#fff\",\n fontSize: \"12px\",\n lineHeight: \"1.4\",\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n boxShadow: \"0 8px 30px rgba(0, 0, 0, 0.25)\",\n pointerEvents: \"auto\",\n cursor: \"pointer\",\n maxWidth: \"min(70vw, 720px)\",\n wordBreak: \"break-all\",\n opacity: \"0\",\n transition: \"opacity 120ms ease\",\n });\n\n const show = (message: string, tone: \"idle\" | \"success\" | \"error\") => {\n element.textContent = message;\n element.style.background =\n tone === \"success\"\n ? \"rgba(6, 95, 70, 0.92)\"\n : tone === \"error\"\n ? \"rgba(153, 27, 27, 0.94)\"\n : \"rgba(17, 24, 39, 0.92)\";\n element.style.opacity = \"1\";\n element.style.pointerEvents = \"auto\";\n\n if (hideTimer) {\n clearTimeout(hideTimer);\n }\n\n hideTimer = setTimeout(() => {\n element.style.opacity = \"0\";\n element.style.pointerEvents = \"none\";\n }, 2000);\n };\n\n element.addEventListener(\"click\", async () => {\n if (!copyValue) {\n return;\n }\n\n try {\n await navigator.clipboard.writeText(copyValue);\n show(`[react-code-locator] copied`, \"success\");\n } catch {\n show(`[react-code-locator] copy failed`, \"error\");\n }\n });\n\n show(`[react-code-locator] enabled (${triggerKey}+click, alt+1/2/3 to switch mode)`, \"idle\");\n\n const mount = () => {\n if (!element.isConnected && document.body) {\n document.body.appendChild(element);\n }\n };\n\n if (document.body) {\n mount();\n } else {\n document.addEventListener(\"DOMContentLoaded\", mount, { once: true });\n }\n\n return {\n setStatus(message, tone = \"idle\") {\n show(message, tone);\n },\n setCopyValue(value) {\n copyValue = value;\n },\n setMode(mode) {\n currentMode = mode;\n show(`[react-code-locator] ${getModeDescription(mode)}`, \"idle\");\n },\n remove() {\n if (hideTimer) {\n clearTimeout(hideTimer);\n }\n element.remove();\n },\n };\n}\n\nexport function locateComponentSource(target: EventTarget | null, mode: LocatorMode = \"screen\"): LocatorResult | null {\n const elementTarget =\n target instanceof Element ? target : target instanceof Node ? target.parentElement : null;\n const fiber = getClosestReactFiber(elementTarget);\n if (!fiber) {\n return null;\n }\n\n const candidates = resolveSourceCandidates(fiber);\n const source = candidates[mode] ?? candidates.screen ?? candidates.direct ?? candidates.implementation;\n if (source) {\n return {\n source,\n mode,\n };\n }\n\n const componentSource = resolveComponentSourceFromFiber(fiber);\n if (!componentSource) {\n return null;\n }\n\n return {\n source: componentSource,\n mode,\n };\n}\n\nexport function enableReactComponentJump(options: LocatorOptions = {}) {\n const overlay = createStatusOverlay(options.triggerKey ?? \"shift\");\n let currentMode: LocatorMode = \"screen\";\n const {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n overlay?.setCopyValue(result.source);\n overlay?.setStatus(`[react-code-locator] ${result.source}`, \"success\");\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n const message = error instanceof Error ? error.message : String(error);\n overlay?.setCopyValue(null);\n overlay?.setStatus(`[react-code-locator] ${message}`, \"error\");\n },\n } = options;\n\n console.log(\"[react-code-locator] enabled\", { triggerKey });\n\n const keyHandler = (event: KeyboardEvent) => {\n if (!event.altKey) {\n return;\n }\n\n if (event.code === \"Digit1\") {\n currentMode = \"direct\";\n overlay?.setMode(currentMode);\n event.preventDefault();\n return;\n }\n\n if (event.code === \"Digit2\") {\n currentMode = \"screen\";\n overlay?.setMode(currentMode);\n event.preventDefault();\n return;\n }\n\n if (event.code === \"Digit3\") {\n currentMode = \"implementation\";\n overlay?.setMode(currentMode);\n event.preventDefault();\n }\n };\n\n const handler = (event: MouseEvent) => {\n console.log(\"[react-code-locator] click\", {\n triggerKey,\n shiftKey: event.shiftKey,\n altKey: event.altKey,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n target: event.target,\n });\n\n if (!isTriggerPressed(event, triggerKey)) {\n return;\n }\n\n const result = locateComponentSource(event.target, currentMode);\n if (!result) {\n onError(new Error(\"No React component source metadata found for clicked element.\"));\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n onLocate(result);\n };\n\n document.addEventListener(\"click\", handler, true);\n document.addEventListener(\"keydown\", keyHandler, true);\n\n return () => {\n document.removeEventListener(\"click\", handler, true);\n document.removeEventListener(\"keydown\", keyHandler, true);\n overlay?.remove();\n };\n}\n"],"mappings":"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","names":["source"]}
package/dist/client.d.cts CHANGED
@@ -1,14 +1 @@
1
- type TriggerKey = "alt" | "meta" | "ctrl" | "shift" | "none";
2
- type LocatorResult = {
3
- source: string;
4
- mode: "jsx" | "component";
5
- };
6
- type LocatorOptions = {
7
- triggerKey?: TriggerKey;
8
- onLocate?: (result: LocatorResult) => void;
9
- onError?: (error: unknown) => void;
10
- };
11
- declare function locateComponentSource(target: EventTarget | null): LocatorResult | null;
12
- declare function enableReactComponentJump(options?: LocatorOptions): () => void;
13
-
14
- export { type LocatorOptions, type LocatorResult, type TriggerKey, enableReactComponentJump, locateComponentSource };
1
+ export { a as LocatorOptions, b as LocatorResult, T as TriggerKey, e as enableReactComponentJump, l as locateComponentSource } from './client-sm5wi0uT.cjs';
package/dist/client.d.ts CHANGED
@@ -1,14 +1 @@
1
- type TriggerKey = "alt" | "meta" | "ctrl" | "shift" | "none";
2
- type LocatorResult = {
3
- source: string;
4
- mode: "jsx" | "component";
5
- };
6
- type LocatorOptions = {
7
- triggerKey?: TriggerKey;
8
- onLocate?: (result: LocatorResult) => void;
9
- onError?: (error: unknown) => void;
10
- };
11
- declare function locateComponentSource(target: EventTarget | null): LocatorResult | null;
12
- declare function enableReactComponentJump(options?: LocatorOptions): () => void;
13
-
14
- export { type LocatorOptions, type LocatorResult, type TriggerKey, enableReactComponentJump, locateComponentSource };
1
+ export { a as LocatorOptions, b as LocatorResult, T as TriggerKey, e as enableReactComponentJump, l as locateComponentSource } from './client-sm5wi0uT.js';