react-code-locator 0.1.7 → 0.1.9
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/client.cjs +114 -11
- package/dist/client.cjs.map +1 -1
- package/dist/client.js +114 -11
- package/dist/client.js.map +1 -1
- package/dist/index.cjs +114 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +114 -11
- package/dist/index.js.map +1 -1
- package/dist/vite.cjs +2 -2
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +2 -2
- package/dist/vite.js.map +1 -1
- package/dist/webpackRuntimeEntry.cjs +114 -11
- package/dist/webpackRuntimeEntry.cjs.map +1 -1
- package/dist/webpackRuntimeEntry.js +114 -11
- package/dist/webpackRuntimeEntry.js.map +1 -1
- package/package.json +1 -1
package/dist/client.cjs
CHANGED
|
@@ -77,10 +77,10 @@ function getSourceFromProps(props) {
|
|
|
77
77
|
const source = props?.[SOURCE_PROP];
|
|
78
78
|
return typeof source === "string" ? source : null;
|
|
79
79
|
}
|
|
80
|
-
function
|
|
80
|
+
function resolveComponentSourceFromFiber(fiber) {
|
|
81
81
|
let current = fiber;
|
|
82
82
|
while (current) {
|
|
83
|
-
const source =
|
|
83
|
+
const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);
|
|
84
84
|
if (source) {
|
|
85
85
|
return source;
|
|
86
86
|
}
|
|
@@ -88,35 +88,122 @@ function resolveJsxSourceFromFiber(fiber) {
|
|
|
88
88
|
}
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
|
-
function
|
|
92
|
-
|
|
91
|
+
function getDirectDebugSource(fiber) {
|
|
92
|
+
const debugSource = fiber?._debugSource;
|
|
93
|
+
if (debugSource?.fileName && typeof debugSource.lineNumber === "number") {
|
|
94
|
+
return `${debugSource.fileName.replace(/\\/g, "/")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function resolveOwnerJsxSource(fiber) {
|
|
99
|
+
let current = fiber?._debugOwner ?? null;
|
|
93
100
|
while (current) {
|
|
94
|
-
const source =
|
|
101
|
+
const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
|
|
95
102
|
if (source) {
|
|
96
103
|
return source;
|
|
97
104
|
}
|
|
98
|
-
current = current.
|
|
105
|
+
current = current._debugOwner ?? null;
|
|
99
106
|
}
|
|
100
107
|
return null;
|
|
101
108
|
}
|
|
102
|
-
function
|
|
109
|
+
function resolveNearestJsxSource(fiber) {
|
|
103
110
|
let current = fiber;
|
|
104
111
|
while (current) {
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
return
|
|
112
|
+
const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
|
|
113
|
+
if (source) {
|
|
114
|
+
return source;
|
|
108
115
|
}
|
|
109
116
|
current = current.return ?? null;
|
|
110
117
|
}
|
|
111
118
|
return null;
|
|
112
119
|
}
|
|
120
|
+
function createStatusOverlay(triggerKey) {
|
|
121
|
+
if (typeof document === "undefined") {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const element = document.createElement("div");
|
|
125
|
+
let currentText = "";
|
|
126
|
+
let copyValue = null;
|
|
127
|
+
let hideTimer = null;
|
|
128
|
+
element.setAttribute("data-react-code-locator", "true");
|
|
129
|
+
Object.assign(element.style, {
|
|
130
|
+
position: "fixed",
|
|
131
|
+
right: "12px",
|
|
132
|
+
bottom: "12px",
|
|
133
|
+
zIndex: "2147483647",
|
|
134
|
+
padding: "8px 10px",
|
|
135
|
+
borderRadius: "8px",
|
|
136
|
+
background: "rgba(17, 24, 39, 0.92)",
|
|
137
|
+
color: "#fff",
|
|
138
|
+
fontSize: "12px",
|
|
139
|
+
lineHeight: "1.4",
|
|
140
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
141
|
+
boxShadow: "0 8px 30px rgba(0, 0, 0, 0.25)",
|
|
142
|
+
pointerEvents: "auto",
|
|
143
|
+
cursor: "pointer",
|
|
144
|
+
maxWidth: "min(70vw, 720px)",
|
|
145
|
+
wordBreak: "break-all",
|
|
146
|
+
opacity: "0",
|
|
147
|
+
transition: "opacity 120ms ease"
|
|
148
|
+
});
|
|
149
|
+
const show = (message, tone) => {
|
|
150
|
+
currentText = message;
|
|
151
|
+
element.textContent = message;
|
|
152
|
+
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
|
+
element.style.opacity = "1";
|
|
154
|
+
element.style.pointerEvents = "auto";
|
|
155
|
+
if (hideTimer) {
|
|
156
|
+
clearTimeout(hideTimer);
|
|
157
|
+
}
|
|
158
|
+
hideTimer = setTimeout(() => {
|
|
159
|
+
element.style.opacity = "0";
|
|
160
|
+
element.style.pointerEvents = "none";
|
|
161
|
+
}, 1500);
|
|
162
|
+
};
|
|
163
|
+
element.addEventListener("click", async () => {
|
|
164
|
+
if (!copyValue) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
await navigator.clipboard.writeText(copyValue);
|
|
169
|
+
show(`[react-code-locator] copied`, "success");
|
|
170
|
+
} catch {
|
|
171
|
+
show(`[react-code-locator] copy failed`, "error");
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
show(`[react-code-locator] enabled (${triggerKey}+click)`, "idle");
|
|
175
|
+
const mount = () => {
|
|
176
|
+
if (!element.isConnected && document.body) {
|
|
177
|
+
document.body.appendChild(element);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
if (document.body) {
|
|
181
|
+
mount();
|
|
182
|
+
} else {
|
|
183
|
+
document.addEventListener("DOMContentLoaded", mount, { once: true });
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
setStatus(message, tone = "idle") {
|
|
187
|
+
show(message, tone);
|
|
188
|
+
},
|
|
189
|
+
setCopyValue(value) {
|
|
190
|
+
copyValue = value;
|
|
191
|
+
},
|
|
192
|
+
remove() {
|
|
193
|
+
if (hideTimer) {
|
|
194
|
+
clearTimeout(hideTimer);
|
|
195
|
+
}
|
|
196
|
+
element.remove();
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
113
200
|
function locateComponentSource(target) {
|
|
114
201
|
const elementTarget = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
115
202
|
const fiber = getClosestReactFiber(elementTarget);
|
|
116
203
|
if (!fiber) {
|
|
117
204
|
return null;
|
|
118
205
|
}
|
|
119
|
-
const jsxSource =
|
|
206
|
+
const jsxSource = resolveOwnerJsxSource(fiber) ?? resolveNearestJsxSource(fiber);
|
|
120
207
|
if (jsxSource) {
|
|
121
208
|
return {
|
|
122
209
|
source: jsxSource,
|
|
@@ -133,16 +220,31 @@ function locateComponentSource(target) {
|
|
|
133
220
|
};
|
|
134
221
|
}
|
|
135
222
|
function enableReactComponentJump(options = {}) {
|
|
223
|
+
const overlay = createStatusOverlay(options.triggerKey ?? "shift");
|
|
136
224
|
const {
|
|
137
225
|
triggerKey = "shift",
|
|
138
226
|
onLocate = (result) => {
|
|
139
227
|
console.log(`[react-code-locator] ${result.source}`);
|
|
228
|
+
overlay?.setCopyValue(result.source);
|
|
229
|
+
overlay?.setStatus(`[react-code-locator] ${result.source}`, "success");
|
|
140
230
|
},
|
|
141
231
|
onError = (error) => {
|
|
142
232
|
console.error("[react-code-locator]", error);
|
|
233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
234
|
+
overlay?.setCopyValue(null);
|
|
235
|
+
overlay?.setStatus(`[react-code-locator] ${message}`, "error");
|
|
143
236
|
}
|
|
144
237
|
} = options;
|
|
238
|
+
console.log("[react-code-locator] enabled", { triggerKey });
|
|
145
239
|
const handler = (event) => {
|
|
240
|
+
console.log("[react-code-locator] click", {
|
|
241
|
+
triggerKey,
|
|
242
|
+
shiftKey: event.shiftKey,
|
|
243
|
+
altKey: event.altKey,
|
|
244
|
+
ctrlKey: event.ctrlKey,
|
|
245
|
+
metaKey: event.metaKey,
|
|
246
|
+
target: event.target
|
|
247
|
+
});
|
|
146
248
|
if (!isTriggerPressed(event, triggerKey)) {
|
|
147
249
|
return;
|
|
148
250
|
}
|
|
@@ -158,6 +260,7 @@ function enableReactComponentJump(options = {}) {
|
|
|
158
260
|
document.addEventListener("click", handler, true);
|
|
159
261
|
return () => {
|
|
160
262
|
document.removeEventListener("click", handler, true);
|
|
263
|
+
overlay?.remove();
|
|
161
264
|
};
|
|
162
265
|
}
|
|
163
266
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/client.cjs.map
CHANGED
|
@@ -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 _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\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 getDebugSource(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const debugSource = current._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n current = current.return ?? null;\n }\n\n return null;\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 = resolveJsxSourceFromFiber(fiber) ?? getDebugSource(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 {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n },\n } = options;\n\n const handler = (event: MouseEvent) => {\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 };\n}\n"],"mappings":"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","names":["source"]}
|
|
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"]}
|
package/dist/client.js
CHANGED
|
@@ -50,10 +50,10 @@ function getSourceFromProps(props) {
|
|
|
50
50
|
const source = props?.[SOURCE_PROP];
|
|
51
51
|
return typeof source === "string" ? source : null;
|
|
52
52
|
}
|
|
53
|
-
function
|
|
53
|
+
function resolveComponentSourceFromFiber(fiber) {
|
|
54
54
|
let current = fiber;
|
|
55
55
|
while (current) {
|
|
56
|
-
const source =
|
|
56
|
+
const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);
|
|
57
57
|
if (source) {
|
|
58
58
|
return source;
|
|
59
59
|
}
|
|
@@ -61,35 +61,122 @@ function resolveJsxSourceFromFiber(fiber) {
|
|
|
61
61
|
}
|
|
62
62
|
return null;
|
|
63
63
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
64
|
+
function getDirectDebugSource(fiber) {
|
|
65
|
+
const debugSource = fiber?._debugSource;
|
|
66
|
+
if (debugSource?.fileName && typeof debugSource.lineNumber === "number") {
|
|
67
|
+
return `${debugSource.fileName.replace(/\\/g, "/")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function resolveOwnerJsxSource(fiber) {
|
|
72
|
+
let current = fiber?._debugOwner ?? null;
|
|
66
73
|
while (current) {
|
|
67
|
-
const source =
|
|
74
|
+
const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
|
|
68
75
|
if (source) {
|
|
69
76
|
return source;
|
|
70
77
|
}
|
|
71
|
-
current = current.
|
|
78
|
+
current = current._debugOwner ?? null;
|
|
72
79
|
}
|
|
73
80
|
return null;
|
|
74
81
|
}
|
|
75
|
-
function
|
|
82
|
+
function resolveNearestJsxSource(fiber) {
|
|
76
83
|
let current = fiber;
|
|
77
84
|
while (current) {
|
|
78
|
-
const
|
|
79
|
-
if (
|
|
80
|
-
return
|
|
85
|
+
const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
|
|
86
|
+
if (source) {
|
|
87
|
+
return source;
|
|
81
88
|
}
|
|
82
89
|
current = current.return ?? null;
|
|
83
90
|
}
|
|
84
91
|
return null;
|
|
85
92
|
}
|
|
93
|
+
function createStatusOverlay(triggerKey) {
|
|
94
|
+
if (typeof document === "undefined") {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const element = document.createElement("div");
|
|
98
|
+
let currentText = "";
|
|
99
|
+
let copyValue = null;
|
|
100
|
+
let hideTimer = null;
|
|
101
|
+
element.setAttribute("data-react-code-locator", "true");
|
|
102
|
+
Object.assign(element.style, {
|
|
103
|
+
position: "fixed",
|
|
104
|
+
right: "12px",
|
|
105
|
+
bottom: "12px",
|
|
106
|
+
zIndex: "2147483647",
|
|
107
|
+
padding: "8px 10px",
|
|
108
|
+
borderRadius: "8px",
|
|
109
|
+
background: "rgba(17, 24, 39, 0.92)",
|
|
110
|
+
color: "#fff",
|
|
111
|
+
fontSize: "12px",
|
|
112
|
+
lineHeight: "1.4",
|
|
113
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
114
|
+
boxShadow: "0 8px 30px rgba(0, 0, 0, 0.25)",
|
|
115
|
+
pointerEvents: "auto",
|
|
116
|
+
cursor: "pointer",
|
|
117
|
+
maxWidth: "min(70vw, 720px)",
|
|
118
|
+
wordBreak: "break-all",
|
|
119
|
+
opacity: "0",
|
|
120
|
+
transition: "opacity 120ms ease"
|
|
121
|
+
});
|
|
122
|
+
const show = (message, tone) => {
|
|
123
|
+
currentText = message;
|
|
124
|
+
element.textContent = message;
|
|
125
|
+
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)";
|
|
126
|
+
element.style.opacity = "1";
|
|
127
|
+
element.style.pointerEvents = "auto";
|
|
128
|
+
if (hideTimer) {
|
|
129
|
+
clearTimeout(hideTimer);
|
|
130
|
+
}
|
|
131
|
+
hideTimer = setTimeout(() => {
|
|
132
|
+
element.style.opacity = "0";
|
|
133
|
+
element.style.pointerEvents = "none";
|
|
134
|
+
}, 1500);
|
|
135
|
+
};
|
|
136
|
+
element.addEventListener("click", async () => {
|
|
137
|
+
if (!copyValue) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
await navigator.clipboard.writeText(copyValue);
|
|
142
|
+
show(`[react-code-locator] copied`, "success");
|
|
143
|
+
} catch {
|
|
144
|
+
show(`[react-code-locator] copy failed`, "error");
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
show(`[react-code-locator] enabled (${triggerKey}+click)`, "idle");
|
|
148
|
+
const mount = () => {
|
|
149
|
+
if (!element.isConnected && document.body) {
|
|
150
|
+
document.body.appendChild(element);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
if (document.body) {
|
|
154
|
+
mount();
|
|
155
|
+
} else {
|
|
156
|
+
document.addEventListener("DOMContentLoaded", mount, { once: true });
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
setStatus(message, tone = "idle") {
|
|
160
|
+
show(message, tone);
|
|
161
|
+
},
|
|
162
|
+
setCopyValue(value) {
|
|
163
|
+
copyValue = value;
|
|
164
|
+
},
|
|
165
|
+
remove() {
|
|
166
|
+
if (hideTimer) {
|
|
167
|
+
clearTimeout(hideTimer);
|
|
168
|
+
}
|
|
169
|
+
element.remove();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
86
173
|
function locateComponentSource(target) {
|
|
87
174
|
const elementTarget = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
88
175
|
const fiber = getClosestReactFiber(elementTarget);
|
|
89
176
|
if (!fiber) {
|
|
90
177
|
return null;
|
|
91
178
|
}
|
|
92
|
-
const jsxSource =
|
|
179
|
+
const jsxSource = resolveOwnerJsxSource(fiber) ?? resolveNearestJsxSource(fiber);
|
|
93
180
|
if (jsxSource) {
|
|
94
181
|
return {
|
|
95
182
|
source: jsxSource,
|
|
@@ -106,16 +193,31 @@ function locateComponentSource(target) {
|
|
|
106
193
|
};
|
|
107
194
|
}
|
|
108
195
|
function enableReactComponentJump(options = {}) {
|
|
196
|
+
const overlay = createStatusOverlay(options.triggerKey ?? "shift");
|
|
109
197
|
const {
|
|
110
198
|
triggerKey = "shift",
|
|
111
199
|
onLocate = (result) => {
|
|
112
200
|
console.log(`[react-code-locator] ${result.source}`);
|
|
201
|
+
overlay?.setCopyValue(result.source);
|
|
202
|
+
overlay?.setStatus(`[react-code-locator] ${result.source}`, "success");
|
|
113
203
|
},
|
|
114
204
|
onError = (error) => {
|
|
115
205
|
console.error("[react-code-locator]", error);
|
|
206
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
207
|
+
overlay?.setCopyValue(null);
|
|
208
|
+
overlay?.setStatus(`[react-code-locator] ${message}`, "error");
|
|
116
209
|
}
|
|
117
210
|
} = options;
|
|
211
|
+
console.log("[react-code-locator] enabled", { triggerKey });
|
|
118
212
|
const handler = (event) => {
|
|
213
|
+
console.log("[react-code-locator] click", {
|
|
214
|
+
triggerKey,
|
|
215
|
+
shiftKey: event.shiftKey,
|
|
216
|
+
altKey: event.altKey,
|
|
217
|
+
ctrlKey: event.ctrlKey,
|
|
218
|
+
metaKey: event.metaKey,
|
|
219
|
+
target: event.target
|
|
220
|
+
});
|
|
119
221
|
if (!isTriggerPressed(event, triggerKey)) {
|
|
120
222
|
return;
|
|
121
223
|
}
|
|
@@ -131,6 +233,7 @@ function enableReactComponentJump(options = {}) {
|
|
|
131
233
|
document.addEventListener("click", handler, true);
|
|
132
234
|
return () => {
|
|
133
235
|
document.removeEventListener("click", handler, true);
|
|
236
|
+
overlay?.remove();
|
|
134
237
|
};
|
|
135
238
|
}
|
|
136
239
|
export {
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/runtime.ts"],"sourcesContent":["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 _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\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 getDebugSource(fiber: ReactFiber | null) {\n let current = fiber;\n\n while (current) {\n const debugSource = current._debugSource;\n if (debugSource?.fileName && typeof debugSource.lineNumber === \"number\") {\n return `${debugSource.fileName.replace(/\\\\/g, \"/\")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;\n }\n\n current = current.return ?? null;\n }\n\n return null;\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 = resolveJsxSourceFromFiber(fiber) ?? getDebugSource(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 {\n triggerKey = \"shift\",\n onLocate = (result) => {\n console.log(`[react-code-locator] ${result.source}`);\n },\n onError = (error) => {\n console.error(\"[react-code-locator]\", error);\n },\n } = options;\n\n const handler = (event: MouseEvent) => {\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 };\n}\n"],"mappings":"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","names":["source"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/runtime.ts"],"sourcesContent":["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"]}
|
package/dist/index.cjs
CHANGED
|
@@ -77,10 +77,10 @@ function getSourceFromProps(props) {
|
|
|
77
77
|
const source = props?.[SOURCE_PROP];
|
|
78
78
|
return typeof source === "string" ? source : null;
|
|
79
79
|
}
|
|
80
|
-
function
|
|
80
|
+
function resolveComponentSourceFromFiber(fiber) {
|
|
81
81
|
let current = fiber;
|
|
82
82
|
while (current) {
|
|
83
|
-
const source =
|
|
83
|
+
const source = getSourceFromType(current.type) ?? getSourceFromType(current.elementType);
|
|
84
84
|
if (source) {
|
|
85
85
|
return source;
|
|
86
86
|
}
|
|
@@ -88,35 +88,122 @@ function resolveJsxSourceFromFiber(fiber) {
|
|
|
88
88
|
}
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
|
-
function
|
|
92
|
-
|
|
91
|
+
function getDirectDebugSource(fiber) {
|
|
92
|
+
const debugSource = fiber?._debugSource;
|
|
93
|
+
if (debugSource?.fileName && typeof debugSource.lineNumber === "number") {
|
|
94
|
+
return `${debugSource.fileName.replace(/\\/g, "/")}:${debugSource.lineNumber}:${debugSource.columnNumber ?? 1}`;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function resolveOwnerJsxSource(fiber) {
|
|
99
|
+
let current = fiber?._debugOwner ?? null;
|
|
93
100
|
while (current) {
|
|
94
|
-
const source =
|
|
101
|
+
const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
|
|
95
102
|
if (source) {
|
|
96
103
|
return source;
|
|
97
104
|
}
|
|
98
|
-
current = current.
|
|
105
|
+
current = current._debugOwner ?? null;
|
|
99
106
|
}
|
|
100
107
|
return null;
|
|
101
108
|
}
|
|
102
|
-
function
|
|
109
|
+
function resolveNearestJsxSource(fiber) {
|
|
103
110
|
let current = fiber;
|
|
104
111
|
while (current) {
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
return
|
|
112
|
+
const source = getSourceFromProps(current.pendingProps) ?? getSourceFromProps(current.memoizedProps) ?? getDirectDebugSource(current);
|
|
113
|
+
if (source) {
|
|
114
|
+
return source;
|
|
108
115
|
}
|
|
109
116
|
current = current.return ?? null;
|
|
110
117
|
}
|
|
111
118
|
return null;
|
|
112
119
|
}
|
|
120
|
+
function createStatusOverlay(triggerKey) {
|
|
121
|
+
if (typeof document === "undefined") {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
const element = document.createElement("div");
|
|
125
|
+
let currentText = "";
|
|
126
|
+
let copyValue = null;
|
|
127
|
+
let hideTimer = null;
|
|
128
|
+
element.setAttribute("data-react-code-locator", "true");
|
|
129
|
+
Object.assign(element.style, {
|
|
130
|
+
position: "fixed",
|
|
131
|
+
right: "12px",
|
|
132
|
+
bottom: "12px",
|
|
133
|
+
zIndex: "2147483647",
|
|
134
|
+
padding: "8px 10px",
|
|
135
|
+
borderRadius: "8px",
|
|
136
|
+
background: "rgba(17, 24, 39, 0.92)",
|
|
137
|
+
color: "#fff",
|
|
138
|
+
fontSize: "12px",
|
|
139
|
+
lineHeight: "1.4",
|
|
140
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
141
|
+
boxShadow: "0 8px 30px rgba(0, 0, 0, 0.25)",
|
|
142
|
+
pointerEvents: "auto",
|
|
143
|
+
cursor: "pointer",
|
|
144
|
+
maxWidth: "min(70vw, 720px)",
|
|
145
|
+
wordBreak: "break-all",
|
|
146
|
+
opacity: "0",
|
|
147
|
+
transition: "opacity 120ms ease"
|
|
148
|
+
});
|
|
149
|
+
const show = (message, tone) => {
|
|
150
|
+
currentText = message;
|
|
151
|
+
element.textContent = message;
|
|
152
|
+
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
|
+
element.style.opacity = "1";
|
|
154
|
+
element.style.pointerEvents = "auto";
|
|
155
|
+
if (hideTimer) {
|
|
156
|
+
clearTimeout(hideTimer);
|
|
157
|
+
}
|
|
158
|
+
hideTimer = setTimeout(() => {
|
|
159
|
+
element.style.opacity = "0";
|
|
160
|
+
element.style.pointerEvents = "none";
|
|
161
|
+
}, 1500);
|
|
162
|
+
};
|
|
163
|
+
element.addEventListener("click", async () => {
|
|
164
|
+
if (!copyValue) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
await navigator.clipboard.writeText(copyValue);
|
|
169
|
+
show(`[react-code-locator] copied`, "success");
|
|
170
|
+
} catch {
|
|
171
|
+
show(`[react-code-locator] copy failed`, "error");
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
show(`[react-code-locator] enabled (${triggerKey}+click)`, "idle");
|
|
175
|
+
const mount = () => {
|
|
176
|
+
if (!element.isConnected && document.body) {
|
|
177
|
+
document.body.appendChild(element);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
if (document.body) {
|
|
181
|
+
mount();
|
|
182
|
+
} else {
|
|
183
|
+
document.addEventListener("DOMContentLoaded", mount, { once: true });
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
setStatus(message, tone = "idle") {
|
|
187
|
+
show(message, tone);
|
|
188
|
+
},
|
|
189
|
+
setCopyValue(value) {
|
|
190
|
+
copyValue = value;
|
|
191
|
+
},
|
|
192
|
+
remove() {
|
|
193
|
+
if (hideTimer) {
|
|
194
|
+
clearTimeout(hideTimer);
|
|
195
|
+
}
|
|
196
|
+
element.remove();
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
}
|
|
113
200
|
function locateComponentSource(target) {
|
|
114
201
|
const elementTarget = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
115
202
|
const fiber = getClosestReactFiber(elementTarget);
|
|
116
203
|
if (!fiber) {
|
|
117
204
|
return null;
|
|
118
205
|
}
|
|
119
|
-
const jsxSource =
|
|
206
|
+
const jsxSource = resolveOwnerJsxSource(fiber) ?? resolveNearestJsxSource(fiber);
|
|
120
207
|
if (jsxSource) {
|
|
121
208
|
return {
|
|
122
209
|
source: jsxSource,
|
|
@@ -133,16 +220,31 @@ function locateComponentSource(target) {
|
|
|
133
220
|
};
|
|
134
221
|
}
|
|
135
222
|
function enableReactComponentJump(options = {}) {
|
|
223
|
+
const overlay = createStatusOverlay(options.triggerKey ?? "shift");
|
|
136
224
|
const {
|
|
137
225
|
triggerKey = "shift",
|
|
138
226
|
onLocate = (result) => {
|
|
139
227
|
console.log(`[react-code-locator] ${result.source}`);
|
|
228
|
+
overlay?.setCopyValue(result.source);
|
|
229
|
+
overlay?.setStatus(`[react-code-locator] ${result.source}`, "success");
|
|
140
230
|
},
|
|
141
231
|
onError = (error) => {
|
|
142
232
|
console.error("[react-code-locator]", error);
|
|
233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
234
|
+
overlay?.setCopyValue(null);
|
|
235
|
+
overlay?.setStatus(`[react-code-locator] ${message}`, "error");
|
|
143
236
|
}
|
|
144
237
|
} = options;
|
|
238
|
+
console.log("[react-code-locator] enabled", { triggerKey });
|
|
145
239
|
const handler = (event) => {
|
|
240
|
+
console.log("[react-code-locator] click", {
|
|
241
|
+
triggerKey,
|
|
242
|
+
shiftKey: event.shiftKey,
|
|
243
|
+
altKey: event.altKey,
|
|
244
|
+
ctrlKey: event.ctrlKey,
|
|
245
|
+
metaKey: event.metaKey,
|
|
246
|
+
target: event.target
|
|
247
|
+
});
|
|
146
248
|
if (!isTriggerPressed(event, triggerKey)) {
|
|
147
249
|
return;
|
|
148
250
|
}
|
|
@@ -158,6 +260,7 @@ function enableReactComponentJump(options = {}) {
|
|
|
158
260
|
document.addEventListener("click", handler, true);
|
|
159
261
|
return () => {
|
|
160
262
|
document.removeEventListener("click", handler, true);
|
|
263
|
+
overlay?.remove();
|
|
161
264
|
};
|
|
162
265
|
}
|
|
163
266
|
// Annotate the CommonJS export names for ESM import in node:
|